Featured image of post 透過多階段建置減少 Docker 映像大小Featured image of post 透過多階段建置減少 Docker 映像大小

透過多階段建置減少 Docker 映像大小

了解如何使用 Docker 多階段建置將編譯時工具與生產運行時分離,以最佳化容器佔用空間和安全性。

介紹

保持 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*.jsonRUN npm install)放在之前複製原始碼(COPY . .)。這可確保 Docker 快取依賴層,僅在來源檔案發生變更時跳過套件重新安裝。

結論

多階段建置是優化容器操作的基本技術:

  1. **命名您的建置階段(例如 FROM ... AS builder)。 **
  2. **使用 COPY --from 僅檢索所需的檔案。 **
  3. **使用輕量級基礎作業系統,如 Alpine Linux。 **

在專案中採用多階段 Dockerfile 來加速 CI/CD 管道並保護生產環境。