多平台建置
多平台建置是指單一建置呼叫,目標是多個不同的作業系統或 CPU 架構組合。建置映像檔時,這可讓您建立可在多個平台上執行的單一映像檔,例如 linux/amd64
、linux/arm64
和 windows/amd64
。
為何要進行多平台建置?
Docker 解決了「它在我的機器上可以運作」的問題,方法是將應用程式及其相依性打包到容器中。這使得在不同的環境(例如開發、測試和生產)上執行相同的應用程式變得容易。
但容器化本身只解決了部分問題。容器共用主機核心,這表示在容器內執行的程式碼必須與主機的架構相容。這就是為什麼您無法在 arm64 主機上執行 linux/amd64
容器(不使用模擬),或在 Linux 主機上執行 Windows 容器的原因。
多平台建置透過將同一個應用程式的多個變體打包到單一映像檔中來解決這個問題。這讓您可以在不同類型的硬體上執行相同的映像檔,例如執行 x86-64 的開發機器或雲端中基於 ARM 的 Amazon EC2 執行個體,而不需要模擬。
單平台和多平台映像檔的差異
多平台映像檔的結構與單平台映像檔不同。單平台映像檔包含單一資訊清單,指向單一設定和單組圖層。多平台映像檔包含資訊清單清單,指向多個資訊清單,每個資訊清單指向不同的設定和圖層集。
當您將多平台映像檔推送到登錄檔時,登錄檔會儲存資訊清單清單和所有個別的資訊清單。當您提取映像檔時,登錄檔會傳回資訊清單清單,Docker 會根據主機的架構自動選取正確的變體。例如,如果您在基於 ARM 的 Raspberry Pi 上執行多平台映像檔,Docker 會選取 linux/arm64
變體。如果您在 x86-64 筆記型電腦上執行相同的映像檔,Docker 會選取 linux/amd64
變體(如果您使用的是 Linux 容器)。
先決條件
要建置多平台映像檔,您首先需要確保您的 Docker 環境已設定為支援它。您可以透過兩種方式做到這一點
- 您可以從「傳統」映像檔存放區切換到 containerd 映像檔存放區。
- 您可以建立和使用自定義建置器。
Docker 引擎的「傳統」映像檔存放區不支援多平台映像檔。切換到 containerd 映像檔存放區可確保您的 Docker 引擎可以推送、提取和建置多平台映像檔。
建立使用支援多平台的驅動程式的自定義建置器,例如 docker-container
驅動程式,將允許您在不切換到不同映像檔存放區的情況下建置多平台映像檔。但是,您仍然無法將您建置的多平台映像檔載入到您的 Docker 引擎映像檔存放區中。但您可以使用 docker build --push
將它們直接推送到容器登錄檔。
啟用 containerd 映像檔存放區的步驟取決於您使用的是 Docker Desktop 還是 Docker Engine 獨立版本
如果您使用的是 Docker Desktop,請在 Docker Desktop 設定 中啟用 containerd 映像檔存放區。
如果您使用的是 Docker Engine 獨立版本,請使用 守護程式設定檔 啟用 containerd 映像檔存放區。
要建立自定義建置器,請使用 docker buildx create
命令建立使用 docker-container
驅動程式的建置器。
$ docker buildx create \
--name container-builder \
--driver docker-container \
--bootstrap --use
**注意**
使用
docker-container
驅動程式的建置不會自動載入到您的 Docker 引擎映像檔存放區。如需更多資訊,請參閱 建置驅動程式。
如果您使用的是 Docker Engine 獨立版本,並且需要使用模擬建置多平台映像檔,則還需要安裝 QEMU,請參閱 手動安裝 QEMU。
建置多平台映像檔
觸發建置時,請使用 --platform
旗標定義建置輸出的目標平台,例如 linux/amd64
和 linux/arm64
$ docker buildx build --platform linux/amd64,linux/arm64 .
策略
您可以使用三種不同的策略建置多平台映像檔,具體取決於您的使用案例
QEMU
如果您的建置器已經支援,使用 QEMU 在模擬下建置多平台映像檔是最簡單的入門方法。使用模擬不需要更改您的 Dockerfile,BuildKit 會自動偵測可用的模擬架構。
**注意**
使用 QEMU 進行模擬可能比原生建置慢得多,尤其是在計算密集型任務(例如編譯和壓縮或解壓縮)時。
Docker Desktop 預設支援在模擬下執行和建置多平台映像檔。不需要進行任何設定,因為建置器使用 Docker Desktop VM 中內建的 QEMU。
手動安裝 QEMU
如果您使用 Docker Desktop 以外的建置器,例如如果您在 Linux 上使用 Docker Engine 或自定義遠端建置器,則需要安裝 QEMU 並在主機作業系統上註冊可執行檔類型。安裝 QEMU 的先決條件是
- Linux 核心版本 4.8 或更高版本
binfmt-support
版本 2.1.7 或更高版本- QEMU 二進位檔必須靜態編譯並使用
fix_binary
旗標註冊
使用 tonistiigi/binfmt
映像檔來安裝 QEMU 並使用單一指令在主機上註冊可執行檔類型
$ docker run --privileged --rm tonistiigi/binfmt --install all
這將安裝 QEMU 二進位檔,並使用 binfmt_misc
註冊它們,讓 QEMU 能夠執行非原生檔案格式進行模擬。
一旦在主機作業系統上安裝 QEMU 並註冊可執行檔類型後,它們就能在容器內透明地運作。您可以檢查 /proc/sys/fs/binfmt_misc/qemu-*
中的旗標是否包含 F
來驗證您的註冊。
多個原生節點
使用多個原生節點可以更好地支援 QEMU 無法處理的更複雜情況,並且還能提供更好的效能。
您可以使用 --append
旗標將其他節點新增到建置器。
以下指令會從名為 node-amd64
和 node-arm64
的 Docker Context 建立一個多節點建置器。此範例假設您已經新增了這些 Context。
$ docker buildx create --use --name mybuild node-amd64
mybuild
$ docker buildx create --append --name mybuild node-arm64
$ docker buildx build --platform linux/amd64,linux/arm64 .
雖然這種方法比模擬更有優勢,但管理多節點建置器會增加設定和管理建置器叢集的負擔。或者,您可以使用 Docker Build Cloud,這是一項在 Docker 的基礎架構上提供託管多節點建置器的服務。使用 Docker Build Cloud,您可以獲得原生多平台 ARM 和 X86 建置器,而無需承擔維護它們的負擔。使用雲端建置器還能提供其他好處,例如共用的建置快取。
註冊 Docker Build Cloud 後,將建置器新增到您的本機環境並開始建置。
$ docker buildx create --driver cloud <ORG>/<BUILDER_NAME>
cloud-<ORG>-<BUILDER_NAME>
$ docker build \
--builder cloud-<ORG>-<BUILDER_NAME> \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
--tag <IMAGE_NAME> \
--push .
如需更多資訊,請參閱 Docker Build Cloud。
交叉編譯
根據您的專案,如果您使用的程式語言對交叉編譯有良好的支援,您可以利用多階段建置從建置器的原生架構建置目標平台的二進位檔。特殊的建置參數,例如 BUILDPLATFORM
和 TARGETPLATFORM
,會自動提供在您的 Dockerfile 中使用。
在以下範例中,FROM
指令被固定到建置器的原生平台(使用 --platform=$BUILDPLATFORM
選項)以防止啟動模擬。然後,預先定義的 $BUILDPLATFORM
和 $TARGETPLATFORM
建置參數會在 RUN
指令中插入。在這種情況下,這些值只是使用 echo
列印到標準輸出,但这說明了您如何將它們傳遞給編譯器進行交叉編譯。
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log
範例
以下是一些多平台建置的範例
使用模擬的簡單多平台建置
此範例示範如何使用 QEMU 模擬建置一個簡單的多平台映像檔。該映像檔包含一個列印容器架構的檔案。
先決條件
- Docker Desktop 或安裝 QEMU 的 Docker Engine
- 已啟用 containerd 映像檔儲存
步驟
建立一個空目錄並瀏覽到該目錄
$ mkdir multi-platform $ cd multi-platform
建立一個簡單的 Dockerfile 來列印容器的架構
# syntax=docker/dockerfile:1 FROM alpine RUN uname -m > /arch
為
linux/amd64
和linux/arm64
建置映像檔$ docker build --platform linux/amd64,linux/arm64 -t multi-platform .
執行映像檔並列印架構
$ docker run --rm multi-platform cat /arch
- 如果您在 x86-64 機器上執行,您應該會看到
x86_64
。 - 如果您在 ARM 機器上執行,您應該會看到
aarch64
。
- 如果您在 x86-64 機器上執行,您應該會看到
使用 Docker Build Cloud 的多平台 Neovim 建置
此範例示範如何使用 Docker Build Cloud 執行多平台建置,以編譯和匯出 Neovim
步驟
建立一個空目錄並瀏覽到該目錄
$ mkdir docker-build-neovim $ cd docker-build-neovim
建立一個建置 Neovim 的 Dockerfile。
# syntax=docker/dockerfile:1 FROM debian:bookworm AS build WORKDIR /work RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update && apt-get install -y \ build-essential \ cmake \ curl \ gettext \ ninja-build \ unzip ADD https://github.com/neovim/neovim.git#stable . RUN make CMAKE_BUILD_TYPE=RelWithDebInfo FROM scratch COPY --from=build /work/build/bin/nvim /
使用 Docker Build Cloud 為
linux/amd64
和linux/arm64
建置映像檔$ docker build \ --builder <cloud-builder> \ --platform linux/amd64,linux/arm64 \ --output ./bin .
此指令使用雲端建置器建置映像檔,並將二進位檔匯出到
bin
目錄。驗證二進位檔是否為兩個平台建置的。您應該會看到適用於
linux/amd64
和linux/arm64
的nvim
二進位檔。$ tree ./bin ./bin ├── linux_amd64 │ └── nvim └── linux_arm64 └── nvim 3 directories, 2 files
交叉編譯 Go 應用程式
此範例示範如何使用多階段建置交叉編譯 Go 應用程式以適用於多個平台。該應用程式是一個簡單的 HTTP 伺服器,監聽埠 8080 並返回容器的架構。此範例使用 Go,但相同的原則適用於其他支援交叉編譯的程式語言。
Docker 建置的交叉編譯功能是透過利用一系列預先定義的(在 BuildKit 中)建置參數來實現的,這些參數提供有關建置器和建置目標平台的資訊。您可以使用這些預先定義的參數將平台資訊傳遞給編譯器。
在 Go 中,您可以使用 GOOS
和 GOARCH
環境變數來指定要建置的目標平台。
先決條件
- Docker Desktop 或 Docker Engine
步驟
建立一個空目錄並瀏覽到該目錄
$ mkdir go-server $ cd go-server
建立一個建置 Go 應用程式的基本 Dockerfile
# syntax=docker/dockerfile:1 FROM golang:alpine AS build WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . RUN go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
此 Dockerfile 尚無法使用交叉編譯建置多平台。如果您嘗試使用
docker build
建置此 Dockerfile,建置器將嘗試使用模擬來為指定的平台建置映像檔。要新增交叉編譯支援,請更新 Dockerfile 以使用預先定義的
BUILDPLATFORM
和TARGETPLATFORM
建置參數。當您將--platform
旗標與docker build
一起使用時,這些參數會自動在 Dockerfile 中提供。- 使用
--platform=$BUILDPLATFORM
選項將golang
映像檔固定到建置器的平台。 - 為 Go 編譯階段新增
ARG
指令,以便TARGETOS
和TARGETARCH
建置參數可供此階段中的指令使用。 - 將
GOOS
和GOARCH
環境變數設定為TARGETOS
和TARGETARCH
的值。Go 編譯器使用這些變數進行交叉編譯。
# syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM golang:alpine AS build ARG TARGETOS ARG TARGETARCH WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
# syntax=docker/dockerfile:1 FROM golang:alpine AS build WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . RUN go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
# syntax=docker/dockerfile:1 -FROM golang:alpine AS build +FROM --platform=$BUILDPLATFORM golang:alpine AS build +ARG TARGETOS +ARG TARGETARCH WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . -RUN go build -o server . RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
- 使用
為
linux/amd64
和linux/arm64
建置映像檔$ docker build --platform linux/amd64,linux/arm64 -t go-server .
此範例示範了如何使用 Docker 建置交叉編譯 Go 應用程式以適用於多個平台。有關如何進行交叉編譯的具體步驟可能會因您使用的程式語言而異。請參閱您程式語言的文件,以瞭解更多關於為不同平台進行交叉編譯的資訊。
小技巧
您也可以考慮查看 xx - Dockerfile 交叉編譯輔助工具