整理在 Linux 需要編譯與建立多選架構(Multi-Architecture) 的 Docker Image 會遇到的處理與觀念.
上一篇在紀錄 .NET Core 換到 Apple M1
的調整文章 有提到 QEMU
模擬器不兼容的問題與處理方式,而本次又遇到類似的狀況.但這次卻要靠 QEMU
才有辦法編譯與建立多選架構(Multi-Architecture) 的 Docker Image .由於步驟實在有點多且有大量前置資訊需要研讀,這邊還是整理了一篇回顧.
Docker Multi-arch and support
Docker images we create run on the linux/amd64 platform
在使用 Docker 建立印象檔(image) 預設如果不指定會使用 linux/amd64
這個平台作為編譯與上傳的識別.而在現在 Apple M1 逐漸風行的情況下 ARM 的開發或是運行環境會慢慢的變普遍.
而 Docker 在編譯(build) 已經決定要用在哪一個 OS 與 architecture(CPU),也因為這樣所以能進一步的在執行時使用更輕量化的印象檔.
Docker 官方的文件裡面有篇關於 Leverage multi-CPU architecture support 這邊有說明了 Docker Desktop 與 Docker Buildx 的作法.
簡單來說在 Windows, macOS 的 Docker Desktop
環境是有直接內建支援 binfmt_misc
以利 Multi-arch 的支援、無需而外安裝與設定就可以直接透過 manifest 建立. 關於 Docker Desktop 對於 Multi-arch 的支援可以參考 Building Multi-Arch Images for Arm and x86 with Docker Desktop
而上面提到的 manifest 可以將 x86 或 Arm 等不同架構儲存在單個 Container image 內透過 meta 描述的方式說明啟動 Container image 時會自動選擇適合當前合適 CPU 架構的 image 使用(透過不同的
CPU架構找到對應的 image SHA-1)。
但由於 Linux 環境不一定在硬體上都有支援 arm
或是 ppc64le
等環境的編譯與運行,所以這個使用就需要再用一個模擬器(buildx
搭配 qemu
)來模擬 Host OS 中可能不支援的部分做全軟體虛擬化運行(Software Virtualization),這種全軟體虛擬化的好處就是可以透過虛擬機抹平硬體不支援、OS不支援的問題,但效果絕對遠低於硬體直接運行.
Docker 有兩種方法可以建立 multi-arch image:manifest 以及 Buildx
Building Multi-arch with Buildx
如果要啟用 Buildx 需要確認 ~/.docker/config.json
底下的 experimental 是啟用的
{
...
"experimental": “enabled”
}
啟用後就可以用下方指令確認當前 buildx 能夠進行編譯的平台有哪些:
docker buildx ls
# 如果運行在 macOS 上
# NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
# desktop-linux docker
# desktop-linux desktop-linux running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
# default * docker
# default default running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Building Multi-arch with registry and
buildx 搭配 build farm
則能達到分散編譯與建置的好處,除了可以不用虛擬機減少問題、也可以透過實際的主機與分流併發達到明顯的編譯加速
# 查詢當前的建置
docekr context ls
# 使用預設 build farm
docker buildx use default
# 建立新的 buildx 的 build farm 與對應的 driver
docker buildx create --use --name helloworld dotnet-test-linux-x64
# 添加新的機器到 build farm
docker buildx create --append --name helloworld dotnet-test-arm-x64
Alternative - Building Multi-arch with manifest
這算是比較傳統的方式,簡單來講就是編譯與建制每個跨平台的 image 並透過 manifest 的檔案將所有配置的 os + architecture 有一個統一的 tag 的方式去識別並上傳至指定的 registry.
use –platform
建構 multi-arch 一樣透過 docker build 建立印象檔(要使用 docker buildx 執行也行),並搭配 --platform
docker buildx build \
--platform linux/amd64,linux/arm64 \
--push \
-t ${registry url}/multi-arch:latest \
.
--platform
用於指定要建構哪些在哪些 CPU 平台架構,例如 linux/amd64, linux/arm64.(docker buildx inspect
找到支援的 platform)--push
指定後可以將 manifest lists 推送到指定 registry
create manifest list
透過 docker cli 可以建立多個不同架構的 image (SHA-1) 並推至 registry 內存放
docker buildx build --platform linux/arm/v7 -t amouat/multi-test:armv7 .
docker push amouat/arch-test:armv7
docker buildx build -t amouat/arch-test:amd64 .
docker push amouat/arch-test:amd64
而之後就可以建立一個 maifest list 來描述該 tag 的 image 有哪些對應的架構的 SHA-1 image:
docker manifest create ${registry url}/multi-test:latest amouat/arch-test:amd64 amouat/arch-test:armv7
#Created manifest list docker.io/amouat/arch-test:blog
docker manifest push ${registry url}/multi-test:latest
#sha256:039dd768fc0758fbe82e3296d40b45f71fd69768f21bb9e0da02d0fb28c67648
這邊我們是將剛剛的 SHA-1 tag 直接放入並用 multi-test:latest
作為識別
Running Multi-arch
正常使用的情況下,預設 docker run 會依照當前的環境的 CPU 架構去選擇對應的 arch
docker run --rm ${registry url}/multi-arch:latest
而如家需要指定,可以使用 -–platform
選擇啟動的 image arch
$ docker run –rm –platform linux/amd64 ${registry url}/multi-arch:latest
Multi-arch Key Recap
實務上,使用 docker build 多半會用 multi-stage
加速,而選擇上 buildx
> manifest
,其他細節可以參考下面這篇的分享,算是把歷史與觀念講得很清楚的,在影片中其實一次提到 multi-arch 其實不是一個新觀念了,很多語言有這樣的特性很久了(跨平台支援需求).
The key takeaways from the meetup on using buildx:
- Everything should be
multi-platform
- Always use multi-stage Dockerfiles
- buildx is experimental but solid (based on BuildKit)
- Alternatively use docker manifest — also experimental