116 lines
4.4 KiB
YAML
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
|