# ===== 阶段1: 构建前端 ===== FROM node:20-slim AS frontend-builder WORKDIR /build COPY web/frontend/package.json web/frontend/package-lock.json* ./ # 优先 npmmirror,失败时回退官方 registry RUN npm config set registry https://registry.npmmirror.com && \ (npm ci || npm install) || \ (npm config set registry https://registry.npmjs.org && (npm ci || npm install)) COPY web/frontend/ ./ RUN npm run build # ===== 阶段2: 构建应用宝 Worker 的 Node 依赖 ===== FROM node:20-slim AS yyb-node-builder WORKDIR /build COPY services/yyb-worker/package.json services/yyb-worker/package-lock.json ./ # 优先 npmmirror,失败时回退官方 registry,避免国内构建时依赖下载中断。 RUN npm config set registry https://registry.npmmirror.com && \ npm ci --omit=dev || \ (npm config set registry https://registry.npmjs.org && npm ci --omit=dev) # ===== 阶段3: Python 运行时 ===== FROM python:3.12-slim AS runtime ARG USE_CHINA_MIRRORS=true # 服务器默认使用阿里云镜像;本地构建可设 USE_CHINA_MIRRORS=false 直连官方源。 RUN if [ "$USE_CHINA_MIRRORS" = "true" ]; then \ cp /etc/apt/sources.list.d/debian.sources /tmp/debian.sources && \ sed -i 's/deb.debian.org/mirrors.cloud.aliyuncs.com/g' /etc/apt/sources.list.d/debian.sources && \ (apt-get update -o APT::Update::Error-Mode=any || \ (sed -i 's/mirrors.cloud.aliyuncs.com/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources && \ apt-get update -o APT::Update::Error-Mode=any) || \ (cp /tmp/debian.sources /etc/apt/sources.list.d/debian.sources && \ apt-get update -o APT::Update::Error-Mode=any)); \ else \ apt-get update -o APT::Update::Error-Mode=any; \ fi && \ apt-get install -y --no-install-recommends \ libgl1 \ libglib2.0-0 \ nodejs \ && rm -rf /var/lib/apt/lists/* # 安装 uv:服务器走阿里云镜像,本地直连官方 PyPI。 RUN if [ "$USE_CHINA_MIRRORS" = "true" ]; then \ pip install -i https://mirrors.cloud.aliyuncs.com/pypi/simple/ uv || \ pip install -i https://mirrors.aliyun.com/pypi/simple/ uv || \ pip install uv; \ else \ pip install uv; \ fi WORKDIR /app # 先复制依赖文件,利用 Docker 缓存层 COPY pyproject.toml uv.lock* README.md alembic.ini ./ RUN uv venv /app/.venv && \ if [ "$USE_CHINA_MIRRORS" = "true" ]; then \ (UV_INDEX_URL=https://mirrors.cloud.aliyuncs.com/pypi/simple/ uv pip install -e . || \ UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ uv pip install -e . || \ uv pip install -e .); \ else \ uv pip install -e .; \ fi # 复制项目代码 COPY core/ ./core/ COPY utils/ ./utils/ COPY web/backend/ ./web/backend/ COPY scripts/migrate_sqlite_to_mysql.py ./scripts/migrate_sqlite_to_mysql.py # 将应用宝 Worker 合并进同一应用镜像;其 Python 依赖已纳入项目 uv.lock。 COPY services/yyb-worker/runtime/ ./services/yyb-worker/runtime/ COPY --from=yyb-node-builder /build/node_modules ./services/yyb-worker/runtime/node_modules COPY docker-entrypoint.sh ./docker-entrypoint.sh # 复制前端构建产物 COPY --from=frontend-builder /build/dist ./web/frontend/dist # 创建数据和日志目录 RUN mkdir -p /app/data /app/logs /app/data/yyb-worker-jobs && \ chmod +x /app/docker-entrypoint.sh # 环境变量 ENV PYTHONUNBUFFERED=1 ENV PYTHONDONTWRITEBYTECODE=1 ENV PATH="/app/.venv/bin:$PATH" # 生产镜像默认关闭文件监控重载,避免 data/logs 写入触发重启循环。 ENV UVICORN_RELOAD=false ENV YYB_WORKER_URL=http://127.0.0.1:8810 EXPOSE 8800 # 健康检查同时覆盖 Web API 与同容器 Worker。 HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ 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')" || exit 1 # 同一容器内启动 Web 与本机 Worker;入口脚本负责联动退出和信号转发。 ENTRYPOINT ["/app/docker-entrypoint.sh"]