Building Multi-Architecture container images on Linux


整理在 Linux 需要編譯與建立多選架構(Multi-Architecture) 的 Docker Image 會遇到的處理與觀念.

docker buildx

上一篇在紀錄 .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 running Multi-arch

Docker 官方的文件裡面有篇關於 Leverage multi-CPU architecture support 這邊有說明了 Docker DesktopDocker 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

How does it work for Docker Desktop and Multi-arch

而上面提到的 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 則能達到分散編譯與建置的好處,除了可以不用虛擬機減少問題、也可以透過實際的主機與分流併發達到明顯的編譯加速

透過 buildx 讓其他


# 查詢當前的建置
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

Reference


作者: Blackie
版權聲明: 本站所有文章除特別聲明外,均採用 CC BY 4.0 許可協議。轉載請註明來源 Blackie !
  目錄