Files
dpb/docker/docker-compose.yaml
34047007@qq.com 86126ea16f checkpoint: 架构治理四件套——统一 async / 测试真 PG / Redis 降级可选 / DPB 扶正
- A1 业务层统一 async:gencode 表内省改 run_sync 异步(不再阻塞事件循环)、
  check_db 改 async_engine、dict_util 启动预热异步载入;psycopg 降为
  APScheduler SQLAlchemyJobStore 专用同步孤岛(注释标注)
- A2 测试真 PG 化:conftest 弃 SQLite + mock Redis,改连真实 PG16(dpb_test)+
  Redis(number_gen advisory lock 路径真被测);新增 docker/test-compose.yaml
  测试依赖栈(postgres:16:5433 + redis:7:6380);run_ci 前置探测 + 容器兜底,
  pytest -m pg 10 套正式 tc 全过(真实 PG16 + Redis)
- B1 Redis 启动不强依赖:redis_connect 失败降级启动,缓存类回源 DB、存储类
  (会话/AI 配置/调度)经 require_redis 守卫返回 503;真机 3 阶段降级测试
  PASS=11 FAIL=0(独立 Redis 6390 + 后端 8091,不动共享 dev 服务)
- C 扶正 DPB:后端横幅/日志(dpb.log)/README/pyproject/ai_factory agent 名、
  前端 package.json/署名注释/链接文案/deploy.sh/docker 注释全部去 fastapiadmin;
  grep backend/app + frontend/web/src 零残留
- 验证:全量回归 pass=72 fail=0
2026-08-07 09:06:19 +08:00

256 lines
8.5 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ============================================
# DPB Docker Compose 配置文件
# ============================================
# 使用方式:
# 开发环境: 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"
# 纵深防御:禁止提权、丢弃全部 capabilitiesuvicorn 绑 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