跨平臺

在本指南的這一部分之前,您一直在構建 Linux 二進位制檔案。本節介紹如何使用跨平臺構建(透過模擬和交叉編譯)支援其他作業系統和架構。

使用模擬開始跨平臺構建的最簡單方法是。使用模擬,您可以構建應用程式到多個架構,而無需對 Dockerfile 進行任何更改。您只需要將 --platform 標誌傳遞給構建命令,並指定您要構建到的作業系統和架構。

以下命令為 linux/arm/v7 平臺構建伺服器映象

$ docker build --target=server --platform=linux/arm/v7 .

您還可以使用模擬一次生成多個平臺的輸出。但是,Docker Engine 中的預設映象儲存不支援構建和載入跨平臺映象。您需要啟用 containerd 映象儲存,它支援併發跨平臺構建。

啟用 containerd 映象儲存


要在 Docker Desktop 中啟用 containerd 映象儲存,請轉到 **設定**,然後在 **常規** 選項卡中選擇 **使用 containerd 拉取和儲存映象**。

請注意,更改映象儲存意味著您將暫時無法訪問經典映象儲存中的映象和容器。這些資源仍然存在,但要檢視它們,您需要停用 containerd 映象儲存。

如果您沒有使用 Docker Desktop,請透過將以下功能配置新增到您的 /etc/docker/daemon.json 配置檔案中來啟用 containerd 映象儲存。

{
  "features": {
    "containerd-snapshotter": true
  }
}

更新配置檔案後重新啟動守護程序。

$ systemctl restart docker

使用模擬構建

要執行跨平臺構建,請呼叫 docker build 命令,並傳遞與之前相同的引數。只是這一次,還要新增一個 --platform 標誌,指定多個架構。

$ docker build \
    --target=binaries \
    --output=bin \
    --platform=linux/amd64,linux/arm64,linux/arm/v7 .

此命令使用模擬三次執行相同的構建,每個平臺一次。構建結果被匯出到 bin 目錄。

bin
├── linux_amd64
│   ├── client
│   └── server
├── linux_arm64
│   ├── client
│   └── server
└── linux_arm_v7
    ├── client
    └── server

當您同時為多個平臺構建時,BuildKit 在模擬下為每個指定的平臺執行所有構建步驟。有效地將構建分叉到多個併發程序中。

Build pipelines using emulation

但是,使用模擬執行跨平臺構建有一些缺點

  • 如果您嘗試執行上面的命令,您可能已經注意到它需要很長時間才能完成。對於 CPU 密集型任務,模擬可能會比原生執行慢得多。
  • 模擬僅在您使用的基礎映象支援的架構時才有效。本指南中的示例使用 golang 映象的 Alpine Linux 版本,這意味著您只能以此方式構建 Linux 映象,用於有限的 CPU 架構集,而無需更改基礎映象。

作為模擬的替代方案,下一步將探討交叉編譯。交叉編譯使跨平臺構建更快、更靈活。

使用交叉編譯構建

使用交叉編譯意味著利用編譯器的功能為多個平臺構建,而無需模擬。

您需要做的第一件事是將構建器固定到使用節點的原生架構作為構建平臺。這樣做是為了防止模擬。然後,從節點的原生架構開始,構建器將應用程式交叉編譯到許多其他目標平臺。

平臺構建引數

此方法涉及使用幾個預定義的構建引數,您可以在 Docker 構建中使用它們:BUILDPLATFORMTARGETPLATFORM(以及衍生引數,如 TARGETOS)。這些構建引數反映您傳遞給 --platform 標誌的值。

例如,如果您使用 --platform=linux/amd64 呼叫構建,則構建引數將解析為

  • TARGETPLATFORM=linux/amd64
  • TARGETOS=linux
  • TARGETARCH=amd64

當您將多個值傳遞給平臺標誌時,使用預定義平臺引數的構建階段會自動為每個平臺分叉。這與在模擬下執行的構建形成對比,在模擬下,整個構建管道會針對每個平臺執行。

Build pipelines using cross-compilation

更新 Dockerfile

要使用交叉編譯技術構建應用程式,請更新 Dockerfile,如下所示

  • --platform=$BUILDPLATFORM 新增到初始 base 階段的 FROM 指令中,將 golang 映象的平臺固定到與主機機器的架構匹配。
  • 為 Go 編譯階段新增 ARG 指令,使 TARGETOSTARGETARCH 構建引數可用於此階段中的命令。
  • GOOSGOARCH 環境變數設定為 TARGETOSTARGETARCH 的值。Go 編譯器使用這些變數進行交叉編譯。
  # syntax=docker/dockerfile:1
  ARG GO_VERSION=1.21
  ARG GOLANGCI_LINT_VERSION=v1.59
- FROM golang:${GO_VERSION}-alpine AS base
+ FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base
  WORKDIR /src
  RUN --mount=type=cache,target=/go/pkg/mod \
      --mount=type=bind,source=go.mod,target=go.mod \
      --mount=type=bind,source=go.sum,target=go.sum \
      go mod download -x

  FROM base AS build-client
+ ARG TARGETOS
+ ARG TARGETARCH
  RUN --mount=type=cache,target=/go/pkg/mod \
      --mount=type=bind,target=. \
-     go build -o /bin/client ./cmd/client
+     GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /bin/client ./cmd/client

  FROM base AS build-server
+ ARG TARGETOS
+ ARG TARGETARCH
  RUN --mount=type=cache,target=/go/pkg/mod \
      --mount=type=bind,target=. \
-     go build -o /bin/server ./cmd/server
+     GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /bin/server ./cmd/server

  FROM scratch AS client
  COPY --from=build-client /bin/client /bin/
  ENTRYPOINT [ "/bin/client" ]

  FROM scratch AS server
  COPY --from=build-server /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

  FROM scratch AS binaries
  COPY --from=build-client /bin/client /
  COPY --from=build-server /bin/server /

  FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION} as lint
  WORKDIR /test
  RUN --mount=type=bind,target=. \
      golangci-lint run

現在剩下要做的就是執行實際的構建。要執行跨平臺構建,請設定 --platform 選項,並指定要構建到的作業系統和架構的 CSV 字串。以下命令說明了如何為 Mac(ARM64)、Windows 和 Linux 構建和匯出二進位制檔案

$ docker build \
  --target=binaries \
  --output=bin \
  --platform=darwin/arm64,windows/amd64,linux/amd64 .

構建完成後,您將在 bin 目錄中找到所有選定平臺的客戶端和伺服器二進位制檔案

bin
├── darwin_arm64
│   ├── client
│   └── server
├── linux_amd64
│   ├── client
│   └── server
└── windows_amd64
    ├── client
    └── server

總結

本節演示瞭如何使用模擬和交叉編譯開始跨平臺構建。

相關資訊

您可能還想考慮檢視 xx - Dockerfile 交叉編譯助手xx 是一個 Docker 映象,其中包含使 Docker 構建交叉編譯更容易的實用程式指令碼。

下一步

本節是使用 Docker 構建指南的最後一部分。下一頁包含一些關於下一步去哪裡的提示。