Files
chat/docker-compose.prod.yml
T
2026-06-15 21:21:20 +08:00

116 lines
4.4 KiB
YAML

# ============================================================
# 青叶 (QingYe) —— 生产环境 Docker Compose(自包含)
# ------------------------------------------------------------
# 启动命令:
# docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build
#
# 说明:
# * 此文件为「生产专用」,与开发用的 docker-compose.yml 完全独立。
# 生产服务器上请始终带 -f docker-compose.prod.yml,切勿运行裸 docker compose up。
# * 所有密钥通过 --env-file .env.prod 注入(密钥文件已在 .gitignore 中)。
# * 后端必须「单进程」运行:WebSocket 连接管理器为进程内存单例
# (见 backend/app/websocket/manager.py),多 worker / gunicorn 会导致
# 跨用户、跨标签页的实时消息丢失。镜像 Dockerfile.prod 已固定为单进程。
# * postgres / redis 不向主机暴露端口,仅容器内网互通。
# * backend / frontend 仅绑定 127.0.0.1,由宿主机 Nginx 反向代理对外。
# ============================================================
services:
# ==================== PostgreSQL ====================
postgres:
image: postgres:16-alpine
container_name: qingye-postgres
restart: always
environment:
POSTGRES_DB: ${POSTGRES_DB:-qingye}
POSTGRES_USER: ${POSTGRES_USER:-qingye}
# 密钥必填:缺失时 compose 会直接报错,绝不回退到弱口令
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env.prod 中设置 POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-qingye} -d ${POSTGRES_DB:-qingye}"]
interval: 5s
timeout: 5s
retries: 5
networks:
- qingye-network
# 生产环境不向宿主机映射 5432 端口(安全)
# ==================== Redis ====================
# Redis 非可选:flash_service 原子计数 / draft_service 草稿等依赖它
redis:
image: redis:7-alpine
container_name: qingye-redis
restart: always
command: redis-server --appendonly yes
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
- qingye-network
# 生产环境不向宿主机映射 6379 端口(安全)
# ==================== Backend (FastAPI) ====================
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
container_name: qingye-backend
restart: always
ports:
- "127.0.0.1:8000:8000" # 仅本机可达,由宿主机 Nginx 反向代理
environment:
# DATABASE_URL 由上面的 POSTGRES_* 变量自动拼接,保持口令一致
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-qingye}:${POSTGRES_PASSWORD:?请在 .env.prod 中设置 POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-qingye}
REDIS_URL: redis://redis:6379/0
JWT_SECRET_KEY: ${JWT_SECRET_KEY:?请在 .env.prod 中设置 JWT_SECRET_KEY}
JWT_REFRESH_SECRET_KEY: ${JWT_REFRESH_SECRET_KEY:?请在 .env.prod 中设置 JWT_REFRESH_SECRET_KEY}
CORS_ORIGINS: ${CORS_ORIGINS:-https://www.e4s.world}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?请在 .env.prod 中设置 ADMIN_PASSWORD}
APP_ENV: production
volumes:
- upload_data:/app/uploads # 仅持久化用户上传文件,不挂载源码(运行镜像内已打包的代码)
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- qingye-network
security_opt:
- no-new-privileges:true
# ==================== Frontend (Vue 3 构建产物 + Nginx) ====================
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
# 生产构建为「域名无关」:不传入任何 VITE_* 变量,
# 前端代码会自动使用页面同源地址(/api/v1 与 wss://当前域名)。
container_name: qingye-frontend
restart: always
ports:
- "127.0.0.1:8080:80" # 仅本机可达,由宿主机 Nginx 反向代理
depends_on:
- backend
networks:
- qingye-network
security_opt:
- no-new-privileges:true
# ==================== 数据卷 ====================
volumes:
pgdata:
redisdata:
upload_data:
# ==================== 网络 ====================
networks:
qingye-network:
driver: bridge