Files
photo-live/docker/server.Dockerfile
T
浅念 250d4355b9 build(deploy): production images, compose stacks and 1panel guide
P5-prep Task 1(docs/plans/2026-09-21-P5-准备件.md):

- docker/server.Dockerfile:单镜像 photo-live-server 多角色(api-cn/api-ov/
  worker/sync/migrate-deploy/seed-admin,entrypoint.sh 分发;migrate-deploy
  支持 cn|ov|all 区域参数)。三阶段:全量 install(锁文件冻结)→ prisma
  generate 双库 + 逐包构建;--prod 依赖层;runtime=node:20-alpine+openssl/curl
  + 全局 prisma@5.22.0(对齐锁文件)+ 双库 client 镜像内重建(db-cn 落 pnpm
  store .prisma、db-ov 落包内 generated/)。sharp 走 musl 预编译验证通过。
- docker/web.Dockerfile + infra/deploy/nginx.conf.template:photo-live-web
  (nginx:alpine + envsubst 模板)。viewer(/,history 回退) + admin(/admin/,
  hash 路由) + /v1 反代 API_UPSTREAM(含 /v1/realtime WS 升级)+
  /assets/、/admin/assets/、/embed/v1/ immutable 长缓存(admin assets 的
  alias 前缀剥除坑已实测修复)。VITE_* 为构建期参数(VIEWER_BASE/
  EMBED_ORIGIN 随域名构建传入)。
- infra/deploy/docker-compose.cn.yml / -ov.yml:cn=api-cn+worker+sync+web,
  ov=api-ov+web-ov(核实架构:sync 仅 cn 侧,inbox/emergency 内嵌 api-ov,
  无 ov 侧 sync 进程);健康检查 /v1/health、unless-stopped、json-file
  10m×3、外部 1panel-network 复用 1Panel MySQL/Redis、SERVER_IMAGE/
  WEB_IMAGE 环境变量化镜像 tag(回滚杠杆)。
- .env.production.example-cn/-ov:生产变量全量清单(cn 25 键/ov 24 键,
  DB/Redis/OSS/SYNC/AUTH/限速/域名分组注释,源自 services+packages
  process.env 全量审计);.gitignore 放行 example,真实 .env.production-*
  永不入库。
- infra/deploy/README-1Panel.md:镜像导入双路径(save tar 导入 / 仓库 pull)、
  MySQL 建库授权 SQL(utf8mb4_0900_bin,<8.0.30 回退 utf8mb4_bin——D12)、
  Redis 连接、1Panel 编排、OpenResty 反代+证书、migrate-deploy 一次性容器、
  首次部署顺序(DB→迁移→ov 栈→cn 栈→seed→冒烟)。
- 根 package.json pnpm.onlyBuiltDependencies 增补 sharp/prisma/@prisma/
  engines/@prisma/client(镜像构建所需 install 脚本白名单)。

本地验证:两镜像构建成功(server 988MB / web 97.7MB);compose config 校验
通过;server 镜像内逐服务 require(sharp/argon2/双 Prisma client/bullmq/
ioredis)与双引擎 so.node、migrate-deploy 角色、无 .env 泄漏均实测;web 镜像
容器实测路由(viewer 回退/admin 子路径/assets 与 embed immutable/WS 代理路径
502 语义)全通过。未起真实栈(无真实 DB,符合准备件边界)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:39:55 +08:00

114 lines
6.3 KiB
Docker
Raw 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.
# syntax=docker/dockerfile:1
# docker/server.Dockerfile —— photo-live-server 单镜像多角色(P5-prep Task 1)。
# 决策(docs/plans/2026-09-21-P5-准备件.md Task 1):单镜像 photo-live-server,
# CMD 区分 api-cn / api-ov / worker / sync / migrate-deploy / seed-admin,
# 减少 1Panel 维护面(一个 tar 导入,多角色复用)。
#
# 构建:docker build -f docker/server.Dockerfile -t photo-live-server:<tag> .
# <tag> = git short sha + 日期(见 infra/deploy/README-1Panel.md 版本约定)。
#
# 阶段:
# build 全量 install(锁文件冻结)→ prisma generate 双库 → 逐包 tsc 构建
# (显式依赖序代替 turbo:镜像内无需 turbo 二进制,链路更短更确定)
# prod-deps 仅生产依赖的 workspace install(--prod,供 runtime 复用清单与结构)
# runtime node:20-alpine + openssl(prisma musl 引擎依赖)/curl(healthcheck)
# + 全局 prisma@5.22.0(migrate-deploy 角色;版本对齐 pnpm-lock.yaml)
# + prisma generate 双库(db-cn 生成到 pnpm store 内 .prisma,db-ov
# 生成到包内 generated/——运行时本阶段重建,避免拷贝易碎的 store 深路径)
#
# sharp 在 alpine(musl) 的预编译二进制随 optionalDependencies 分发(0.33+ 无需
# 编译);根 package.json pnpm.onlyBuiltDependencies 已放行其 install 脚本。若
# 未来 sharp 版本在 musl 失败,回退方案:两阶段 base 换 node:20-slim(debian,
# glibc 预编译),无需其它改动。
# ── 阶段 1:构建 ──
FROM node:20-alpine AS build
WORKDIR /app
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN corepack enable
# 先拷全部 workspace 清单 → install 层缓存(源码变动不失效)。
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/admin/package.json apps/admin/
COPY apps/viewer/package.json apps/viewer/
COPY packages/contracts/package.json packages/contracts/
COPY packages/db-cn/package.json packages/db-cn/
COPY packages/db-ov/package.json packages/db-ov/
COPY packages/embed-loader/package.json packages/embed-loader/
COPY packages/storage/package.json packages/storage/
COPY services/api/package.json services/api/
COPY services/sync/package.json services/sync/
COPY services/worker/package.json services/worker/
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# 源码(.dockerignore 已剔除 node_modules/dist/.env/apps/mobile/docs)。
COPY packages packages
COPY services services
# prisma generate 双库:db-ov 的 src/index.ts 转出口 generated/client(gitignore
# 工件,必须先于 tsc);generate 不需要真实 DATABASE_URL_*,migrate deploy 才需要。
RUN pnpm --filter @photo/db-cn --filter @photo/db-ov exec prisma generate
# 逐包构建(依赖序显式;服务端不需要构建 apps/viewer、apps/admin——那是 web 镜像的事)。
RUN pnpm --filter @photo/contracts run build \
&& pnpm --filter @photo/db-cn run build \
&& pnpm --filter @photo/db-ov run build \
&& pnpm --filter @photo/storage run build \
&& pnpm --filter @photo/api-cn --filter @photo/worker --filter @photo/sync run build
# ── 阶段 2:生产依赖 ──
FROM node:20-alpine AS prod-deps
WORKDIR /app
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN corepack enable
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/admin/package.json apps/admin/
COPY apps/viewer/package.json apps/viewer/
COPY packages/contracts/package.json packages/contracts/
COPY packages/db-cn/package.json packages/db-cn/
COPY packages/db-ov/package.json packages/db-ov/
COPY packages/embed-loader/package.json packages/embed-loader/
COPY packages/storage/package.json packages/storage/
COPY services/api/package.json services/api/
COPY services/sync/package.json services/sync/
COPY services/worker/package.json services/worker/
# --prod:剔除 jest/ts-node/playwright 等开发依赖;@prisma/client 走 optional
# 引擎依赖仍在(.prisma 生成产物由 runtime 阶段的 prisma generate 重建)。
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile --prod
# ── 阶段 3:运行时 ──
FROM node:20-alpine
# openssl:prisma 查询引擎 musl 构建链接 openssl3;curl:compose healthcheck;
# tini 未装——compose 侧统一 init: true 承担 PID 1 信号转发。
RUN apk add --no-cache openssl curl
# 全局 prisma CLI(migrate-deploy 角色)。版本必须与 pnpm-lock.yaml 的
# prisma@5.22.0 一致,避免 schema/engine 行为漂移。npm 显式重试参数:构建机到
# registry 偶发 ECONNRESET,npm 默认不重试会硬失败(pnpm 自带重试无此问题)。
RUN --mount=type=cache,target=/root/.npm \
npm install -g --fetch-retries=5 --fetch-retry-mintimeout=20000 \
--fetch-retry-maxtimeout=120000 prisma@5.22.0
ENV NODE_ENV=production
WORKDIR /app
# workspace 结构(清单 + 各包 node_modules 符号链接,pnpm 相对链接布局完整保留)。
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=prod-deps /app/packages ./packages
COPY --from=prod-deps /app/services ./services
# 构建产物。
COPY --from=build /app/packages/contracts/dist ./packages/contracts/dist
COPY --from=build /app/packages/db-cn/dist ./packages/db-cn/dist
COPY --from=build /app/packages/db-ov/dist ./packages/db-ov/dist
COPY --from=build /app/packages/storage/dist ./packages/storage/dist
COPY --from=build /app/services/api/dist ./services/api/dist
COPY --from=build /app/services/worker/dist ./services/worker/dist
COPY --from=build /app/services/sync/dist ./services/sync/dist
# prisma schema + 迁移(migrate-deploy 角色与 generate 都在包内 prisma/ 目录)。
COPY packages/db-cn/prisma ./packages/db-cn/prisma
COPY packages/db-ov/prisma ./packages/db-ov/prisma
# 双库 client 生成:db-cn → pnpm store 内 .prisma/client;db-ov → 包内 generated/
# (运行时镜像内重建,产物即最终布局;docker build 网络可下载 musl 引擎)。
RUN prisma generate --schema=packages/db-cn/prisma/schema.prisma \
&& prisma generate --schema=packages/db-ov/prisma/schema.prisma
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# 缺省角色 api-cn;compose 用 command: 覆盖(api-ov/worker/sync/migrate-deploy)。
CMD ["api-cn"]