# 两阶段构建(Phase 4 Task 1): # 阶段一 node:20-alpine 构建 admin 前端 → dist; # 阶段二 python:3.13-slim 只带运行时所需(app/static/seed_profiles/ # scripts + 前端 dist),不带 node/tests/docs。 # ---- 阶段一: admin 前端构建 -------------------------------------------------- FROM node:20-alpine AS admin-build WORKDIR /build # 先拷清单再拷源码:依赖层缓存与源码改动解耦 COPY admin/package.json admin/pnpm-lock.yaml admin/pnpm-workspace.yaml ./ # corepack 按 package.json 的 packageManager 字段钉 pnpm 版本(与本地 # 一致的单一事实源);显式装 0.36.0(实测通过)规避 node20 镜像内置旧版 # 的签名钥问题——钉具体版本,不追 latest(构建可复现) RUN npm install -g corepack@0.36.0 && corepack enable RUN pnpm install --frozen-lockfile COPY admin/ ./ # VITE_BASE_URL 取 admin/.env.production(=/admin/):产物资源引用与后端 # 挂载路径同源;不靠构建期 env 注入(vite 的 env/文件优先级易漂移, # 文件为单一事实源) RUN pnpm build # ---- 阶段二: 生产镜像 -------------------------------------------------------- FROM python:3.13-slim WORKDIR /app # tzdata: trixie slim 实测已自带(容器内 TZ=Asia/Shanghai 生效,UTC+8); # 显式安装留作基镜像漂移保险——将来换基/slim 进一步瘦身时,缺 tzdata 的 # TZ 会被静默忽略(退 UTC),推送窗口等业务时间随之漂移 RUN apt-get update \ && apt-get install -y --no-install-recommends tzdata \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY app ./app COPY static ./static COPY seed_profiles ./seed_profiles COPY scripts ./scripts COPY --from=admin-build /build/dist ./admin/dist EXPOSE 8018 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8018", "--workers", "1"]