# Stage 1: Install dependencies (isolated layout, same as local dev) FROM node:22-bookworm AS deps # 使用阿里云镜像源 # RUN sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources RUN corepack enable && corepack prepare pnpm@10.24.0 --activate RUN apt-get update && \ apt-get install -y --no-install-recommends ffmpeg python3 python3-pip && \ rm -rf /var/lib/apt/lists/* RUN pip3 install --break-system-packages -i https://mirrors.aliyun.com/pypi/simple/ faster-whisper WORKDIR /app COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./ COPY apps/cli/package.json apps/cli/ COPY apps/web/package.json apps/web/ COPY packages/shared/package.json packages/shared/ COPY packages/core/package.json packages/core/ COPY packages/tts/package.json packages/tts/ COPY packages/templates/package.json packages/templates/ COPY packages/collect/package.json packages/collect/ RUN pnpm config set registry https://registry.npmmirror.com && \ pnpm install --frozen-lockfile # Stage 2: Build FROM deps AS build COPY . . RUN pnpm build # Stage 3: Runtime — fresh prod install with hoisted layout FROM node:22-bookworm-slim AS runtime # RUN sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources RUN corepack enable && corepack prepare pnpm@10.24.0 --activate RUN apt-get update && \ apt-get install -y --no-install-recommends \ ffmpeg python3 python3-pip fontconfig \ libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \ libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \ libgbm1 libpango-1.0-0 libcairo2 libasound2 && \ rm -rf /var/lib/apt/lists/* RUN pip3 install --break-system-packages faster-whisper WORKDIR /app # Copy workspace manifests + lockfile so pnpm install can resolve workspaces COPY --from=build /app/package.json /app/pnpm-lock.yaml /app/pnpm-workspace.yaml /app/turbo.json ./ COPY --from=build /app/apps/cli/package.json apps/cli/ COPY --from=build /app/apps/web/package.json apps/web/ COPY --from=build /app/packages/shared/package.json packages/shared/ COPY --from=build /app/packages/core/package.json packages/core/ COPY --from=build /app/packages/tts/package.json packages/tts/ COPY --from=build /app/packages/templates/package.json packages/templates/ COPY --from=build /app/packages/collect/package.json packages/collect/ # Install production deps with hoisted layout (everything flat at root node_modules) RUN pnpm config set registry https://registry.npmmirror.com && \ pnpm install --frozen-lockfile --prod --config.node-linker=hoisted # Re-create workspace symlinks at root so any package can resolve @pipeline/* by walking up RUN mkdir -p node_modules/@pipeline && \ ln -s ../../packages/shared node_modules/@pipeline/shared && \ ln -s ../../packages/core node_modules/@pipeline/core && \ ln -s ../../packages/tts node_modules/@pipeline/tts && \ ln -s ../../packages/templates node_modules/@pipeline/templates && \ ln -s ../../packages/collect node_modules/@pipeline/collect # Copy build artifacts (dist, .next, template src for Remotion entry, etc.) COPY --from=build /app/apps/cli/dist apps/cli/dist/ COPY --from=build /app/apps/web/.next apps/web/.next/ COPY --from=build /app/apps/web/next.config.mjs apps/web/ COPY --from=build /app/apps/web/src apps/web/src/ COPY --from=build /app/packages/shared/dist packages/shared/dist/ COPY --from=build /app/packages/core/dist packages/core/dist/ COPY --from=build /app/packages/tts/dist packages/tts/dist/ COPY --from=build /app/packages/tts/scripts packages/tts/scripts/ COPY --from=build /app/packages/templates/dist packages/templates/dist/ COPY --from=build /app/packages/templates/src packages/templates/src/ COPY --from=build /app/packages/collect/dist packages/collect/dist/ COPY --from=build /app/assets assets/ COPY --from=build /app/config config/ # Install bundled Noto Sans SC as system font so Chrome can render CJK via fontconfig fallback RUN mkdir -p /usr/share/fonts/truetype/noto && \ cp assets/fonts/NotoSansSC-Regular.ttf assets/fonts/NotoSansSC-Bold.ttf /usr/share/fonts/truetype/noto/ && \ fc-cache -f # Run as the non-root `node` user (uid/gid 1000, shipped in the base image) so # the image is Pod Security "restricted"-friendly. Only the writable paths are # chowned to keep the layer small: the PVC-backed output, Next.js' .next cache, # and a HOME for Remotion's browser / faster-whisper model download cache. ENV HOME=/home/node RUN mkdir -p /app/output /home/node && \ chown -R node:node /app/output /home/node /app/apps/web/.next USER node EXPOSE 3000 # Docker Compose / `docker run` health; Kubernetes uses its own probes against # the same endpoints. global fetch is available on Node 22. HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD node -e "fetch('http://127.0.0.1:3000/api/health/live').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" WORKDIR /app/apps/web CMD ["/app/node_modules/.bin/next", "start"]