介紹
保持 Docker 映像佔用空間較小對於加速部署週期、降低儲存成本以及透過縮小容器的攻擊面來加強安全性至關重要。
然而,簡單的 Dockerfile 建構通常會將編譯時相依性(如 gcc、標頭、建置快取)和測試工具直接捆綁到最終的執行時間映像中。這會使圖像大小從幾兆位元組膨脹到數百或千兆位元組。
為了解決這個問題,Docker 引入了多階段建置。本文回顧了多階段架構的核心實踐,展示了使用 TypeScript Node.js 專案進行影像佔用空間最佳化。
1. 什麼是多階段建構?
多階段建置允許您在單一 Dockerfile 中使用多個 FROM 語句。每個 FROM 語句都會使用新的基礎映像啟動一個新的、獨立的「階段」。
您可以選擇性地將工件(例如編譯的二進位檔案或縮小的套件)從上一個階段複製到下一個階段。這允許您放棄繁重的建置依賴項(編譯器、原始碼、linter),並僅將執行時間需求打包到最終容器中。
[Build Stage]
・Base: node (large)
・Install devDependencies
・Compile source (npm run build)
│
▼ (Copy only compiled JS & prod dependencies)
[Runtime Stage]
・Base: alpine (minimal)
・Copy compiled output
・CMD Execution
2. 未最佳化與多階段 Dockerfile
讓我們將傳統 Dockerfile 與 TypeScript 應用程式的多階段 Dockerfile 進行比較。
未優化的方法(重圖像)
# Uses a full Node image containing compilation environments
FROM node:20
WORKDIR /app
# Restore dependencies
COPY package*.json ./
RUN npm install
# Copy source and compile (TypeScript -> JS)
COPY . .
RUN npm run build
# Start the application
EXPOSE 3000
CMD ["node", "dist/index.js"]
- 問題: 產生的映像包含 TypeScript 編譯器(
devDependencies內的typescript套件)、未編譯的.ts原始檔、快取有效負載和重型建置工具。最終影像大小通常超過 900MB。
多層解決方案(Slim Image)
# -----------------------------------------------
# Stage 1: Build Environment (builder)
# -----------------------------------------------
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
# Install all dependencies including devDependencies
RUN npm ci
COPY . .
# Transpile TypeScript to JavaScript
RUN npm run build
# Clean up devDependencies to leave only production modules
RUN npm prune --production
# -----------------------------------------------
# Stage 2: Runtime Environment (runner)
# -----------------------------------------------
# Use a minimal, slim OS base (Alpine Linux)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Copy only the compiled code and production node_modules from the builder stage
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
3. 為什麼影像足跡急遽縮小
實施這種多階段設定實現了兩個主要優化:
- 消除開發依賴:
執行
npm prune --production會去掉節點開發套件(例如打字稿編譯器和 linter),只留下必要的生產庫。 - 採用Alpine作業系統:
使用
node:alpine取代基於 Debian 的節點映像可減少基準作業系統佔用空間。 - 結果: 最終容器佔用空間從 900MB 降至 低於 150MB。
4. 減少足跡的高級技巧
編寫 .dockerignore 文件
為了防止大量本機目錄或敏感值進入建置上下文,請在根資料夾中建立一個 .dockerignore 檔案:
node_modules
.git
.github
npm-debug.log
dist
.env
圖層快取調整
請務必將相依性安裝步驟(COPY package*.json 和 RUN npm install)放在之前複製原始碼(COPY . .)。這可確保 Docker 快取依賴層,僅在來源檔案發生變更時跳過套件重新安裝。
結論
多階段建置是優化容器操作的基本技術:
- **命名您的建置階段(例如
FROM ... AS builder)。 ** - **使用
COPY --from僅檢索所需的檔案。 ** - **使用輕量級基礎作業系統,如 Alpine Linux。 **
在專案中採用多階段 Dockerfile 來加速 CI/CD 管道並保護生產環境。

