# =========================================== # 小虾剪辑 SaaS Docker Compose 配置 # =========================================== # # 用法: # Staging: docker compose --env-file ../../.env -f compose.yml up -d # Production: 设置相关环境变量后执行 # # 环境变量说明: # API_IMAGE - API 镜像名称 (默认: xiaoxia-saas-api:dev) # WORKER_IMAGE - Worker 镜像名称 (默认: xiaoxia-saas-worker:dev) # WEB_IMAGE - Web 镜像名称 (默认: xiaoxia-saas-web:dev) # API_PORT - API 端口映射 (staging: 8000, production: 8001) # WEB_PORT - Web 端口映射 (staging: 3001, production: 3002) # GENERATED_FILES_HOST_DIR - 生成文件的主机目录 # GENERATION_CONCURRENCY - Generation worker 并发(用户实时任务,默认 2) # TRANSCODE_CONCURRENCY - Transcode worker 并发(后台/转码/AI,默认 2) # WORKER_MAX_TASKS_PER_CHILD - Worker 每个子进程最大任务数 (默认: 100) # BEAT_ENABLED - 容器内启动 celery beat(默认 1;独立 beat 容器部署设为 0) # WORKER_CONCURRENCY - 兼容旧变量:未显式设置上面两个并发时按此总数分配 # # 重要: # - 生产环境不要挂载 web-dist volume,否则会导致 403 # - 确保环境隔离网络已创建: docker network create xiaoxia-net-${ENV} # - ENV=staging -> xiaoxia-net-staging # - ENV=production -> xiaoxia-net-production # # #2073 队列分流:worker 容器内跑三个独立进程——beat(只发定时任务)、 # generation worker(只消费 generation 队列,实时高优)、transcode worker(消费 # transcode + celery 队列,后台任务)。beat 不再嵌入 generation worker, # 不占实时任务槽位;TRANSCODE_CONCURRENCY 独立伸缩,不再依赖 WORKER_CONCURRENCY 差值。 # =========================================== # 日志轮转配置(所有服务共享) # =========================================== x-logging: &default-logging driver: json-file options: max-size: "50m" max-file: "3" services: # ========================================= # API 服务 # ========================================= api: image: ${API_IMAGE:-xiaoxia-saas-api:dev} container_name: xiaoxia-api-${ENV:-staging} restart: unless-stopped stop_grace_period: 30s stop_signal: SIGTERM env_file: - ../../.env environment: APP_ENV: ${APP_ENV:-staging} GENERATED_FILES_DIR: /app/generated GENERATED_FILES_URL_PREFIX: /generated-files PUBLIC_API_BASE_URL: ${PUBLIC_API_BASE_URL:-https://api.xiaoxiajianji.com} ports: - "127.0.0.1:${API_PORT:-8000}:8000" volumes: - generated-files:/app/generated - ../../deploy/configs:/app/configs:ro networks: - xiaoxia-net healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=5)"] interval: 30s timeout: 10s retries: 3 start_period: 40s logging: *default-logging deploy: resources: limits: cpus: '2.0' memory: 2g reservations: cpus: '0.5' memory: 512M # ========================================= # Worker 服务(#2073 队列分流:beat + generation + transcode 同容器三进程) # ========================================= # 三个进程独立启动,任一退出则容器整体退出由 docker restart 拉起; # 各自的并发与资源占用通过环境变量控制: # - generation:GENERATION_CONCURRENCY(默认 2),消费 generation 队列 # - transcode: TRANSCODE_CONCURRENCY(默认 2),消费 transcode,celery 队列 # - beat: 不消费任务,只发定时任务到 celery 默认队列 worker: image: ${WORKER_IMAGE:-xiaoxia-saas-worker:dev} container_name: xiaoxia-worker-${ENV:-staging} restart: unless-stopped # 长任务(ingest HEVC 转码最长 30min、生成硬超时 11min)给足优雅关闭窗口 stop_grace_period: 300s stop_signal: SIGTERM env_file: - ../../.env environment: APP_ENV: ${APP_ENV:-staging} # 兼容旧变量:若两个 *_CONCURRENCY 均未显式设置,entrypoint 会按此总数分配 WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-4} WORKER_MAX_TASKS_PER_CHILD: ${WORKER_MAX_TASKS_PER_CHILD:-100} # #2073 队列独立伸缩:generation 默认 2,transcode 默认 2(不再差值计算) GENERATION_CONCURRENCY: ${GENERATION_CONCURRENCY:-2} TRANSCODE_CONCURRENCY: ${TRANSCODE_CONCURRENCY:-2} # beat 默认在本容器启动;独立 beat 容器部署时设为 0 BEAT_ENABLED: ${BEAT_ENABLED:-1} GENERATED_FILES_DIR: /app/generated GENERATED_FILES_URL_PREFIX: /generated-files PUBLIC_API_BASE_URL: ${PUBLIC_API_BASE_URL:-https://api.xiaoxiajianji.com} volumes: - generated-files:/app/generated networks: - xiaoxia-net # 健康检查:至少有一个 celery worker 进程在跑(beat 本身不作为存活依据) healthcheck: test: ["CMD-SHELL", "grep -q 'celery.*worker' /proc/[0-9]*/cmdline 2>/dev/null || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 40s logging: *default-logging # 资源限制:容器总资源 = gen + trans + beat,按 2+2 并发场景建议 4C8G; # 后续如需独立扩容/重启,可拆为 worker-generation / worker-transcode / worker-beat 三个 service。 deploy: resources: limits: cpus: '4.0' memory: 8g reservations: cpus: '1.0' memory: 2G # ========================================= # Web 服务(Nginx + 前端静态文件) # ========================================= web: image: ${WEB_IMAGE:-xiaoxia-saas-web:dev} container_name: xiaoxia-web-${ENV:-staging} restart: unless-stopped ports: - "127.0.0.1:${WEB_PORT:-3001}:80" networks: - xiaoxia-net environment: - APP_ENV=${ENV:-staging} volumes: - ./nginx-${ENV:-staging}.conf:/etc/nginx/conf.d/default.conf:ro healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:80"] interval: 30s timeout: 5s retries: 3 logging: *default-logging deploy: resources: limits: cpus: '0.5' memory: 512M # =========================================== # 共享卷配置 # =========================================== volumes: generated-files: driver: local driver_opts: type: none o: bind device: ${GENERATED_FILES_HOST_DIR:?GENERATED_FILES_HOST_DIR must be set in .env} # =========================================== # 网络配置 # =========================================== networks: xiaoxia-net: external: true name: xiaoxia-net-${ENV:-staging}