|
|
@@ -2,15 +2,16 @@
|
|
|
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 sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources
|
|
|
|
|
|
+ENV COREPACK_NPM_REGISTRY=https://registry.npmmirror.com
|
|
|
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 faster-whisper
|
|
|
+RUN pip3 install --break-system-packages -i https://mirrors.aliyun.com/pypi/simple/ faster-whisper
|
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
|
@@ -30,13 +31,18 @@ RUN pnpm config set registry https://registry.npmmirror.com && \
|
|
|
FROM deps AS build
|
|
|
|
|
|
COPY . .
|
|
|
-RUN pnpm build
|
|
|
+# `next start` only reads .next/{server,static,types,*.json}; the 189MB
|
|
|
+# .next/cache is a webpack/swc build cache used solely by `next build`/dev.
|
|
|
+# Drop it here so it never enters the runtime COPY (and never gets re-duplicated
|
|
|
+# by the writable-dirs step below).
|
|
|
+RUN pnpm build && rm -rf apps/web/.next/cache
|
|
|
|
|
|
# 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 sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources
|
|
|
|
|
|
+ENV COREPACK_NPM_REGISTRY=https://registry.npmmirror.com
|
|
|
RUN corepack enable && corepack prepare pnpm@10.24.0 --activate
|
|
|
|
|
|
RUN apt-get update && \
|
|
|
@@ -47,7 +53,7 @@ RUN apt-get update && \
|
|
|
libgbm1 libpango-1.0-0 libcairo2 libasound2 && \
|
|
|
rm -rf /var/lib/apt/lists/*
|
|
|
|
|
|
-RUN pip3 install --break-system-packages faster-whisper
|
|
|
+RUN pip3 install --break-system-packages -i https://mirrors.aliyun.com/pypi/simple/ faster-whisper
|
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
|
@@ -75,7 +81,7 @@ RUN mkdir -p node_modules/@pipeline && \
|
|
|
|
|
|
# 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 --chown=node:node /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/
|
|
|
@@ -93,13 +99,47 @@ 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
|
|
|
|
|
|
+# Bundle Remotion's headless Chrome so rendering needs NO network at runtime.
|
|
|
+# Remotion normally auto-downloads chrome-headless-shell from Google's CDN
|
|
|
+# (storage.googleapis.com / remotion.media) on first render, but that is
|
|
|
+# ~5KB/s from CN networks and stalls indefinitely. Fetch the SAME binary
|
|
|
+# Remotion wants from the Aliyun-backed npmmirror binary mirror at build time
|
|
|
+# and lay it out exactly as Remotion's BrowserFetcher expects, so
|
|
|
+# ensureBrowser() finds it present (revision.local && existsSync(executablePath))
|
|
|
+# and skips the download. Version is read from the installed @remotion/renderer
|
|
|
+# so this auto-tracks Remotion upgrades instead of hardcoding a number.
|
|
|
+RUN set -eux; \
|
|
|
+ SRC=/app/node_modules/@remotion/renderer/dist/browser/get-chrome-download-url.js; \
|
|
|
+ VERSION=$(grep -oE "TESTED_VERSION = '[0-9.]+'" "$SRC" | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -1); \
|
|
|
+ test -n "$VERSION"; \
|
|
|
+ CACHE=/app/node_modules/.remotion/chrome-headless-shell; \
|
|
|
+ mkdir -p "$CACHE/linux64"; \
|
|
|
+ URL="https://registry.npmmirror.com/-/binary/chrome-for-testing/${VERSION}/linux64/chrome-headless-shell-linux64.zip"; \
|
|
|
+ node -e "const fs=require('fs');fetch(process.argv[1]).then(r=>r.arrayBuffer()).then(b=>fs.writeFileSync(process.argv[2],Buffer.from(b)))" "$URL" /tmp/chs.zip; \
|
|
|
+ python3 -m zipfile -e /tmp/chs.zip "$CACHE/linux64/"; \
|
|
|
+ rm -f /tmp/chs.zip; \
|
|
|
+ BIN="$CACHE/linux64/chrome-headless-shell-linux64/chrome-headless-shell"; \
|
|
|
+ chmod +x "$BIN"; \
|
|
|
+ printf '%s' "$VERSION" > "$CACHE/VERSION"
|
|
|
+
|
|
|
# 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.
|
|
|
+# the image is Pod Security "restricted"-friendly. We only chown the SMALL,
|
|
|
+# empty runtime-writable dirs (output, HOME, webpack cache). We deliberately do
|
|
|
+# NOT `chown -R` over .next or the Remotion chrome tree: in overlayfs a
|
|
|
+# recursive chown copies every touched file into a fresh layer, which here
|
|
|
+# duplicated ~470MB (.next + chrome) for no functional benefit.
|
|
|
+# - .next is already node-owned via `COPY --chown` above.
|
|
|
+# - The Remotion chrome under .remotion is root-owned but world a+rx (default
|
|
|
+# umask 022 plus the explicit `chmod +x` on the binary), so the node user
|
|
|
+# can read and execute it. We only chmod the .remotion *directory* 777 so a
|
|
|
+# stray write (none expected once chrome is pre-placed) can still succeed.
|
|
|
ENV HOME=/home/node
|
|
|
-RUN mkdir -p /app/output /home/node && \
|
|
|
- chown -R node:node /app/output /home/node /app/apps/web/.next
|
|
|
+RUN mkdir -p /app/output /home/node \
|
|
|
+ /app/node_modules/.remotion \
|
|
|
+ /app/packages/templates/node_modules/.cache && \
|
|
|
+ chown -R node:node /app/output /home/node \
|
|
|
+ /app/packages/templates/node_modules/.cache && \
|
|
|
+ chmod 777 /app/node_modules/.remotion
|
|
|
USER node
|
|
|
|
|
|
EXPOSE 3000
|