騰訊云多平臺(tái)容器鏡像構(gòu)建(上)

來源: 騰訊云原生
作者:孔礬建
時(shí)間:2020-12-29
17339
本篇章會(huì)大量分析技術(shù)原理及實(shí)現(xiàn)細(xì)節(jié),對(duì)于希望快速GET可執(zhí)行方案的同學(xué),可以直接跳轉(zhuǎn)到最后部分【可執(zhí)行方案回顧】查看。

NDQxYmRiNi5qcGVn.jpg

前言

愿景與現(xiàn)實(shí)

早在1995年,就有“write once and run anywhere”(WORA,編寫一次即可在任何地方運(yùn)行)用于描述Java應(yīng)用程序。時(shí)過20年,Docker高聲喊出了自己的口號(hào)——“Build Once,Run Anywhere”(一次構(gòu)建,隨處可用)。

愿望是美好的,然而,現(xiàn)實(shí)總比理想骨感。Linux、Windows這些不同的操作系統(tǒng)擁有不同的系統(tǒng)API;x86、Arm、IBM PowerPC這些不同的硬件平臺(tái)的指令集不同,某些同平臺(tái)的硬件甚至擁有不同的專用指令集用于加速應(yīng)用。一次構(gòu)建,隨處可用面臨著巨大的挑戰(zhàn),要構(gòu)建能夠在不同操作系統(tǒng)、不同硬件平臺(tái)的運(yùn)行的應(yīng)用程序,仍然需要工程師們針對(duì)具體的操作系統(tǒng)和硬件平臺(tái)進(jìn)行海量的移植工作。

Why and How

既然多平臺(tái)的支持這么麻煩、充滿挑戰(zhàn),我們是不是可以放棄支持?然而隨著國產(chǎn)化大潮和IoT物聯(lián)網(wǎng)的來臨,我們編寫的應(yīng)用程序不僅僅會(huì)在X86服務(wù)器上運(yùn)行,新時(shí)代的工程師們不得不面對(duì)更多的硬件平臺(tái),放棄多平臺(tái)的支持無疑是放棄更寬廣的未來。多平臺(tái)的支持,勢(shì)在必行。

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

虛擬化技術(shù)使得我們可以做到模擬其它硬件平臺(tái);Docker等容器技術(shù)打破混亂,讓開發(fā)、編譯、運(yùn)行環(huán)境一致化;Golang、Rust這樣原生支持多系統(tǒng)多平臺(tái)的編程語言屏蔽大量底層差異,降低跨平臺(tái)應(yīng)用的開發(fā)難度。這一系列前人智慧火花匯聚到一起,發(fā)生了奇妙的反應(yīng)——WORA真正變的觸手可及,夢(mèng)想的陽光已經(jīng)照進(jìn)現(xiàn)實(shí)。

本篇章會(huì)大量分析技術(shù)原理及實(shí)現(xiàn)細(xì)節(jié),對(duì)于希望快速GET可執(zhí)行方案的同學(xué),可以直接跳轉(zhuǎn)到最后部分【可執(zhí)行方案回顧】查看。

如何支持多平臺(tái)

要了解容器鏡像是如何支持多平臺(tái)的,那我們需要仔細(xì)聊聊Manifest。使用過容器技術(shù)的同學(xué)都知道,我們運(yùn)行容器所使用的鏡像是由多層構(gòu)成的,而這些層的清單和其它容器信息共同存放在Manifest當(dāng)中。

Manifest

我們通常使用的容器鏡像是x86平臺(tái)的,執(zhí)行docker manifest inspect harbor-community.tencentcloudcr.com/multi-arch/alpine命令可以查看鏡像harbor-community.tencentcloudcr.com/multi-arch/alpine的Manifest內(nèi)容是一個(gè)JSON對(duì)象(如代碼段-01所示)。各個(gè)字段的解釋如下:

1.mediaType字段聲明這是一個(gè)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當(dāng)中并沒有任何字段描述鏡像的平臺(tái)信息。那應(yīng)該怎么樣支持多平臺(tái)呢?

我們可以設(shè)想一個(gè)簡單粗暴的,無視鏡像的平臺(tái),強(qiáng)行把交叉編譯出來的其它平臺(tái)的二進(jìn)制程序添加到鏡像內(nèi),使用Repository名稱或者Tag名稱來區(qū)分不同平臺(tái)的鏡像,例如coredns/coredns:coredns-arm64。在使用的時(shí)候,人工或者通過腳本判斷應(yīng)該拉取那個(gè)鏡像。

Schema 2

Ohhhhh,這當(dāng)然可以跑起來,但是難免太挫了吧?事實(shí)上,早在2015年底Docker社區(qū)的manifest v2.2規(guī)格文檔(也叫Schema 2,參見https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md)中就提及了多平臺(tái)鏡像,該功能通過manifest list(也叫做fat manifest)引用多個(gè)不同平臺(tái)鏡像的Manifest實(shí)現(xiàn)。

首先讓我們看看manifest是什么樣的,執(zhí)行docker manifest inspect alpine命令可以查看Docker Hub上的多平臺(tái)鏡像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是一個(gè)JSON數(shù)組,數(shù)組當(dāng)中應(yīng)用了不同平臺(tái)鏡像的Manifest。所以,推送多平臺(tái)鏡像時(shí),我們需要先分別推送不同平臺(tái)的鏡像層;然后創(chuàng)建manifest list,再引用平臺(tái)鏡像的Manifest,最后把manifest list上傳到Registry服務(wù)。而拉取鏡像時(shí),客戶端應(yīng)當(dāng)設(shè)置HTTP的請(qǐng)求頭字段Accept值為application/vnd.docker.distribution.manifest.v2+json和application/vnd.docker.distribution.manifest.list.v2+json,然后檢查服務(wù)端返回的響應(yīng)頭字段Content-Type判斷是舊鏡像格式,新鏡像格式或者時(shí)鏡像清單。

拋開規(guī)格文檔來說,只要我們使用的Registry服務(wù)的Distribution版本不低于v2.3,Docker CLI版本不低于v1.10就能過支持多平臺(tái)鏡像功能。

構(gòu)建多平臺(tái)鏡像

要構(gòu)建多平臺(tái)的容器鏡像,我們需要確保容器基礎(chǔ)鏡像和應(yīng)用程序的代碼或者二進(jìn)制都是目標(biāo)平臺(tái)的。

程序代碼

一些編程語言的編譯器能夠?yàn)槠渌脚_(tái)編譯二進(jìn)制文件,最為著名的包括Golang和Rust。我們將使用Golang編寫一個(gè)演示用web程序——通過HTTP訪問查看web服務(wù)程序的操作系統(tǒng)、硬件平臺(tái)等信息。具體代碼如代碼段-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)

}

我們?cè)贛acOS上使用go run運(yùn)行代碼段-03,httpie工具訪問本機(jī):9090端口,將會(huì)看見如下信息。

640.png

代碼準(zhǔn)備好了,現(xiàn)在我們有兩種構(gòu)建方法:手動(dòng)編譯,使用docker build構(gòu)建鏡像;使用docker buildx工具自動(dòng)化編譯構(gòu)建。

手動(dòng)編譯構(gòu)建

前置條件

·Dockerd啟用experimental

我們需要在Docker daemon配置文件中配置"experimental":true開啟實(shí)驗(yàn)性功能:

$vi/etc/docker/daemon.json

{

"experimental":true

}

修改Docker daemon配置需要重啟服務(wù)使配置生效:

$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,執(zhí)行docker manifest命令會(huì)看到報(bào)錯(cuò)提示docker manifest is only supported on a Docker cli with experimental cli features enabled,此時(shí)我們需要執(zhí)行export DOCKER_CLI_EXPERIMENTAL="enabled"開啟客戶端實(shí)驗(yàn)特性支持。在v20.10及以上版本的Docker CLI會(huì)默認(rèn)開啟實(shí)驗(yàn)特性,無需額外操作。

交叉編譯

在我們的Golang代碼中沒有使用CGO的時(shí)候,通過簡單設(shè)置環(huán)境變量就能夠交叉編譯出其它平臺(tái)和操作系統(tǒng)上能夠執(zhí)行的二進(jìn)制文件。其中:

GOARCH用于指定編譯的目標(biāo)平臺(tái),如amd64、arm64、riscv64等平臺(tái)。

GOOS用于指定編譯的目標(biāo)系統(tǒng),如darwin、linux。

本篇中,我們構(gòu)建能夠在Linux發(fā)行版中執(zhí)行的容器鏡像,所以編譯目標(biāo)系統(tǒng)環(huán)境變量GOOS統(tǒng)一設(shè)置為linux。執(zhí)行代碼段0-4中的命令構(gòu)建出二進(jìn)制文件備用。

// 代碼段-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

構(gòu)建各個(gè)平臺(tái)的鏡像

首先,我們編寫一個(gè)Dockerfile用于構(gòu)建鏡像。

FROM scratch

LABEL authors="Fanjian Kong"

ADD webapp/app/

WORKDIR/app

CMD["/app/webapp"]

然后,分別構(gòu)建不同平臺(tái)的鏡像,可以使用如代碼段-05的腳本幫助構(gòu)建。

//代碼段-05

#!/bin/bash

IMAGE?=kofj/multi-demo

NOCOLOR:='33[0m'

RED:='33[0;31m'

GREEN:='33[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


立即登錄,閱讀全文
版權(quán)說明:
本文內(nèi)容來自于騰訊云原生,本站不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。文章內(nèi)容系作者個(gè)人觀點(diǎn),不代表快出海對(duì)觀點(diǎn)贊同或支持。如有侵權(quán),請(qǐng)聯(lián)系管理員(zzx@kchuhai.com)刪除!
相關(guān)文章
騰訊云數(shù)據(jù)庫PostgreSQL全面支持PG 17
騰訊云數(shù)據(jù)庫PostgreSQL全面支持PG 17
即日起,騰訊云PostgreSQL全面支持PostgreSQL 17.0。所有用戶可使用大版本升級(jí)能力升級(jí)至最新的PostgreSQL 17.0進(jìn)行體驗(yàn),也可以在產(chǎn)品購買頁直接購買。
騰訊云
云服務(wù)
2024-12-152024-12-15
高可用這個(gè)問題,加機(jī)器就能解決?
高可用這個(gè)問題,加機(jī)器就能解決?
互聯(lián)網(wǎng)服務(wù)的可用性問題是困擾企業(yè)IT人員的達(dá)摩克利斯之劍:防于未然,體現(xiàn)不出價(jià)值。已然發(fā)生,又面臨P0危機(jī)。就更別提穩(wěn)定性建設(shè)背后顯性的IT預(yù)算問題與隱性的人員成本問題。
騰訊云
云服務(wù)
2024-11-252024-11-25
TDSQL TDStore引擎版替換HBase:在歷史庫場(chǎng)景中的成本與性能優(yōu)勢(shì)
TDSQL TDStore引擎版替換HBase:在歷史庫場(chǎng)景中的成本與性能優(yōu)勢(shì)
HBase憑借其高可用性、高擴(kuò)展性和強(qiáng)一致性,以及在廉價(jià)PC服務(wù)器上的低部署成本,廣泛應(yīng)用于大規(guī)模數(shù)據(jù)分析。
騰訊云
云服務(wù)
2024-11-042024-11-04
復(fù)雜查詢性能弱,只讀分析引擎來幫忙
復(fù)雜查詢性能弱,只讀分析引擎來幫忙
隨著當(dāng)今業(yè)務(wù)的高速發(fā)展,復(fù)雜多表關(guān)聯(lián)的場(chǎng)景越來越普遍。但基于行式存儲(chǔ)的數(shù)據(jù)庫在進(jìn)行復(fù)雜查詢時(shí)性能相對(duì)較弱。
騰訊云
云服務(wù)
2024-11-022024-11-02
掃碼登錄
打開掃一掃, 關(guān)注公眾號(hào)后即可登錄/注冊(cè)
加載中
二維碼已失效 請(qǐng)重試
刷新
賬號(hào)登錄/注冊(cè)
個(gè)人VIP
小程序
快出海小程序
公眾號(hào)
快出海公眾號(hào)
商務(wù)合作
商務(wù)合作
投稿采訪
投稿采訪
出海管家
出海管家