多平臺容器鏡像構建就看這一篇

前言

願景與現實

早在1995年,就有「write once and run anywhere」(WORA,編寫一次便可在任何地方運行)用於描述 Java 應用程序。時過20年,Docker 高聲喊出了本身的口號——「Build Once, Run Anywhere」(一次構建,隨處可用)。html

願望是美好的,然而,現實總比理想骨感。 Linux、Windows 這些不一樣的操做系統擁有不一樣的系統 API; x8六、Arm、IBM PowerPC 這些不一樣的硬件平臺的指令集不一樣,某些同平臺的硬件甚至擁有不一樣的專用指令集用於加速應用。一次構建,隨處可用面臨着巨大的挑戰,要構建可以在不一樣操做系統、不一樣硬件平臺的運行的應用程序,仍然須要工程師們針對具體的操做系統和硬件平臺進行海量的移植工做。python

Why and How

既然多平臺的支持這麼麻煩、充滿挑戰,咱們是否是能夠放棄支持?然而隨着國產化大潮和 IoT 物聯網的來臨,咱們編寫的應用程序不只僅會在X86服務器上運行,新時代的工程師們不得不面對更多的硬件平臺,放棄多平臺的支持無疑是放棄更寬廣的將來。多平臺的支持,勢在必行。linux

咱們正處在一個波瀾壯闊的大時代中,新技術、新工具在一次次的迭代升級,不斷從Proposal (提議)到 Prototype(原型),再逐漸的實用化。git

虛擬化技術使得咱們能夠作到模擬其它硬件平臺;Docker 等容器技術打破混亂,讓開發、編譯、運行環境一致化;Golang、Rust 這樣原生支持多系統多平臺的編程語言屏蔽大量底層差別,下降跨平臺應用的開發難度。這一系列前人智慧火花匯聚到一塊兒,發生了奇妙的反應——WORA 真正變的觸手可及,夢想的陽光已經照進現實。github

本篇章會大量分析技術原理及實現細節,對於但願快速 GET 可執行方案的同窗,能夠直接跳轉到【可執行方案回顧】查看。golang

如何支持多平臺

要了解容器鏡像是如何支持多平臺的,那咱們須要仔細聊聊 Manifest。使用過容器技術的同窗都知道,咱們運行容器所使用的鏡像是由多層構成的,而這些層的清單和其它容器信息共同存放在 Manifest 當中。web

Manifest

咱們一般使用的容器鏡像是x86平臺的,執行 docker manifest inspect harbor-community.tencentcloudcr.com/multi-arch/alpine 命令能夠查看鏡像 harbor-community.tencentcloudcr.com/multi-arch/alpine 的 Manifest 內容是一個 JSON 對象(如代碼段-01所示)。各個字段的解釋以下:docker

  1. mediaType 字段聲明這是一個V2 Manifes。
  2. schemaVersion 版本。
  3. config 鏡像配置信息。
  4. layers 鏡像層信息。
// 代碼段-01
{
	"schemaVersion": 2,
	"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
	"config": {
		"mediaType": "application/vnd.docker.container.image.v1+json",
		"size": 1507,
		"digest": "sha256:f70734b6a266dcb5f44c383274821207885b549b75c8e119404917a61335981a"
	},
	"layers": [
		{
			"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
			"size": 2813316,
			"digest": "sha256:cbdbe7a5bc2a134ca8ec91be58565ec07d037386d1f1d8385412d224deafca08"
		}
	]
}

顯而易見的是,Manifest 當中並無任何字段描述鏡像的平臺信息。那應該怎麼樣支持多平臺呢?編程

咱們能夠設想一個簡單粗暴的,無視鏡像的平臺,強行把交叉編譯出來的其它平臺的二進制程序添加到鏡像內,使用 Repository 名稱或者 Tag 名稱來區分不一樣平臺的鏡像,例如 coredns/coredns:coredns-arm64。在使用的時候,人工或者經過腳本判斷應該拉取那個鏡像。json

Schema 2

Ohhhhh,這固然能夠跑起來,可是不免太挫了吧?事實上,早在2015年末 Docker 社區的 manifest v2.2 規格文檔(也叫 Schema 2,參見 https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md )中就說起了多平臺鏡像,該功能經過 manifest list(也叫作 fat manifest) 引用多個不一樣平臺鏡像的 Manifest 實現。

首先讓咱們看看 manifest 是什麼樣的,執行 docker manifest inspect alpine 命令能夠查看Docker Hub 上的多平臺鏡像 alpine 的 Manifest。

// 代碼段-02
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:a15790640a6690aa1730c38cf0a440e2aa44aaca9b0e8931a9f2b0d7cc90fd65",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:71465c7d45a086a2181ce33bb47f7eaef5c233eace65704da0c5e5454a79cee5",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v6"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:c929c5ca1d3f793bfdd2c6d6d9210e2530f1184c0f488f514f1bb8080bb1e82b",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v7"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:3b3f647d2d99cac772ed64c4791e5d9b750dd5fe0b25db653ec4976f7b72837c",
         "platform": {
            "architecture": "arm64",
            "os": "linux",
            "variant": "v8"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:90baa0922fe90624b05cb5766fa5da4e337921656c2f8e2b13bd3c052a0baac1",
         "platform": {
            "architecture": "386",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:5d950b30f229f0c53dd7dd7ed6e0e33e89d927b16b8149cc68f59bbe99219cc1",
         "platform": {
            "architecture": "ppc64le",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 528,
         "digest": "sha256:a5426f084c755f4d6c1d1562a2d456aa574a24a61706f6806415627360c06ac0",
         "platform": {
            "architecture": "s390x",
            "os": "linux"
         }
      }
   ]
}

能夠看出來 manifest list 是一個 JSON 數組,數組當中應用了不一樣平臺鏡像的 Manifest。因此,推送多平臺鏡像時,咱們須要先分別推送不一樣平臺的鏡像層;而後建立 manifest list , 再引用平臺鏡像的 Manifest,最後把 manifest list 上傳到 Registry 服務。而拉取鏡像時,客戶端應當設置 HTTP 的請求頭字段 Accept 值爲 application/vnd.docker.distribution.manifest.v2+jsonapplication/vnd.docker.distribution.manifest.list.v2+json,而後檢查服務端返回的響應頭字段Content-Type 判斷是舊鏡像格式,新鏡像格式或者時鏡像清單。

拋開規格文檔來講,只要咱們使用的 Registry 服務的 Distribution 版本不低於 v2.3,Docker CLI 版本不低於 v1.10 就能過支持多平臺鏡像功能。

構建多平臺鏡像

要構建多平臺的容器鏡像,咱們須要確保容器基礎鏡像和應用程序的代碼或者二進制都是目標平臺的。

程序代碼

一些編程語言的編譯器可以爲其它平臺編譯二進制文件,最爲著名的包括 Golang 和 Rust。咱們將使用 Golang 編寫一個演示用 web 程序——經過 HTTP 訪問查看 web 服務程序的操做系統、硬件平臺等信息。具體代碼如代碼段-03 所示。

// 代碼段-03
package main
import (
	"net/http"
	"runtime"
	"github.com/gin-gonic/gin"
)
var (
	r = gin.Default()
)
func main() {
	r.GET("/", indexHandler)
	r.Run(":9090")
}
func indexHandler(c *gin.Context) {
	var osinfo = map[string]string{
		"arch":    runtime.GOARCH,
		"os":      runtime.GOOS,
		"version": runtime.Version(),
	}
	c.JSON(http.StatusOK, osinfo)
}

咱們在 MacOS 上使用 go run 運行代碼段-03, httpie 工具訪問本機:9090 端口,將會看見以下信息。

enter image description here

代碼準備好了,如今咱們有兩種構建方法:手動編譯,使用 docker build 構建鏡像;使用 docker buildx 工具自動化編譯構建。

手動編譯構建

前置條件

  • Dockerd 啓用 experimental

咱們須要在 Docker daemon 配置文件中配置 "experimental": true開啓實驗性功能:

$ vi /etc/docker/daemon.json
{
  "experimental": true
}

修改 Docker daemon 配置須要重啓服務使配置生效:

$ sudo systemctl restart docker.service

使用 docker version 命令查看版本信息,配置生效後能夠看到 Server: Docker Engine 中有 Experimental: true

$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:45:36 2020
 OS/Arch:           linux/amd64
 Experimental:      false
Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:44:07 2020
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
`

若是您使用的 Docker CLI 版本低於 v20.10 ,執行 docker manifest 命令會看到報錯提示 docker manifest is only supported on a Docker cli with experimental cli features enabled ,此時咱們須要執行 export DOCKER_CLI_EXPERIMENTAL="enabled" 開啓客戶端實驗特性支持。在 v20.10 及以上版本的 Docker CLI 會默認開啓實驗特性,無需額外操做。

交叉編譯

在咱們的 Golang 代碼中沒有使用 CGO 的時候,經過簡單設置環境變量就可以交叉編譯出其它平臺和操做系統上可以執行的二進制文件。其中:

  1. GOARCH 用於指定編譯的目標平臺,如 amd64arm64riscv64 等平臺。
  2. GOOS 用於指定編譯的目標系統,如 darwinlinux

本篇中,咱們構建可以在 Linux 發行版中執行的容器鏡像,因此編譯目標系統環境變量GOOS統一設置爲linux。執行代碼段0-4中的命令構建出二進制文件備用。

// 代碼段-04
#!/bin/bash
IMAGE?=kofj/multi-demo
NOCOLOR:='\033[0m'
RED:='\033[0;31m'
GREEN:='\033[0;32m'
BUILD_ARCH?=$(uname -m)
BUILD_OS?=$(uname -s)
BUILD_PATH:=build/docker/linux
LINUX_ARCH?=amd64 arm64 riscv64
LDFLAGS:="-s -w -X github.com/kofj/multi-arch-demo/cmd/info.BuildArch=$(BUILD_ARCH) -X github.com/kofj/multi-arch-demo/cmd/info.BuildOS=$(BUILD_OS)"
for arch in ${LINUX_ARCH}; do
	echo ===================;
	echo ${GREEN}Build binary for ${RED}linux/$$arch${NOCOLOR};
	echo ===================;
	GOARCH=$$arch GOOS=linux go build -o ${BUILD_PATH}/$$arch/webapp -ldflags=${LDFLAGS} -v cmd/main.go;
done

構建各個平臺的鏡像

首先,咱們編寫一個 Dockerfile用於構建鏡像。

FROM scratch
LABEL authors="Fanjian Kong"
ADD webapp /app/
WORKDIR /app
CMD ["/app/webapp"]

而後,分別構建不一樣平臺的鏡像,可使用如代碼段-05的腳本幫助構建。

// 代碼段-05
#!/bin/bash
IMAGE?=kofj/multi-demo
NOCOLOR:='\033[0m'
RED:='\033[0;31m'
GREEN:='\033[0;32m'
LINUX_ARCH?=amd64 arm64 riscv64
BUILD_PATH:=build/docker/linux
for arch in ${LINUX_ARCH}; do
	echo =================== ;
	echo ${GREEN}Build docker image for ${RED}linux/$$arch${NOCOLOR} ;
	echo =================== ;
	cp Dockerfile.slim ${BUILD_PATH}/$$arch/Dockerfile ;
	docker build -t ${IMAGE}:$$arch ${BUILD_PATH}/$$arch ;
done

建立 Manifest List

咱們使用docker manifest 子命令管理 manifest list。其中,docker manifest create 子命令用於在本地建立一個 manifest list。該命令須要指定待 manifest list 地址和一系列的 manifests。例如須要建立包含amd64arm64 兩個平臺鏡像的 manifest list,則命令如:

docker manifest create kofj/multi-demo kofj/multi-demo:amd64 kofj/multi-demo:arm64

docker manifest create 命令的詳細幫助信息以下所示:

# docker manifest create --help
Usage:	docker manifest create MANIFEST_LIST MANIFEST [MANIFEST...]
Create a local manifest list for annotating and pushing to a registry
Options:
  -a, --amend      Amend an existing manifest list
      --insecure   Allow communication with an insecure registry

咱們按照上述方法建立出來的 manifest list 中並無說明其中的 manifest 是什麼操做系統和平臺的,docker manifest annotate 命令用於註釋建立出來的 manifest list。例如註釋某個 manifest 是 linxu系統 arm64 平臺的,則命令:

docker manifest annotate kofj/multi-demo kofj/multi-demo:arm64 --os linux --arch arm64

docker manifest annotate 命令的詳細幫助信息以下所示:

# docker manifest annotate --help
Usage:	docker manifest annotate [OPTIONS] MANIFEST_LIST MANIFEST
Add additional information to a local image manifest
Options:
      --arch string           Set architecture
      --os string             Set operating system
      --os-features strings   Set operating system feature
      --variant string        Set architecture variant

注意:

  1. 建立 manifest list 清單的過程當中會檢查遠端倉庫中 manifests 是否存在,因此咱們必修提早推送鏡像到遠端。若是遠端倉庫是不安全的,在建立的過程當中須要添加參數 --inseure
  2. 使用 docker manifest annotate 註釋 manifest list 的時候不須要使用--insecure

爲了方便使用,咱們可使用下述代碼段-06 的腳本建立 manifest list。

// 代碼段-06
#!/bin/bash
IMAGE?=kofj/multi-demo
NOCOLOR:='\033[0m'
RED:='\033[0;31m'
GREEN:='\033[0;32m'
LINUX_ARCH?=amd64 arm64 riscv64
IMAGES=$(foreach arch,$(LINUX_ARCH),$(IMAGE):$(arch))
@echo ${GREEN}Create manifest for ${RED} \( ${LINUX_ARCH}\) ${NOCOLOR};
@docker manifest create ${IMAGE} ${IMAGES}
@for arch in ${LINUX_ARCH}; do \
	echo ${GREEN}Annotate manifest ${RED}linux/$$arch${NOCOLOR}; \
	echo ===================; \
	docker manifest annotate ${IMAGE} ${IMAGE}:$$arch --os linux --arch $$arch; \
done

推送 manifest list

當咱們完成 manifest list 的建立工做後,它仍是存儲在本地的。這時候,還須要推送到遠端的鏡像倉庫。與推送普通鏡像不一樣,推送 manifest list 須要使用 docker manifest push 命令進行。若是咱們要推送 kofj/multi-demo 這個 manifest list,則命令如:

docker manifest push kofj/multi-demo

使用 docker manifest push 命令能夠經過附加--purge 選項在推送完成後刪除存儲在本地的 manifest list; 當咱們的目標倉庫沒有使用或者使用了非可信 TLS 證書的時候,則須要使用 --insecure 選項。

buildx 自動構建

軟件依賴

  • Docker >= 19.03: 自該 Docker 版本包含 buildx。
  • Linux kernel >= 4.8: 自該Linux內核版本 binfmt_misc 支持 fix-binary (F) flag。fix_binary 標誌容許內核在容器或chroot內使用binfmt_misc註冊的二進制格式處理程序,即便該處理程序二進制文件不是該容器或chroot內可見的文件系統的一部分。
  • binfmt_misc file system mounted: 須要掛載binfmt_misc文件系統,以便用戶空間工具能夠控制此內核功能,即註冊和啓用處理程序。
  • Docker Desktop >= 2.1.0 若是是使用的 Docker Desktop。
Environment Docker 安裝包 Kernel binfmt-support (F) Flag
需求 >= 19.03 >= 4.8 >= 2.1.7 yes
Ubuntu:
18.04 (bionic) 17.12.1 docker.io 4.15.0 2.1.8 yes
19.04 (disco) 18.09.5 docker.io 5.0 2.2.0 yes
19.10 (eoan) 19.03.2 docker.io 5.3 2.2.0 yes
20.04 (focal) 19.03.2 docker.io 5.5 2.2.0 yes
Debian:
9 (stretch) - 4.9.0 2.1.6 no
10 (buster) 18.09.1 docker.io 4.19.0 2.2.0 yes
11 (bullseye/testing) 19.03.4 docker.io 5.4 2.2.0 yes
騰訊雲
Ubuntu 16.04 (xenial) 18.09.7 docker.io 4.4.0 2.1.6-1 no
Ubuntu 18.04 (bionic) 19.03.6 docker.io 4.15.0 2.1.8-2 yes
亞馬遜 EC2:
Ubuntu 16.04 (xenial) 18.09.7 docker.io 4.4.0 2.1.6 no
Ubuntu 18.04 (bionic) 18.09.7 docker.io 4.15.0 2.1.8 yes
Travis (谷歌 GCP):
Ubuntu 14.04 (trusty) 17.09.0 docker-ce 4.4.0 2.1.4 no
Ubuntu 16.04 (xenial) 18.06.0 docker-ce 4.15.0 2.1.6 no
Ubuntu 18.04 (bionic) 18.06.0 docker-ce 4.15.0 2.1.8 yes
Github Actions (微軟 Azure):
Ubuntu 16.04 (xenial) 3.0.8 moby-engine 4.15.0 2.1.6 no
Ubuntu 18.04 (bionic) 3.0.8 moby-engine 5.0.0 2.1.8 yes

配置 Buildx

buildx 從 19.03 開始與 Docker CE 捆綁發佈,可是須要咱們在 Docker CLI 上啓用實驗性功能開開啓。能夠經過兩種方式啓用它:

  1. "experimental": "enabled」 添加到 Docker CLI 的配置文件 ~/.docker/config.json
  2. 另一種方法時設置環境變量 DOCKER_CLI_EXPERIMENTAL=enabled

使用 Docker Desktop 的同窗能夠經過 UI 菜單 PreferencesCommand Line 進入 Docker CLI 配置界面,經過Switch 開關 Enable experimental features啓用實驗性功能。

image-20201006100313617.pngenter image description here

若是須要使用最新版本的 buildx,能夠從 https://github.com/docker/buildx/releases/latest 下載最新的二進制發行版,並將其複製到~/.docker/cli-plugins文件夾中,重命名爲docker-buildx,而後更改執行權限:

chmod +x ~/.docker/cli-plugins/docker-buildx

最後讓咱們驗證 buildx 是否已經可用了:

$ docker buildx version
github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7

配置 binfmt_misc

QEMU 是一個很棒的開源項目,它能夠模擬不少平臺。將 QEMU 和 Docker 結合起來使用能使得咱們更容易的構建跨平臺的容器鏡像。集成 QEMU依賴於 Linux 內核功能 。Linux 內核中的 binfmt_misc功能可使得內核識別任意類型的能夠執行文件格式,並傳遞到特定的用戶空間應用程序和虛擬機(https://zh.wikipedia.org/wiki/Binfmt_misc)。當 Linux 遇到一種沒法識別的可執行文件格式(好比說其它平臺的可執行文件格式)時,它會檢查有沒有配置任何「用戶空間應用程序」用於處理它。若是檢測到了,就將可執行文件傳遞給該應用程序。

爲此,咱們須要在內核當中註冊其它平臺的可執行文件格式。

對於使用 Docker Desktop(MacOS 和 Windows 上都是)的同窗,由於默認配置了 binfmt_misc,能夠跳過這一步。而使用 Linux 發行版操做系統的同窗則須要自行安裝配置 binfmt_misc,以便可以非原生的其它平臺的鏡像。

要在宿主機上執行其它 CPU 平臺的指令,須要安裝 QEMU 模擬器。由於程序執行時會在當前程序可見的文件系統中查找動態庫,而在容器或chroot環境中註冊的處理程序在其它的 cgroup namespace 中可能沒法找到,因此須要靜態編譯鏈接的QEMU。同時,咱們須要安裝一個包含足夠新的update-binfmts二進制文件的包,以便可以支持fix-binary(F)標誌,並在註冊QEMU模擬器時實際使用,這樣才能結合 buildx 一塊兒鏡像跨平臺構建。

QEMU 和 binfmt_misc 支持工具能夠經過宿主機或者Docker 容器鏡像安裝。可是,使用Docker鏡像安裝配置能讓事情變得更加簡單。鏡像 docker/binfmt 中包含QEMU二進制文件和在binfmt_misc中註冊QEMU的安裝腳本。

docker run --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d

執行完後,咱們驗證下是否註冊成功了。成功註冊後,/proc/sys/fs/binfmt_misc 目錄中會有多個qemu-前綴的文件。查看 /proc/sys/fs/binfmt_misc/qemu-aarch64 文件內容,能夠看到 falgs 標誌爲 OCF,說明這個處理程序是經過 (F)標誌註冊的,可以正常的結合 buildx 完成跨平臺構建。

⚡ root@kofj-hk ~ ls -al /proc/sys/fs/binfmt_misc
total 0
drwxr-xr-x 2 root root 0 Oct 12 20:19 .
dr-xr-xr-x 1 root root 0 Oct 12 20:19 ..
-rw-r--r-- 1 root root 0 Oct 12 20:19 python2.7
-rw-r--r-- 1 root root 0 Oct 12 20:19 python3.6
-rw-r--r-- 1 root root 0 Oct 12 20:21 qemu-aarch64
-rw-r--r-- 1 root root 0 Oct 12 20:21 qemu-arm
-rw-r--r-- 1 root root 0 Oct 12 20:21 qemu-ppc64le
-rw-r--r-- 1 root root 0 Oct 12 20:21 qemu-s390x
--w------- 1 root root 0 Oct 12 20:19 register
-rw-r--r-- 1 root root 0 Oct 12 20:19 status
 ⚡ root@kofj-hk ~ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b7
mask ffffffffffffff00fffffffffffffffffeffff

使用 buildx 構建

前置依賴注備好後,咱們終於可使用 buildx 構建多平臺鏡像了。與其它方案不一樣的是,使用 buildx 可讓咱們沒必要改動 dockerfile。

Buildx 始終使用 BuildKit 引擎構建鏡像,不須要配置環境變量DOCKER_BUILDKIT=1。BuildKit 能夠很好的用於多個平臺的構建,而不只適用於咱們當前構建鏡像時所使用的平臺和操做系統。進行構建時,使用 --platform標誌能夠用於指定構建輸出的目標平臺(例如 linux/amd64linux/arm64,linux/riscv64)。

首先,咱們先準備好 Dockerfile 文件:

FROM golang:1.14 as builder
COPY . /src
WORKDIR /src
RUN ls -al && go build -a -tags netgo -ldflags '-w' -mod=vendor -v -o /src/bin/webapp /src/cmd/main.go
# Final image
FROM ubuntu:18.04
LABEL authors="Fanjian Kong"
COPY --from=builder /src/bin/webapp /app/
WORKDIR /app
CMD ["/app/webapp"]

而後,讓咱們嘗試下運行 buildx。

✘ ⚡ root@kofj-hk ~ docker buildx build --platform linux/amd64,linux/arm64,linux/arm -t harbor-community.tencentcloudcr.com/multi-arch/demo:2020-10-12 . --push
error: auto-push is currently not implemented for docker driver, please create a new builder instance

竟然報錯 error: auto-push is currently not implemented for docker driver, please create a new builder instance 了!別擔憂,這是由於 Docker 默認的 builder 是不支持多平臺構建的。咱們能夠經過 docker buildx ls 查看當前節點上的 builder 有哪些。

⚡ root@kofj-hk ~ docker buildx  ls
NAME/NODE DRIVER/ENDPOINT STATUS  PLATFORMS
default * docker
  default default         running linux/amd64, linux/386

爲了使用多平臺構建功能,咱們須要新建一個 builder,並設置當前 builder 爲新建的。

# 新建同時切換 builder 
docker buildx create --use --name mybuilder
# 只新建,而後再切換 builder
docker buildx create --name mybuilder
docker buildx use mybuilder

如今,讓咱們再次執行 buildx,看着一切向着期待的方向發展了。

注意事項

⚠️注意1:到目前位置,buildx支持 linux/amd64, linux/386, linux/arm/v7, linux/arm/v6, linux/arm64, linux/ppc64le, linux/s390x。因此 docker/binfmt 鏡像僅註冊了 arm、ppc64le 和 s390x 的處理程序。若是你須要構建、運行 RISC-V 平臺的容器鏡像,建議使用 multiarch/qemu-user-static 鏡像鏡像配置。

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

⚠️注意2:在 軟件依賴 中咱們提到須要 Linux 內核版本 >= 4.8.0;若是在內核版本爲 3.10.0 的系統(好比 CentOS)上運行 docker/binfmt,會出現報錯 Cannot write to /proc/sys/fs/binfmt_misc/register: write /proc/sys/fs/binfmt_misc/register: invalid argument,這是因爲內核不支持 (F)標誌形成的。出現這種狀況,建議您升級系統內核或者換使用較高版本內核的 Linux 發行版。

enter image description here

小結

多年前,大規模部署應用程序是一項很是耗費人力、財力、時間,還須要大量技能和技巧的事務,工程師們還須要應對應用程序所運行的每一臺服務器的環境差別。這對大公司而言是個極其沉重的負擔,小公司更是無力應對。

正如多年前人們沒法想象大規模部署複雜的應用程序只須要一個 kubectl create 命令,不久前咱們也不會想到構建多平臺的容器鏡像只須要一個 docker buildx build。可是,咱們還有更加廣闊的想象空間,自動化流程、更多平臺的支持、更智能簡單的工具,你能想到的都有可能在不久的未來變成現實。

技術的發展進步,不斷下降了生產活動中社會平均勞動時間,提高了生產力,可以釋放勞動者去作更多有益的探索。讓咱們不斷學習、擁抱、應用新技術,在時代的浪潮中一往無前。

可執行方案回顧

  1. 確保使用的 Linux 發行版內核>=4.8.0(推薦使用 Ubuntu 18.04 以上的 TLS 發行版),且 Docker >= 19.03
  2. 啓用Docker CLI 實驗性功能: export DOCKER_CLI_EXPERIMENTAL=enabled
  3. 配置其它平臺的模擬器:docker run --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d
  4. 新建 Docker builder 實例支持多平臺構建: docker buildx create --use --name mybuilder
  5. 在項目目錄中執行構建: docker buildx build --platform linux/amd64,linux/arm64,linux/arm -t harbor-community.tencentcloudcr.com/${YOUR_NAMESPACE}/multi-arch:2020-10-12 . --push
  6. 相關演示代碼、腳本能夠在 https://github.com/kofj/multi-arch-demo.git 獲取。

推廣時間

目前,騰訊雲容器鏡像服務 TCR已完成公測進入商業化階段。咱們也已經對部分用戶開放了 Multi Arch 鏡像和 OCI 雲原生製品支持。若是您對該功能感興趣,歡迎聯繫客服開通。
mult-arch-tcr-console

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!

相關文章
相關標籤/搜索