多階段建置把建置工具擋在最終 image 之外
編譯器、devDependencies、原始碼在執行時完全用不到,卻全都留在最終 image 裡:又肥(下載慢、部署慢、存放成本高)又不安全(多餘的工具=多餘的攻擊面,原始碼也被打包進去)。教材稱這是實務上最有價值的單一技巧,而大部分入門教學不會講。
做法:第一階段編譯,第二階段只拿產出物
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # 裝全部依賴(含 dev)
COPY . .
RUN npm run build # 產出 /app/dist
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev # 只裝正式依賴
COPY --from=builder /app/dist ./dist # ← 關鍵這行
USER node
CMD ["node", "dist/server.js"]前面的 stage 只是臨時工作區,整層被丟掉:1.2GB → 約 60MB。
極端案例
- Go +
FROM scratch(完全空白的 image)→ 最終可能只有 8MB,裡面連 shell 都沒有,攻擊面趨近於零 - React / Vue → builder 產出靜態檔,第二階段用
nginx:alpine提供,最終完全不需要 Node
其他瘦身手段
基底選 -alpine 或 -slim/npm ci --omit=dev、pip install --no-cache-dir/同一個 RUN 裝完就清快取/寫好 .dockerignore/用 docker history 找肥的那層(見 Dockerfile 分層快取由上往下連鎖失效)。
⚠️ Alpine 用 musl libc,少數需要編譯原生模組的套件會有相容性問題,不確定就先用 -slim(Debian 精簡版)折衷。