services: douyu-login: build: context: . # 生产服务只构建 runtime 阶段;test 阶段仅供本地回归测试。 target: runtime args: # 服务器默认走阿里云镜像;本地测试设为 false。 USE_CHINA_MIRRORS: ${USE_CHINA_MIRRORS:-true} restart: unless-stopped mem_limit: ${DOUYU_LOGIN_MEM_LIMIT:-2g} cpus: ${DOUYU_LOGIN_CPUS:-2} ports: # 保持现有服务端口;可通过 APP_PORT 覆盖。 - "${APP_PORT:-8000}:8800" volumes: # 持久化运行时文件和日志,数据库由 mysql-data 卷管理。 - ./data:/app/data - ./logs:/app/logs environment: - TZ=Asia/Shanghai # 应用日志等级由 .env 控制;生产环境默认只记录 INFO 及以上。 - LOG_LEVEL=${LOG_LEVEL:-INFO} # 统一写入宿主机挂载的 ./logs,避免容器重建后丢失或写入不可见路径。 - LOG_DIR=/app/logs - LOG_ROTATION_SIZE=${LOG_ROTATION_SIZE:-50M} - LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-14} # MySQL 仅在 Compose 内网开放,不映射宿主机端口。 - DB_HOST=mysql - DB_PORT=3306 - DB_NAME=${MYSQL_DATABASE:-douyu_login} - DB_USER=${MYSQL_USER:-douyu_login} - DB_PASSWORD=${MYSQL_PASSWORD:?请在 .env 中设置 MYSQL_PASSWORD} - DB_POOL_SIZE=${DB_POOL_SIZE:-20} - DB_MAX_OVERFLOW=${DB_MAX_OVERFLOW:-20} - DB_POOL_TIMEOUT=${DB_POOL_TIMEOUT:-30} - DB_POOL_RECYCLE=${DB_POOL_RECYCLE:-1800} # JWT 密钥(生产环境务必修改,可用 python -c "import secrets; print(secrets.token_urlsafe(32))" 生成) - JWT_SECRET_KEY=${JWT_SECRET_KEY:-} # 敏感字段落盘加密密钥(生产环境务必长期保持不变) - APP_ENCRYPTION_KEY=${APP_ENCRYPTION_KEY:-} # 默认管理员账号密码(仅首次启动建库时生效) - ADMIN_USERNAME=${ADMIN_USERNAME:-admin} - ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123} # Cookie 安全标志(HTTPS 部署时设为 true) - COOKIE_SECURE=${COOKIE_SECURE:-false} # CORS 允许的源(逗号分隔) - CORS_ORIGINS=${CORS_ORIGINS:-} # Worker 已合并到本容器,仅通过回环地址访问。 - YYB_WORKER_URL=http://127.0.0.1:8810 - YYB_WORKER_KEY=${YYB_WORKER_KEY:-} # 鱼翅直充供应商凭据仅通过容器环境变量注入,不入库、不返回前端。 - FISH_FIN_RECHARGE_BASE_URL=${FISH_FIN_RECHARGE_BASE_URL:-} - FISH_FIN_RECHARGE_APP_ID=${FISH_FIN_RECHARGE_APP_ID:-} - FISH_FIN_RECHARGE_APP_KEY=${FISH_FIN_RECHARGE_APP_KEY:-} - FISH_FIN_RECHARGE_APP_SECRET=${FISH_FIN_RECHARGE_APP_SECRET:-} - FISH_FIN_RECHARGE_NOTIFY_URL=${FISH_FIN_RECHARGE_NOTIFY_URL:-} - FISH_FIN_RECHARGE_TIMEOUT=${FISH_FIN_RECHARGE_TIMEOUT:-20} - FISH_FIN_RECHARGE_DEBUG=${FISH_FIN_RECHARGE_DEBUG:-false} # Roundcube 邮件验证码读取服务地址(不填则使用代码默认值) - MAIL_ROUNDCUBE_URL=${MAIL_ROUNDCUBE_URL:-} # 生产容器禁止 reload;开发环境由 dev.sh 单独启动 reload。 - UVICORN_RELOAD=false # 自动化调度(见 docs/自动化与资料收集规划.md §7)。 # 这个开关只决定「是否起调度线程」;各作业还有自己的开关,默认关闭, # 在 /automation 页面打开。 - AUTOMATION_ENABLED=${AUTOMATION_ENABLED:-false} # order_site 订单只读库(P0 订单读取适配层用;未配置时适配层整体降级) - ORDER_SITE_DATABASE_URL=${ORDER_SITE_DATABASE_URL:-} - ORDER_SITE_LOOKUP_ENABLED=${ORDER_SITE_LOOKUP_ENABLED:-false} - ORDER_SITE_LOOKUP_TIMEOUT_SECONDS=${ORDER_SITE_LOOKUP_TIMEOUT_SECONDS:-3} # 用户上传图片的桶(在线协作 / P2 资料收集共用;生产用内网 endpoint)。 # OSS_MODE=oss 才走对象存储,否则落容器内 ./data/file_storage。 - OSS_MODE=${OSS_MODE:-local} - OSS_ENDPOINT=${OSS_ENDPOINT:-} - OSS_BUCKET=${OSS_BUCKET:-} - OSS_ACCESS_KEY_ID=${OSS_ACCESS_KEY_ID:-} - OSS_ACCESS_KEY_SECRET=${OSS_ACCESS_KEY_SECRET:-} - OSS_PREFIX=${OSS_PREFIX:-collect} - OSS_MAX_UPLOAD_SIZE_MB=${OSS_MAX_UPLOAD_SIZE_MB:-10} networks: - default - order-site-net depends_on: mysql: condition: service_healthy # 增加文件描述符限制,避免 "Too many open files" 错误 ulimits: nofile: soft: 65536 hard: 65536 healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8800/api/health'); urllib.request.urlopen('http://127.0.0.1:8810/health')"] interval: 30s timeout: 5s retries: 3 # 回归测试容器:docker compose --profile test run --rm test # 镜像内为构建时的代码,改动代码后需重新构建(仅按需启动,不影响 docker compose up)。 test: build: context: . target: test args: USE_CHINA_MIRRORS: ${USE_CHINA_MIRRORS:-true} profiles: ["test"] entrypoint: ["python", "-m", "pytest"] mysql: image: ${MYSQL_IMAGE:-docker.m.daocloud.io/library/mysql:8.4} restart: unless-stopped mem_limit: ${MYSQL_MEM_LIMIT:-2g} cpus: ${MYSQL_CPUS:-2} ports: # 仅绑定宿主机本地地址,供 ./dev.sh 的本地后端连接,不对外网开放。 - "${MYSQL_BIND_HOST:-127.0.0.1}:${MYSQL_HOST_PORT:-3307}:3306" environment: - TZ=Asia/Shanghai - MYSQL_DATABASE=${MYSQL_DATABASE:-douyu_login} - MYSQL_USER=${MYSQL_USER:-douyu_login} - MYSQL_PASSWORD=${MYSQL_PASSWORD:?请在 .env 中设置 MYSQL_PASSWORD} - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:?请在 .env 中设置 MYSQL_ROOT_PASSWORD} volumes: - mysql-data:/var/lib/mysql command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_0900_ai_ci # 会话时区与存储口径对齐:库里所有时间列存的都是 UTC(应用一律写 # datetime.now(UTC),MySQL 绑定参数丢掉 tzinfo 后落库即 UTC 裸值)。 # 若仍跟随系统 +08:00,SQL 里的 NOW()/CURDATE() 会比库里时间快 8 小时, # 查询静默返回空、看着像「没数据」。全库无 TIMESTAMP 列、也没有 # CURRENT_TIMESTAMP 默认值,因此改这里不会改变任何已有数据的语义。 - --default-time-zone=+00:00 # 业务表已超过 300MiB;可通过 .env 调整,默认给 MySQL 512MiB 缓冲池。 - --innodb-buffer-pool-size=${MYSQL_INNODB_BUFFER_POOL_SIZE:-512M} # 默认记录 1 秒以上查询,便于验证查询优化;稳定后可设 MYSQL_SLOW_QUERY_LOG=0。 - --slow-query-log=${MYSQL_SLOW_QUERY_LOG:-1} - --long-query-time=${MYSQL_LONG_QUERY_TIME:-1} healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$$MYSQL_ROOT_PASSWORD --silent"] interval: 10s timeout: 5s retries: 12 start_period: 20s volumes: mysql-data: # 复用项目改名前的旧数据卷 douyu_login_py_mysql-data(4G+ 真实数据)。 # 注意:曾用默认值 live-hub-py_mysql-data,那是**另一个空卷** —— 目录改名后第一次 # 重建容器会挂到空卷上,看起来像数据全丢(2026-10-02 实际发生过)。 # 因此默认值与 external 都指向真实卷,不依赖 .env 是否设置。 name: ${MYSQL_VOLUME_NAME:-douyu_login_py_mysql-data} external: ${MYSQL_VOLUME_EXTERNAL:-true} networks: order-site-net: external: true name: order_site_default