2026-08-06 00:17:49 +08:00
|
|
|
|
# ============================================
|
2026-08-07 09:06:19 +08:00
|
|
|
|
# DPB Docker Compose 配置文件
|
2026-08-06 00:17:49 +08:00
|
|
|
|
# ============================================
|
|
|
|
|
|
# 使用方式:
|
|
|
|
|
|
# 开发环境: docker compose --env-file .env up -d
|
|
|
|
|
|
# 生产环境: docker compose --env-file .env -f docker-compose.yaml up -d
|
|
|
|
|
|
# ============================================
|
|
|
|
|
|
|
|
|
|
|
|
services:
|
|
|
|
|
|
# ==================== 数据库服务(PostgreSQL 16) ====================
|
|
|
|
|
|
postgres:
|
|
|
|
|
|
container_name: postgres
|
|
|
|
|
|
image: postgres:16
|
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
platform: linux/amd64
|
|
|
|
|
|
environment:
|
|
|
|
|
|
TZ: "Asia/Shanghai"
|
|
|
|
|
|
POSTGRES_DB: "${DATABASE_NAME:-dpb}"
|
|
|
|
|
|
POSTGRES_USER: "${DATABASE_USER:-dpb}"
|
|
|
|
|
|
POSTGRES_PASSWORD: "${DATABASE_PASSWORD:?错误: 请设置 DATABASE_PASSWORD 环境变量}"
|
|
|
|
|
|
# 仅绑定本机回环:数据库不应暴露到公网/局域网,只允许后端容器与运维本机访问
|
|
|
|
|
|
ports:
|
|
|
|
|
|
- "127.0.0.1:${DATABASE_PORT:-5432}:5432"
|
|
|
|
|
|
volumes:
|
|
|
|
|
|
- pg_data:/var/lib/postgresql/data
|
|
|
|
|
|
- /etc/localtime:/etc/localtime:ro
|
|
|
|
|
|
networks:
|
|
|
|
|
|
- app_network
|
|
|
|
|
|
healthcheck:
|
|
|
|
|
|
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USER:-dpb}"]
|
|
|
|
|
|
interval: 10s
|
|
|
|
|
|
timeout: 10s
|
|
|
|
|
|
retries: 10
|
|
|
|
|
|
start_period: 30s
|
|
|
|
|
|
logging:
|
|
|
|
|
|
driver: "json-file"
|
|
|
|
|
|
options:
|
|
|
|
|
|
max-size: "10m"
|
|
|
|
|
|
max-file: "3"
|
|
|
|
|
|
deploy:
|
|
|
|
|
|
resources:
|
|
|
|
|
|
limits:
|
|
|
|
|
|
memory: 1g
|
|
|
|
|
|
reservations:
|
|
|
|
|
|
memory: 256m
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== Redis 服务 ====================
|
|
|
|
|
|
redis:
|
|
|
|
|
|
container_name: redis
|
|
|
|
|
|
image: redis:7-alpine
|
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
platform: linux/amd64
|
|
|
|
|
|
environment:
|
|
|
|
|
|
TZ: "Asia/Shanghai"
|
|
|
|
|
|
# 仅绑定本机回环:Redis 不应暴露到公网/局域网
|
|
|
|
|
|
ports:
|
|
|
|
|
|
- "127.0.0.1:${REDIS_PORT:-6379}:6379"
|
|
|
|
|
|
volumes:
|
|
|
|
|
|
- redis_data:/data
|
|
|
|
|
|
# 挂载自定义 redis.conf(若存在则覆盖默认配置)
|
|
|
|
|
|
- ./redis/conf/redis.conf:/usr/local/etc/redis/redis.conf:ro
|
|
|
|
|
|
command: >
|
|
|
|
|
|
redis-server /usr/local/etc/redis/redis.conf
|
|
|
|
|
|
--requirepass ${REDIS_PASSWORD:?错误: 请设置 REDIS_PASSWORD 环境变量}
|
|
|
|
|
|
networks:
|
|
|
|
|
|
- app_network
|
|
|
|
|
|
healthcheck:
|
|
|
|
|
|
test:
|
|
|
|
|
|
["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
|
|
|
|
|
interval: 10s
|
|
|
|
|
|
timeout: 5s
|
|
|
|
|
|
retries: 5
|
|
|
|
|
|
start_period: 10s
|
|
|
|
|
|
logging:
|
|
|
|
|
|
driver: "json-file"
|
|
|
|
|
|
options:
|
|
|
|
|
|
max-size: "10m"
|
|
|
|
|
|
max-file: "3"
|
|
|
|
|
|
deploy:
|
|
|
|
|
|
resources:
|
|
|
|
|
|
limits:
|
|
|
|
|
|
memory: 512m
|
|
|
|
|
|
reservations:
|
|
|
|
|
|
memory: 128m
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== 后端服务 ====================
|
|
|
|
|
|
backend:
|
|
|
|
|
|
container_name: backend
|
|
|
|
|
|
build:
|
|
|
|
|
|
context: ../
|
|
|
|
|
|
dockerfile: ./docker/backend/Dockerfile
|
|
|
|
|
|
args:
|
|
|
|
|
|
DEPLOY_ENV: "${DEPLOY_ENV:-prod}"
|
|
|
|
|
|
image: "backend:${BACKEND_IMAGE_TAG:-3.0.0}"
|
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
platform: linux/amd64
|
|
|
|
|
|
# 以非 root 运行(与 Dockerfile USER app 一致),降权容器内进程
|
|
|
|
|
|
user: "1001:1001"
|
|
|
|
|
|
# 纵深防御:禁止提权、丢弃全部 capabilities(uvicorn 绑 8001 端口无需特权)
|
|
|
|
|
|
security_opt:
|
|
|
|
|
|
- no-new-privileges:true
|
|
|
|
|
|
cap_drop:
|
|
|
|
|
|
- ALL
|
|
|
|
|
|
tmpfs:
|
|
|
|
|
|
- /tmp
|
|
|
|
|
|
environment:
|
|
|
|
|
|
TZ: "Asia/Shanghai"
|
|
|
|
|
|
# 运行环境,对应 backend/env/.env.{dev,prod}
|
|
|
|
|
|
ENVIRONMENT: "${DEPLOY_ENV:-prod}"
|
|
|
|
|
|
# PostgreSQL 连接配置(对应 backend/env/.env.prod 的 DATABASE_*)
|
|
|
|
|
|
DATABASE_TYPE: "postgres"
|
|
|
|
|
|
DATABASE_HOST: "postgres"
|
|
|
|
|
|
DATABASE_PORT: "5432"
|
|
|
|
|
|
DATABASE_USER: "${DATABASE_USER:-dpb}"
|
|
|
|
|
|
DATABASE_PASSWORD: "${DATABASE_PASSWORD:?错误: 请设置 DATABASE_PASSWORD 环境变量}"
|
|
|
|
|
|
DATABASE_NAME: "${DATABASE_NAME:-dpb}"
|
|
|
|
|
|
# Redis 连接配置
|
|
|
|
|
|
REDIS_HOST: "redis"
|
|
|
|
|
|
REDIS_PORT: "6379"
|
|
|
|
|
|
REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
|
|
|
|
|
REDIS_DB_NAME: "1"
|
|
|
|
|
|
REDIS_ENABLE: "true"
|
|
|
|
|
|
# JWT 签名密钥:必须通过 docker/.env 配置独立随机值(settings 会在 prod 下拒绝默认密钥)
|
|
|
|
|
|
SECRET_KEY: "${SECRET_KEY:?错误: 请设置 SECRET_KEY 环境变量(生产 JWT 密钥,勿用默认值)}"
|
|
|
|
|
|
# ── 应用层配置:镜像已排除 backend/env,以下全部由 docker/.env 注入(勿再依赖 .env.prod)──
|
|
|
|
|
|
# CORS 白名单:生产必须配置具体域名,为空时后端 prod 校验将拒绝启动
|
|
|
|
|
|
PROD_CORS_ORIGINS: "${PROD_CORS_ORIGINS:-}"
|
|
|
|
|
|
# Host 头白名单(nginx 反代时后端据此校验 Host,必须配置真实域名,否则请求 403)
|
|
|
|
|
|
ALLOWED_HOSTS: "${ALLOWED_HOSTS:?错误: 请设置 ALLOWED_HOSTS(如 [\"admin.example.com\",\"*.example.com\"])}"
|
|
|
|
|
|
# 是否信任 nginx 透传的客户端 IP 头(经可信代理时必须 true)
|
|
|
|
|
|
IP_TRUST_PROXY_HEADERS: "${IP_TRUST_PROXY_HEADERS:-true}"
|
|
|
|
|
|
# 登录暴力破解防护
|
|
|
|
|
|
LOGIN_MAX_FAILURES: "${LOGIN_MAX_FAILURES:-5}"
|
|
|
|
|
|
LOGIN_FAILURE_WINDOW_SECONDS: "${LOGIN_FAILURE_WINDOW_SECONDS:-900}"
|
|
|
|
|
|
LOGIN_LOCKOUT_SECONDS: "${LOGIN_LOCKOUT_SECONDS:-900}"
|
|
|
|
|
|
# 登录验证码 / 演示模式 / 开发默认密码(生产必须保持为空)
|
|
|
|
|
|
CAPTCHA_ENABLE: "${CAPTCHA_ENABLE:-true}"
|
|
|
|
|
|
DEMO_ENABLE: "${DEMO_ENABLE:-false}"
|
|
|
|
|
|
DEV_DEFAULT_PASSWORD: "${DEV_DEFAULT_PASSWORD:-}"
|
|
|
|
|
|
# SMTP 邮件
|
|
|
|
|
|
SMTP_HOST: "${SMTP_HOST:-}"
|
|
|
|
|
|
SMTP_PORT: "${SMTP_PORT:-465}"
|
|
|
|
|
|
SMTP_USER: "${SMTP_USER:-}"
|
|
|
|
|
|
SMTP_PASSWORD: "${SMTP_PASSWORD:-}"
|
|
|
|
|
|
SMTP_FROM: "${SMTP_FROM:-}"
|
|
|
|
|
|
SMTP_FROM_NAME: "${SMTP_FROM_NAME:-客户服务}"
|
|
|
|
|
|
SMTP_TLS: "${SMTP_TLS:-true}"
|
|
|
|
|
|
EMAIL_CODE_EXPIRE_SECONDS: "${EMAIL_CODE_EXPIRE_SECONDS:-300}"
|
|
|
|
|
|
# 大模型
|
|
|
|
|
|
OPENAI_BASE_URL: "${OPENAI_BASE_URL:-https://api.deepseek.com}"
|
|
|
|
|
|
OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
|
|
|
|
|
|
OPENAI_MODEL: "${OPENAI_MODEL:-deepseek-chat}"
|
|
|
|
|
|
# 日志级别
|
|
|
|
|
|
LOGGER_LEVEL: "${LOGGER_LEVEL:-INFO}"
|
|
|
|
|
|
# 非 root 容器下禁止写 __pycache__(/home 已 chown 可写,此处避免 import 时缓存字节码)
|
|
|
|
|
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
|
|
|
|
# 仅绑定本机回环:后端只经 nginx 对外,避免 8001 直连暴露(否则可绕过 Host 头/CORS/HTTPS 约束)
|
|
|
|
|
|
ports:
|
|
|
|
|
|
- "127.0.0.1:${BACKEND_PORT:-8001}:8001"
|
|
|
|
|
|
# volumes:
|
|
|
|
|
|
# ── 注意 ──────────────────────────────────
|
|
|
|
|
|
# 生产环境禁用代码挂载:镜像已自包含(见 Dockerfile COPY ./backend/)。
|
|
|
|
|
|
# 仅本地开发需要热更新时取消注释下面这行(宿主机代码覆盖容器内代码)。
|
|
|
|
|
|
# ─────────────────────────────────────────
|
|
|
|
|
|
# - ../backend:/home
|
|
|
|
|
|
depends_on:
|
|
|
|
|
|
postgres:
|
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
|
redis:
|
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
|
networks:
|
|
|
|
|
|
- app_network
|
|
|
|
|
|
healthcheck:
|
|
|
|
|
|
test:
|
|
|
|
|
|
[
|
|
|
|
|
|
"CMD-SHELL",
|
|
|
|
|
|
"python -c 'import urllib.request; urllib.request.urlopen(\"http://localhost:8001/common/health/check\")' || exit 1",
|
|
|
|
|
|
]
|
|
|
|
|
|
interval: 15s
|
|
|
|
|
|
timeout: 10s
|
|
|
|
|
|
retries: 5
|
|
|
|
|
|
start_period: 40s
|
|
|
|
|
|
logging:
|
|
|
|
|
|
driver: "json-file"
|
|
|
|
|
|
options:
|
|
|
|
|
|
max-size: "10m"
|
|
|
|
|
|
max-file: "3"
|
|
|
|
|
|
deploy:
|
|
|
|
|
|
resources:
|
|
|
|
|
|
limits:
|
|
|
|
|
|
memory: 1g
|
|
|
|
|
|
cpus: "1.0"
|
|
|
|
|
|
reservations:
|
|
|
|
|
|
memory: 256m
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== Nginx 服务 ====================
|
|
|
|
|
|
nginx:
|
|
|
|
|
|
container_name: nginx
|
|
|
|
|
|
image: nginx:1.25-alpine
|
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
platform: linux/amd64
|
|
|
|
|
|
environment:
|
|
|
|
|
|
TZ: "Asia/Shanghai"
|
|
|
|
|
|
ports:
|
|
|
|
|
|
- "${HTTP_PORT:-80}:80"
|
|
|
|
|
|
- "${HTTPS_PORT:-443}:443"
|
|
|
|
|
|
volumes:
|
|
|
|
|
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
|
|
|
|
|
- ./nginx/web:/usr/share/nginx/html/web:ro
|
|
|
|
|
|
- ./nginx/ssl:/etc/nginx/ssl:ro
|
|
|
|
|
|
# 按需启用(取消注释):
|
|
|
|
|
|
- ./nginx/app:/usr/share/nginx/html/app:ro
|
|
|
|
|
|
- ./nginx/docs:/usr/share/nginx/html/docs:ro
|
|
|
|
|
|
depends_on:
|
|
|
|
|
|
backend:
|
|
|
|
|
|
condition: service_started
|
|
|
|
|
|
networks:
|
|
|
|
|
|
- app_network
|
|
|
|
|
|
healthcheck:
|
|
|
|
|
|
test: ["CMD", "nginx", "-t"]
|
|
|
|
|
|
interval: 30s
|
|
|
|
|
|
timeout: 5s
|
|
|
|
|
|
retries: 3
|
|
|
|
|
|
logging:
|
|
|
|
|
|
driver: "json-file"
|
|
|
|
|
|
options:
|
|
|
|
|
|
max-size: "10m"
|
|
|
|
|
|
max-file: "3"
|
|
|
|
|
|
deploy:
|
|
|
|
|
|
resources:
|
|
|
|
|
|
limits:
|
|
|
|
|
|
memory: 256m
|
|
|
|
|
|
cpus: "0.5"
|
|
|
|
|
|
reservations:
|
|
|
|
|
|
memory: 64m
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== 持久化卷 ====================
|
|
|
|
|
|
volumes:
|
|
|
|
|
|
pg_data:
|
|
|
|
|
|
driver: local
|
|
|
|
|
|
driver_opts:
|
|
|
|
|
|
type: none
|
|
|
|
|
|
device: ./postgres/data
|
|
|
|
|
|
o: bind
|
|
|
|
|
|
redis_data:
|
|
|
|
|
|
driver: local
|
|
|
|
|
|
driver_opts:
|
|
|
|
|
|
type: none
|
|
|
|
|
|
device: ./redis/data
|
|
|
|
|
|
o: bind
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== 网络 ====================
|
|
|
|
|
|
networks:
|
|
|
|
|
|
app_network:
|
|
|
|
|
|
driver: bridge
|