透過 Docker Buildx Bake 精通多平台建置、測試及更多功能
本指南將示範如何利用 Docker Buildx Bake 來簡化並自動化映像檔建置、測試以及生成建置產出物的流程。透過在宣告式的 docker-bake.hcl 檔案中定義建置設定,您可以省去手動指令碼,並為複雜的建置、測試和產出物生成建立高效的工作流程。
預設條件
本指南假設您已熟悉下列內容:
先決條件
- 您的電腦已安裝最新版本的 Docker。
- 您已安裝 Git 以便複製儲存庫。
- 您正在使用 containerd 映像檔儲存庫。
簡介
本指南使用一個範例專案來示範 Docker Buildx Bake 如何簡化您的建置與測試工作流程。該儲存庫同時包含 Dockerfile 和 docker-bake.hcl 檔案,提供您現成的設定來嘗試 Bake 指令。
首先複製範例儲存庫
git clone https://github.com/dvdksn/bakeme.git
cd bakemeBake 檔案 (docker-bake.hcl) 使用宣告式語法定義建置目標,並透過 targets (目標) 和 groups (群組) 的方式,讓您可以高效管理複雜的建置。
以下是開箱即用的 Bake 檔案內容:
target "default" {
target = "image"
tags = [
"bakeme:latest",
]
attest = [
"type=provenance,mode=max",
"type=sbom",
]
platforms = [
"linux/amd64",
"linux/arm64",
"linux/riscv64",
]
}target 關鍵字定義了 Bake 的一個建置目標。default 目標定義了在命令列未指定特定目標時所要建置的目標。以下是 default 目標選項的快速摘要:
target:Dockerfile 中的建置階段 (build stage)。tags:要指派給映像檔的標籤。attest:要附加到映像檔的 證明 (Attestations)。提示證明提供了元數據,例如「建置來源證明 (build provenance)」(追蹤映像檔建置的來源) 和 SBOM (軟體物料清單),這些對於安全性稽核與合規性非常有用。
platforms:要建置的平台變體。
若要執行此建置,只需在儲存庫根目錄下執行下列指令:
$ docker buildx bake
使用 Bake,您可以避免冗長且難以記憶的命令列組合,透過結構化的設定檔取代容易出錯的手動指令碼,從而簡化建置設定的管理。
作為對比,若沒有 Bake,此建置指令看起來會是這樣:
$ docker buildx build \
--target=image \
--tag=bakeme:latest \
--provenance=true \
--sbom=true \
--platform=linux/amd64,linux/arm64,linux/riscv64 \
.
測試與 Lint 檢查
Bake 不僅僅用於定義建置設定與執行建置。您也可以使用 Bake 來執行測試,有效地將 BuildKit 當作工作執行器 (task runner)。在容器中執行測試對於確保結果的可重現性非常有用。本節將展示如何加入兩種類型的測試:
- 使用
go test進行單元測試。 - 使用
golangci-lint進行風格違規檢查 (Linting)。
以測試驅動開發 (TDD) 的方式,首先在 Bake 檔案中加入一個新的 test 目標:
target "test" {
target = "test"
output = ["type=cacheonly"]
}提示使用
type=cacheonly可確保建置輸出被有效地捨棄;層級 (layers) 會儲存到 BuildKit 的快取中,但 Buildx 不會嘗試將結果載入到 Docker Engine 的映像檔儲存庫。對於測試執行,您不需要匯出建置輸出 — 只有測試執行結果才是重要的。
若要執行此 Bake 目標,請執行 docker buildx bake test。此時,您會收到一個錯誤,指出 Dockerfile 中不存在 test 階段。
$ docker buildx bake test
[+] Building 1.2s (6/6) FINISHED
=> [internal] load local bake definitions
...
ERROR: failed to solve: target stage "test" could not be found
為了滿足此目標,請加入對應的 Dockerfile 目標。這裡的 test 階段是基於與 build 階段相同的基礎階段 (base stage)。
FROM base AS test
RUN --mount=target=. \
--mount=type=cache,target=/go/pkg/mod \
go test .提示
--mount=type=cache指令 會在建置之間快取 Go 模組,避免重新下載依賴項,進而提高建置效能。此共用快取確保了在建置、測試及其他階段中均可使用相同的依賴項集合。
現在,使用 Bake 執行 test 目標將會評估此專案的單元測試。如果您想驗證它是否運作,可以對 main_test.go 進行任意修改以導致測試失敗。
接下來,為了啟用 Lint 檢查,請在 Bake 檔案中加入另一個名為 lint 的目標:
target "lint" {
target = "lint"
output = ["type=cacheonly"]
}並在 Dockerfile 中加入建置階段。此階段將使用 Docker Hub 上的官方 golangci-lint 映像檔。
提示由於此階段依賴於執行外部依賴項,通常建議將您想使用的版本定義為建置引數 (build argument)。這讓您將依賴項版本集中放置在 Dockerfile 的開頭,從而更輕鬆地管理未來的版本升級。
ARG GO_VERSION="1.23"
ARG GOLANGCI_LINT_VERSION="1.61"
#...
FROM golangci/golangci-lint:v${GOLANGCI_LINT_VERSION}-alpine AS lint
RUN --mount=target=.,rw \
golangci-lint run最後,若要同時執行兩項測試,您可以使用 Bake 檔案中的 groups 結構。群組可以指定在單次呼叫中執行多個目標。
group "validate" {
targets = ["test", "lint"]
}現在,執行兩項測試只需:
$ docker buildx bake validate
建置變體
有時您需要建置程式的多個版本。以下範例使用 Bake 結合 矩陣 (matrices) 來建置程式的「發行 (release)」與「除錯 (debug)」變體。使用矩陣可以讓您執行具有不同設定的平行建置,節省時間並確保一致性。
矩陣會將單一建置擴展為多個建置,每個建置代表矩陣參數的唯一組合。這意味著您可以編排 Bake 同時建置程式的生產環境與開發環境版本,且僅需極少的設定變更。
本指南的範例專案已設定為使用建置時選項,以有條件地啟用除錯日誌記錄與追蹤功能。
- 如果您使用
go build -tags="debug"編譯程式,則會啟用額外的日誌記錄與追蹤功能 (開發模式)。 - 如果您在沒有
debug標籤的情況下建置,則程式將會使用預設的 logger 進行編譯 (生產模式)。
透過加入定義要建置的變數組合之 matrix 屬性來更新 Bake 檔案:
target "default" {
+ matrix = {
+ mode = ["release", "debug"]
+ }
+ name = "image-${mode}"
target = "image"
matrix 屬性定義了要建置的變體 (「release」與「debug」)。name 屬性定義了矩陣如何擴展為多個不同的建置目標。在此例中,matrix 屬性將建置擴展為兩個工作流程:image-release 與 image-debug,每個工作流程使用不同的設定參數。
接下來,定義一個名為 BUILD_TAGS 的建置引數,該引數採用矩陣變數的值。
target = "image"
+ args = {
+ BUILD_TAGS = mode
+ }
tags = [
您還需要變更映像檔標籤指派給這些建置的方式。目前,兩個矩陣路徑會產生相同的映像檔標籤名稱,並互相覆蓋。請更新 tags 屬性,使用條件運算子根據矩陣變數值來設定標籤。
tags = [
- "bakeme:latest",
+ mode == "release" ? "bakeme:latest" : "bakeme:dev"
]
- 如果
mode為release,標籤名稱為bakeme:latest - 如果
mode為debug,標籤名稱為bakeme:dev
最後,更新 Dockerfile 以在編譯階段使用 BUILD_TAGS 引數。當 -tags="${BUILD_TAGS}" 選項評估為 -tags="debug" 時,編譯器會使用 debug.go 檔案中的 configureLogging 函數。
# build compiles the program
FROM base AS build
-ARG TARGETOS TARGETARCH
+ARG TARGETOS TARGETARCH BUILD_TAGS
ENV GOOS=$TARGETOS
ENV GOARCH=$TARGETARCH
RUN --mount=target=. \
--mount=type=cache,target=/go/pkg/mod \
- go build -o "/usr/bin/bakeme" .
+ go build -tags="${BUILD_TAGS}" -o "/usr/bin/bakeme" .
就是這樣。透過這些變更,您的 docker buildx bake 指令現在會建置兩個多平台映像檔變體。您可以使用 docker buildx bake --print 指令查看 Bake 生成的正規建置設定。執行此指令顯示 Bake 將執行一個 default 群組,其中包含兩個具有不同建置引數與映像檔標籤的目標。
{
"group": {
"default": {
"targets": ["image-release", "image-debug"]
}
},
"target": {
"image-debug": {
"attest": ["type=provenance,mode=max", "type=sbom"],
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"BUILD_TAGS": "debug"
},
"tags": ["bakeme:dev"],
"target": "image",
"platforms": ["linux/amd64", "linux/arm64", "linux/riscv64"]
},
"image-release": {
"attest": ["type=provenance,mode=max", "type=sbom"],
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"BUILD_TAGS": "release"
},
"tags": ["bakeme:latest"],
"target": "image",
"platforms": ["linux/amd64", "linux/arm64", "linux/riscv64"]
}
}
}加上所有平台變體,這意味著此建置設定總共生成 6 種不同的映像檔。
$ docker buildx bake
$ docker image ls --tree
IMAGE ID DISK USAGE CONTENT SIZE USED
bakeme:dev f7cb5c08beac 49.3MB 28.9MB
├─ linux/riscv64 0eae8ba0367a 9.18MB 9.18MB
├─ linux/arm64 56561051c49a 30MB 9.89MB
└─ linux/amd64 e8ca65079c1f 9.8MB 9.8MB
bakeme:latest 20065d2c4d22 44.4MB 25.9MB
├─ linux/riscv64 7cc82872695f 8.21MB 8.21MB
├─ linux/arm64 e42220c2b7a3 27.1MB 8.93MB
└─ linux/amd64 af5b2dd64fde 8.78MB 8.78MB
匯出建置產出物
匯出如二進位檔之類的建置產出物,對於部署到沒有 Docker 或 Kubernetes 的環境非常有用。例如,當您的程式旨在於使用者的本機機器上執行時。
提示本節討論的技術不僅適用於二進位檔等建置輸出,也適用於任何類型的產出物,例如測試報告。
使用 Go 與 Rust 等程式語言時,由於編譯出的二進位檔通常是可攜的,建立僅用於匯出二進位檔的替代建置目標非常簡單。您只需要在 Dockerfile 中加入一個空階段,其中只包含您想要匯出的二進位檔即可。
首先,讓我們加入一種快速的方式來為您的本機平台建置二進位檔,並將其匯出到本機檔案系統的 ./build/local。
在 docker-bake.hcl 檔案中,建立一個新的 bin 目標。在此階段,將 output 屬性設為本機檔案系統路徑。Buildx 會自動偵測到該輸出看起來像是一個檔案路徑,並使用 local exporter (本機匯出器) 將結果匯出到指定路徑。
target "bin" {
target = "bin"
output = ["build/bin"]
platforms = ["local"]
}請注意,此階段指定了 local 平台。預設情況下,如果未指定 platforms,建置會以 BuildKit 主機的作業系統與架構為目標。如果您使用的是 Docker Desktop,這通常意味著建置目標為 linux/amd64 或 linux/arm64,即使您的本機機器是 macOS 或 Windows,因為 Docker 執行於 Linux 虛擬機中。使用 local 平台可強制目標平台與您的本機環境相符。
接下來,將 bin 階段加入到 Dockerfile 中,該階段會從建置階段複製編譯好的二進位檔。
FROM scratch AS bin
COPY --from=build "/usr/bin/bakeme" /現在您可以使用 docker buildx bake bin 匯出本機平台版本的二進位檔。例如,在 macOS 上,此建置目標會生成 Mach-O 格式的執行檔 — 這是 macOS 的標準執行檔格式。
$ docker buildx bake bin
$ file ./build/bin/bakeme
./build/bin/bakeme: Mach-O 64-bit executable arm64
接下來,讓我們加入一個目標來建置程式的所有平台變體。若要執行此操作,您可以 繼承 (inherit) 您剛剛建立的 bin 目標,並透過加入所需的平台來擴展它。
target "bin-cross" {
inherits = ["bin"]
platforms = [
"linux/amd64",
"linux/arm64",
"linux/riscv64",
]
}現在,建置 bin-cross 目標會為所有平台建立二進位檔。每個變體都會自動建立子目錄。
$ docker buildx bake bin-cross
$ tree build/
build/
└── bin
├── bakeme
├── linux_amd64
│ └── bakeme
├── linux_arm64
│ └── bakeme
└── linux_riscv64
└── bakeme
5 directories, 4 files
若要同時生成「release」與「debug」變體,您可以使用與 default 目標相同的方式使用矩陣。使用矩陣時,您還需要根據矩陣值區分輸出目錄,否則二進位檔會被寫入相同位置供每個矩陣執行使用。
target "bin-all" {
inherits = ["bin-cross"]
matrix = {
mode = ["release", "debug"]
}
name = "bin-${mode}"
args = {
BUILD_TAGS = mode
}
output = ["build/bin/${mode}"]
}$ rm -r ./build/
$ docker buildx bake bin-all
$ tree build/
build/
└── bin
├── debug
│ ├── linux_amd64
│ │ └── bakeme
│ ├── linux_arm64
│ │ └── bakeme
│ └── linux_riscv64
│ └── bakeme
└── release
├── linux_amd64
│ └── bakeme
├── linux_arm64
│ └── bakeme
└── linux_riscv64
└── bakeme
10 directories, 6 files
結論
Docker Buildx Bake 簡化了複雜的建置工作流程,實現了高效的多平台建置、測試與產出物匯出。透過將 Buildx Bake 整合到您的專案中,您可以簡化 Docker 建置、使建置設定具有可攜性,並更輕鬆地駕馭複雜的設定。
嘗試不同的設定並擴展您的 Bake 檔案以滿足您的專案需求。您或許可以考慮將 Bake 整合到您的 CI/CD 管線中,以自動化建置、測試與產出物部署。Buildx Bake 的靈活性與強大功能可以顯著改善您的開發與部署流程。
延伸閱讀
如需更多關於如何使用 Bake 的資訊,請參閱以下資源: