iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Kubernetes

從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台系列 第 16

[Day 16] CI/CD 自動化 (二):多架構 (Multi-Arch) Docker Image 構建 —— 支援 ARM 與 AMD64,讓 Image 在各種節點都能跑。

  • 分享至 

  • xImage
  •  

Day 16: CI/CD 自動化 (二):多架構 (Multi-Arch) Docker Image 構建

支援 ARM 與 AMD64,讓 Image 在各種節點都能跑。

1. 為什麼需要多架構鏡像?

過去大家都用 Intel/AMD (x86_64),日子很單純。但現在 Apple Silicon (M 系列)、AWS Graviton、Oracle 的 Ampere A1 這些 ARM 機器越來越普及。如果你在 Mac (ARM) 上打包的 Image 直接丟到 x86 的雲端節點跑,K8S 會無情地賞你一個:

exec format error

程式沒錯、YAML 也沒錯,錯的是 CPU 架構對不上

這件事其實有點反直覺。Docker 一直被講成「Build once, run anywhere」,久了就會以為它是那種打包好就到處都能跑的萬用膠囊。但它沒那麼神——容器共用的是宿主機的 kernel,它沒有模擬一台電腦給你

說穿了,Docker 解決的是「環境」的問題:少裝哪個 library、Python 版本對不上、同事機器上有你沒有的環境變數。這些它處理得很好。但它從來沒有處理過「這顆 CPU 看不看得懂這串指令」。

image 裡面裝的是編譯好的執行檔,而 x86_64 跟 arm64 的機器碼根本是兩種語言。你在 M 系列 Mac 上 build 出來的 binary 帶的是 ARM 指令集,丟到 x86 節點上,kernel 讀 ELF header 一看架構對不上,連載入都懶得載入,直接回你 exec format error。它不是跑到一半壞掉,是根本沒開始跑

拿 Java 來對照就很清楚:.jar 為什麼可以到處丟?因為中間有 JVM 幫你把 bytecode 翻譯成當下那顆 CPU 的指令。Docker 沒有這一層,它就是把原生執行檔連同環境一起打包——所以「這包是給 ARM 跑的」這個屬性,也一起被忠實地打包進去了。

以前幾乎不會遇到這個坑,是因為大家清一色 x86,build 的機器跟跑的機器架構天生一樣,你根本不會意識到有這回事。Apple Silicon 普及之後才開始翻車。而且 Docker Desktop 還會偷偷用 QEMU 幫你模擬,所以在本機拉別人的 x86 image「好像也能跑」——這個貼心反而讓你更晚發現問題,一路順到 CI 或 production 才爆。

2. buildx 實測:一份 Dockerfile,兩條產線

Docker 的 buildx 搭配 QEMU 模擬,可以在一台機器上同時產出多個架構的 image。直接來實測,把我們的 API Gateway 同時建給 amd64 跟 arm64:

https://ithelp.ithome.com.tw/upload/images/20260818/20182549pfODbTTEu2.png

▲ docker buildx 多架構建置:amd64 與 arm64 兩條產線平行開工 + python:3.10-slim 的八架構 manifest list

注意看 build log:[linux/amd64 builder ...][linux/arm64 builder ...] 兩組 stage 平行跑,其中 arm64 那條靠 QEMU 模擬執行。跑完後的產物不是一個 image,而是一個 Manifest List——同一個 tag 底下掛著多個架構的 image,拉取時 Docker 會自動挑符合你 CPU 的那個。

截圖下半部是官方 python:3.10-slim 的 manifest list——人家一個 tag 底下藏了八種架構,從 amd64 到 s390x 都有。以後 docker pull 都不用想架構的事,就是這個機制在默默工作。

3. 在 GitHub Actions 啟用多架構

CI 上要做多架構,多加兩個零件:setup-qemu-action(裝模擬器)和 platforms 參數:

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build and Push
        uses: docker/build-push-action@v5
        with:
          context: ${{ matrix.context }}
          push: true
          platforms: linux/amd64,linux/arm64   # ← 關鍵就這行
          tags: ghcr.io/${{ env.OWNER_LC }}/${{ matrix.service }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

這裡要注意的是:QEMU 模擬不是免費的。純 JS 的 api-gateway 跑 arm64 很快(npm 只是解壓套件),但 Java 的 mvn package 在模擬環境下會慢好幾倍。目前本機 Docker Desktop 開發只用得到 amd64arm64 這條線暫時還沒有實際跑它的節點——Day 9 提過,Always Free 的 Ampere A1(ARM64)本來是雲端部署的備案,但帳號拿不到 OKE、運算容量又連續缺貨,這條路線目前擱置了。platforms 這行先留著:不管日後是升級帳號拿到 OKE(多半是 x86_64),還是真的用上 Ampere A1,兩條產線已經先備好,不用等到真正需要才回頭補。

4. GHCR (GitHub Container Registry)

這裡補充一下,我們的鏡像不是推 Docker Hub 而是 ghcr.io。原因很實際:Docker Hub 有嚴格的 Rate Limit,OCI 的免費 Registry 又有試用期結束被鎖的問題(我們真的被鎖過,站台直接躺平,哭啊)。GHCR 跟 GitHub Actions 無縫整合,權限跟著 repo 走。

一個必踩的坑先幫大家踩:新 Package 首次推上 GHCR 預設是 Private,記得去 Package Settings 把 Visibility 改成 Public,不然叢集拉取時會一直 ImagePullBackOff,你會在叢集裡查半天最後發現是 GitHub 設定問題(別問我怎麼知道的)。

5. 小結

Image 的生產線已經工業化:push 一次,六個服務、(未來)兩種架構、雙 tag 自動出貨。但「部署」這件事還是要人手動嗎?明天迎來本系列的主角之一——GitOps 與 ArgoCD。


上一篇
[Day 15] CI/CD 自動化 (一):GitHub Actions 基礎流程與測試自動化 —— 建立代碼推送後的第一道防線,確保品質不退化。
下一篇
[Day 17] GitOps 降臨:為什麼我們需要 ArgoCD? —— 從「推動式」轉向「聲明式」,順便看清它跟 CI 的分工。
系列文
從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言