使用 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 bakeme

Bake 檔案 docker-bake.hcl 使用目標和群組以宣告式語法定義建置目標,讓您可以有效管理複雜的建置。

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 中的目標建置階段。

  • tags:要指派給映像檔的標籤。

  • attest:要附加到映像檔的證明

    提示

    證明提供中繼資料,例如建置來源,追蹤映像檔建置的來源,以及 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 \
  .

測試和程式碼檢查

Bake 不僅適用於定義建置配置和執行建置。您也可以使用 Bake 來執行測試,有效地將 BuildKit 用作任務執行器。在容器中執行測試非常適合確保結果可重複。本節將說明如何新增兩種測試

  • 使用 go test 進行單元測試。
  • 使用 golangci-lint 檢查樣式違規。

以測試驅動開發 (TDD) 的方式,首先將新的 test 目標新增到 Bake 檔案

target "test" {
  target = "test"
  output = ["type=cacheonly"]
}

提示

使用 type=cacheonly 可確保有效地捨棄建置輸出;圖層會儲存到 BuildKit 的快取中,但 Buildx 不會嘗試將結果載入到 Docker 引擎的映像檔儲存庫中。

對於測試執行,您不需要匯出建置輸出 — 只有測試執行才重要。

要執行此 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 階段與建置階段基於相同的基礎階段。

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 進行任意變更以導致測試失敗。

接下來,要啟用程式碼檢查,請將另一個目標新增到 Bake 檔案,名為 lint

target "lint" {
  target = "lint"
  output = ["type=cacheonly"]
}

在 Dockerfile 中,新增建置階段。此階段將使用 Docker Hub 上的官方 golangci-lint 映像檔。

提示

由於此階段依賴於執行外部依賴項,因此通常最好將您要使用的版本定義為建置引數。這讓您可以透過將依賴項版本並置到 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 來建置程式的個別「發行」和「偵錯」變體,使用矩陣。使用矩陣可以讓您使用不同的配置執行平行建置,節省時間並確保一致性。

矩陣將單一建置擴展為多個建置,每個建置代表矩陣參數的唯一組合。這表示您可以協調 Bake 以平行建置程式的生產和開發建置,只需最少的配置變更。

本指南的範例專案設定為使用建置時選項來有條件地啟用偵錯記錄和追蹤功能。

  • 如果您使用 go build -tags="debug" 編譯程式,則會啟用額外的記錄和追蹤功能(開發模式)。
  • 如果您在沒有 debug 標籤的情況下建置,則程式會使用預設記錄器進行編譯(生產模式)。

透過新增定義要建置的變數組合的矩陣屬性來更新 Bake 檔案

docker-bake.hcl
 target "default" {
+  matrix = {
+    mode = ["release", "debug"]
+  }
+  name = "image-${mode}"
   target = "image"

matrix 屬性定義要建置的變體(「發行」和「偵錯」)。name 屬性定義矩陣如何擴展為多個不同的建置目標。在本例中,矩陣屬性將建置擴展為兩個工作流程:image-releaseimage-debug,每個工作流程使用不同的配置參數。

接下來,定義一個名為 BUILD_TAGS 的建置引數,它採用矩陣變數的值。

docker-bake.hcl
   target = "image"
+  args = {
+    BUILD_TAGS = mode
+  }
   tags = [

您還需要變更映像檔標籤指派給這些建置的方式。目前,兩個矩陣路徑都會產生相同的映像檔標籤名稱,並互相覆蓋。更新 tags 屬性,使用條件運算子根據矩陣變數值設定標籤。

docker-bake.hcl
   tags = [
-    "bakeme:latest",
+    mode == "release" ? "bakeme:latest" : "bakeme:dev"
   ]
  • 如果 moderelease,則標籤名稱為 bakeme:latest
  • 如果 modedebug,則標籤名稱為 bakeme:dev

最後,更新 Dockerfile 以便在編譯階段使用 BUILD_TAGS 參數。當 -tags="${BUILD_TAGS}" 選項的值為 -tags="debug" 時,編譯器會使用 debug.go 檔案中的 configureLogging 函式。

Dockerfile
 # 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 會自動偵測輸出看起來像檔案路徑,並使用 本地匯出器 將結果匯出到指定的路徑。

target "bin" {
  target = "bin"
  output = ["build/bin"]
  platforms = ["local"]
}

請注意,此階段指定了一個 local 平台。預設情況下,如果未指定 platforms,則建置會以 BuildKit 主機的作業系統和架構為目標。如果您使用的是 Docker Desktop,這通常表示建置目標是 linux/amd64linux/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 格式

接下來,讓我們新增一個目標來建置程式的 semua 平台變體。為此,您可以繼承您剛才建立的 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

若也要產生「發行」和「除錯」變體,您可以像使用預設目標一樣使用矩陣。使用矩陣時,您還需要根據矩陣值區分輸出目錄,否則二進位檔會在每次矩陣執行時寫入相同的位置。

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 的更多資訊,請查看以下資源