在 X86_64(amd64) 平臺上的docker支援打包跨平臺的映象(如arm64)

DHclly發表於2024-10-09

在信創,ARM開始崛起的現在,Docker 也從一開始的只支援 x86_64 架構變為支援各種架構了,雖然 Docker 的目的是保證只要 Docker 安裝好,在任意機器上執行都能達到一樣的效果,但是這個的前提是Docker映象的架構和當前伺服器的架構一致,以前都是 x84_64架構自然可以,但現在也有別的架構,因此 一個映象如果只有 x86_64 架構的版本,那麼是無法在
Arm 架構的伺服器上執行的。

和執行同理,打包也是如此,x86_64 的機器只能打包 x86_64 的映象。

但是新版本的 docker (從 server 版本 大於 19.10 開始) 有了一個外掛 buildx , 這個外掛支援從一個平臺構建多個平臺的映象。

下面是常見的幾種方式

Docker Desktop

此軟體是用於開發,測試使用的,很方便,因為新版本預設整合了 buildx ,也整合了多平臺映象模擬執行。、

我安裝的版本

docker version

輸出結果

Client:
 Cloud integration: v1.0.35+desktop.13
 Version:           26.1.1
 API version:       1.45
 Go version:        go1.21.9
 Git commit:        4cf5afa
 Built:             Tue Apr 30 11:46:57 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Desktop
 Engine:
  Version:          26.1.1
  API version:      1.45 (minimum version 1.24)
  Go version:       go1.21.9
  Git commit:       ac2de55
  Built:            Tue Apr 30 11:48:28 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.31
  GitCommit:        e377cd56a71523140ca6ae87e30244719194a521
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

buildx 情況

docker buildx ls

輸出

NAME/NODE       DRIVER/ENDPOINT   STATUS    BUILDKIT   PLATFORMS
default*        docker
 \_ default      \_ default       running   v0.13.2    linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/mips64le, linux/mips64, linux/loong64, linux/ppc64

可以看到,build 支援打包多個架構的映象,如打包 linux/arm64的,只需要如下執行即可

docker build -t test1 --platform linux/arm64 .
docker build --platform linux/amd64,linux/arm64 .

預設情況下,Docker Desktop 支援在模擬下執行和構建多平臺映象。無需配置,因為生成器使用捆綁在 Docker Desktop VM 中的 QEMU。

Docker Server

Docker Desktop 是用於 windows ,mac上開發使用的,實際使用肯定是在 linux 上的,此時裝的都是 Docker Server 服務,如 docker-ce ,由於預設情況下是不支援多平臺的,所以需要參考官方文件:

多平臺 |Docker 文件 --- Multi-platform | Docker Docs(https://docs.docker.com/build/building/multi-platform/)

文件中介紹了三種方式,分別是 QEMU 模擬模擬,使用具有多個原生節點的構建器, 將交叉編譯與多階段構建結合使用,下面別分介紹

QEMU

這個最簡單,不過需要滿足一些條件(不一定是所有條件都需要有效,但是開著準沒錯)

  • 物理機環境變數 DOCKER_BUILDKIT=1,應該會有用
  • 物理機環境變數 DOCKER_CLI_EXPERIMENTAL=enabled,應該會有用
  • 物理機 Linux 核心版本 4.8 或更高版本,即 uname -r 的輸出版本號大於4.8 ,否則會存在各種奇奇怪怪的錯誤。
  • binfmt-support 版本 2.1.7 或更高版本,Ubuntu 可以透過 apt-cache policy binfmt-support 檢視版本
  • QEMU 二進位制檔案必須靜態編譯並使用 fix_binary 標誌進行註冊

一般情況下,你只要核心版本滿足,binfmt-support 啥的也能滿足

QEMU 安裝

官網推薦是透過映象 tonistiigi/binfmt 來操作,常用操作命令如下:

# 檢視支援的架構和模擬器列表
docker run --privileged --rm tonistiigi/binfmt:master

# 安裝所有架構模擬器
docker run --privileged --rm tonistiigi/binfmt:master --install all
# 安裝指定架構的
docker run --privileged --rm tonistiigi/binfmt:master --install arm64,riscv64,arm

# 刪除所有架構
docker run --privileged --rm tonistiigi/binfmt --uninstall qemu-*
# 刪除指定架構
docker run --privileged --rm tonistiigi/binfmt --uninstall qemu-aarch64

# 檢視版本
docker run --privileged --rm tonistiigi/binfmt:master --version

# 檢視幫助
docker run --privileged --rm tonistiigi/binfmt:master --help

驗證命令

docker run --rm arm32v6/alpine:3.20.3 uname -a
docker run --rm arm32v7/alpine:3.20.3 uname -a
docker run --rm arm64v8/alpine:3.20.3 uname -a
docker run --rm s390x/alpine:3.20.3 uname -a
docker run --rm i386/alpine:3.20.3 uname -a
docker run --rm ppc64le/alpine:3.20.3 uname -a
docker run --rm riscv64/alpine:3.20.3 uname -a
docker run --rm amd64/alpine:3.20.3 uname -a
docker run --rm alpine:3.20.3 uname -a

檢視安裝好的註冊節點

ls -alh /proc/sys/fs/binfmt_misc

輸出:

drwxr-xr-x 2 root root 0 Sep 24 13:46 .
dr-xr-xr-x 1 root root 0 Sep 24 13:46 ..
-rw-r--r-- 1 root root 0 Sep 24 13:46 WSLInterop
-rw-r--r-- 1 root root 0 Sep 24 13:46 WSLInterop-late
-rw-r--r-- 1 root root 0 Sep 24 13:46 aarch64
-rw-r--r-- 1 root root 0 Sep 24 13:46 arm
-rw-r--r-- 1 root root 0 Sep 24 13:46 mips64
-rw-r--r-- 1 root root 0 Sep 24 13:46 mips64le
-rw-r--r-- 1 root root 0 Sep 24 13:46 ppc64le
-rw-r--r-- 1 root root 0 Sep 25 23:10 qemu-loongarch64
-rw-r--r-- 1 root root 0 Sep 25 23:10 qemu-s390x
--w------- 1 root root 0 Sep 25 23:07 register
-rw-r--r-- 1 root root 0 Sep 24 13:46 riscv64
-rw-r--r-- 1 root root 0 Sep 24 13:46 status

如果上面的都不行,那麼可能就是存在一些特殊情況了,可以使用 tonistiigi/binfmt 類似的 multiarch/qemu-user-static , 這個也很牛皮,可以在早期的docker 版本上實現跨平臺打包映象,如 docker 19.10 左右的版本,但是需要自己去深入研究一下。

使用命令如下:

$ uname -m
x86_64

$ docker run --rm -t arm64v8/ubuntu uname -m
standard_init_linux.go:211: exec user process caused "exec format error"

$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

$ docker run --rm -t arm64v8/ubuntu uname -m
aarch64

# 檢視幫助說明
docker run --rm --privileged multiarch/qemu-user-static:latest --help
# 安裝除了當前處理器支援的架構之外的模擬器
docker run --rm --privileged multiarch/qemu-user-static:latest
# 刪除現有的模擬器,安裝除了當前處理器支援的架構之外的模擬器
docker run --rm --privileged multiarch/qemu-user-static:latest --reset
# 刪除現有的模擬器,安裝除了當前處理器支援的架構之外的模擬器,並且持久化
docker run --rm --privileged multiarch/qemu-user-static:latest --reset -p yes
# 檢視安裝的模擬器列表
ls -alh /proc/sys/fs/binfmt_misc

使用 QEMU 進行模擬可能比原生構建慢得多,尤其是對於編譯和壓縮或解壓縮等計算密集型任務。

預設情況下,Docker Desktop 支援在模擬下執行和構建多平臺映象。無需配置,因為生成器使用捆綁在 Docker Desktop VM 中的 QEMU。

多個原生節點

需要額外的機器,即有對應的不同平臺的機器叢集打包,不考慮,我有額外的機器了,就不用糾結這個了

交叉編譯

根據您的專案,如果您使用的程式語言對交叉編譯有很好的支援,則可以利用多階段構建從構建器的本機架構為目標平臺構建二進位制檔案。特殊的構建引數(如 BUILDPLATFORMTARGETPLATFORM)會自動在 Dockerfile 中使用。

# 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

Docker in Docker

很多情況都是因為docker所處的核心版本等等不足導致,那麼就可以嘗試使用 Docker in Docker ,套娃的方式打包。效能會低一點,至少可以打包,還沒那麼多問題

GitHub - crazy-max/docker-docker: Docker in Docker (DinD) image(https://github.com/crazy-max/docker-docker#about)

參考連結

  • "sh: write error: Invalid argument" errors when register · Issue #38 · multiarch/qemu-user-static · GitHub(https://github.com/multiarch/qemu-user-static/issues/38)
  • sh: write error: Invalid argument - Centos 7 · Issue #100 · multiarch/qemu-user-static · GitHub(https://github.com/multiarch/qemu-user-static/issues/100)
  • failed to load LLB: runtime execution on platform linux/arm64 not supported · Issue #138 · docker/buildx · GitHub(https://github.com/docker/buildx/issues/138)
  • buildx is not a docker command on linux/amd64 ? · Issue #132 · docker/buildx · GitHub(https://github.com/docker/buildx/issues/132#issuecomment-521759117)
  • GitHub - crazy-max/docker-docker: Docker in Docker (DinD) image(https://github.com/crazy-max/docker-docker#about)
  • sh: write error: Invalid argument - Centos 7 qemu報錯解決方案 - 小小記錄本 - 部落格園(https://www.cnblogs.com/xiaojiluben/p/16745276.html)
  • 多架構 Docker 映象 ·Maartje Eyskens --- Multiarch Docker Images · Maartje Eyskens(https://eyskens.me/multiarch-docker-images/)

相關文章