跳至主要内容

Multi-Stage Build

什麼是多階段構建?

多階段構建(Multi-Stage Build)Dockerfile 提供的一種最佳化技術,可以減少映像檔大小,並確保最終的映像檔只包含必要的執行環境,而不會帶有開發工具或額外的檔案。

在多階段構建中,Dockerfile 會定義多個階段(Stages),前面的階段負責編譯與打包,最後的階段則負責執行應用程式。

multi-stage-build


為什麼需要多階段構建?

在一般的 Dockerfile 中,我們可能會:

  1. 下載依賴(Install Dependencies)
  2. 編譯應用程式(Build Application)
  3. 執行應用程式(Run Application)

這樣做的問題是:

  • 最終的映像檔可能會包含開發工具(如編譯器、測試工具)
  • 映像檔會變得很大,占用大量空間
  • 安全性較低(因為開發工具不應該出現在生產環境)

沒有使用多階段構建的 Dockerfile

FROM golang:1.21 AS base

WORKDIR /app
COPY . .

RUN go build -o myapp
CMD ["./myapp"]

這樣的問題是:映像檔內包含整個 Go 開發環境,即使執行時不需要 Golang,仍然佔用空間。


多階段構建範例

我們可以使用多階段構建來解決這個問題,讓映像檔變得更小、更快、更安全

使用 Multi-Stage Build

# === 第一階段:編譯應用程式(Build Stage) ===
FROM golang:1.21 AS builder

# 設定工作目錄
WORKDIR /app

# 複製專案程式碼
COPY . .

# 初始化 Go 模組(如果沒有 go.mod)
RUN go mod init mygoapp && go mod tidy

# 編譯程式(靜態編譯,讓執行時不依賴 Go 環境)
RUN CGO_ENABLED=0 go build -o myapp && chmod +x myapp

# === 第二階段:最小化執行環境(Run Stage) ===
FROM alpine:latest

# 安裝必要的函式庫
RUN apk --no-cache add ca-certificates

# 設定工作目錄
WORKDIR /root/

# 複製第一階段的執行檔
COPY --from=builder /app/myapp .

# 開放 Port 8080
EXPOSE 8080

# 執行應用程式
CMD ["./myapp"]

多階段構建的運作方式

第一階段(Builder Stage)

  • 使用 golang:1.21 作為基礎映像檔
  • 設定 /app 為工作目錄,並將專案程式碼複製到容器內
  • 初始化 Go 模組go mod init),並下載所需依賴(go mod tidy
  • 編譯 Go 應用程式(使用 CGO_ENABLED=0 來確保靜態編譯)
  • 設定執行權限chmod +x myapp

第二階段(Run Stage)

  • 使用 alpine:latest(更小的 Linux 映像檔)作為執行環境
  • 安裝 ca-certificates(若應用程式需要 HTTPS 請求)
  • 僅複製已編譯的 myapp 可執行檔(不包含 Go 編譯器、測試工具)
  • 開放 8080 端口,供外部存取應用程式
  • 設定 CMD ["./myapp"],讓容器啟動時執行應用程式

優勢比較

方法映像檔大小安全性執行效能
單階段構建大(包含完整開發環境)低(可能包含不必要的工具)慢(更多依賴)
多階段構建小(只保留執行檔)高(無多餘工具)快(精簡執行環境)

多階段構建適用的情境

  • Golang、Rust、C++、Java 等需要編譯的語言
  • Node.js、Python 等有大量依賴的應用程式
  • 任何希望減少映像檔大小、提升安全性與效能的應用

總結

  1. 多階段構建可讓最終映像檔變小,因為不會保留開發工具與不必要的檔案。
  2. 提高安全性,因為只包含最小的執行環境,不會帶入開發工具。
  3. 提升執行效能,減少啟動時間與依賴。
  4. 適用於所有需要編譯的應用(特別適用於 Go、C++、Rust、Java)。

使用 Multi-Stage Build,可以讓 Docker 映像檔更輕量、更快、更安全! 🚀