腾讯云多平台容器镜像构建(上)

时间:2021-07-15 | 标签: | 作者:Q8 | 来源:孔矾建网络

小提示:您能找到这篇{腾讯云多平台容器镜像构建(上)}绝对不是偶然,我们能帮您找到潜在客户,解决您的困扰。如果您对本页介绍的腾讯云多平台容器镜像构建(上)内容感兴趣,有相关需求意向欢迎拨打我们的服务热线,或留言咨询,我们将第一时间联系您!

前言

愿景与现实

< ">早在1995年,就有“write once and run anywhere”(WORA,编写一次即可在任何地方运行)用于描述Java应用程序。时过20年,Docker高声喊出了自己的口号——“Build Once,Run Anywhere”(一次构建,随处可用)。

< ">愿望是美好的,然而,现实总比理想骨感。Linux、Wi保利地产ndows这些不同的操作系统拥有不同的系统API;x86、Arm、IBM PowerPC这些不同的硬件平台的指令集不同,某些同平台的硬件甚至拥有不同的专用指令集用于加速应用。一次构建,随处可用面临着巨大的挑战,要构建能够在不同操作系统、不同硬件平台的运行的应用程序,仍然需要工程师们针对具体的操作系统和硬件平台进行海量的移植工作。

Why and How

< ">既然多平台的支持这么麻烦、充满挑战,我们是不是可以放弃支持?然而随着国产化大潮和IoT物联网的来临,我们编写的应用程序不仅仅会在X86服务器上运行,新时代的工程师们不得不面对更多的硬件平台,放弃多平台的支持无疑是放弃更宽广的未来。多平台的支持,势在必行。

< ">我们正处在一个波澜壮阔的大时代中,新技术、新工具在一次次的迭代升级,不断从Proposal(提议)到Prototype(原型),再逐渐的实用化。

< ">虚拟化技术使得我们可以做到模拟其它硬件平台;Docker等容器技术打破混乱,让开发、编译、运行环境一致化;Golang、Rust这样原生支持多系统多平台的编程语言屏蔽大量底层差异,降低跨平台应用的开发难度。这一系列前人智慧火花汇聚到一起,发生了奇妙的反应——WORA真正变的触手可及,梦想的阳光已经照进现实。

< ">本篇章自己怎么做推广会大量分析技术原理及实现细节,对于希望快速GET可执行方案的同学,可以直接跳转到最后部分【可执行方案回顾】查看。

< ">< color: rgb(0, 112, 192);">如何支持多平台

< ">要了解容器镜像是如何支持多平台的,那我们需要仔细聊聊Manifest。使用过容器技术的同学都知道,我们运行容器所使用的镜像是由多层构成的,而这些层的清单和其它容器信息共同存放在Manifest当中。

Manifest

< ">我们通常使用的容器镜像是x86平台的,执行docker manifest inspect harbor-community.tencentcloudcr.com/multi-arch/alpine命令可以查看镜像harbor-community.tencentcloudcr.com/multi-arch/alpine的Manifest内容是一个JSON对象(如代码段-01所示)。各个字段的解释如下:

< ">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。在使用的时候,人工或者通过脚本判断应该拉取那个镜像。

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+json和application/vnd.docker.distribution.manifest.list.v2+json,然后检查服务端返回的响应头字段Content-Type判断是旧镜像格式,新镜像格式或者时镜像清单。

< ">抛开规格文档来说,只要我们使用的Registry服务的Distribution版本不低于v2.3,Docker CLI版本不低于v1.10就能过支持多平台镜像功能。

< ">< color: rgb(0, 112, 192);">构建多平台镜像

< ">要构建多平台的容器镜像,我们需要确保容器基础镜像和应用程序的代码或者二进制都是目标平台的。

程序代码

< ">一些编程语言的编译器能够为其它平台编译二进制文件,最为著名的包括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端口,将会看见如下信息。

< ">代码准备好了,现在我们有两种构建方法:手动编译,使用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的时候,通过简单设置环境变量就能够交叉编译出其它平台和操作系统上能够执行的二进制文件。其中:

< ">GOARCH用于指定编译的目标平台,如amd64、arm64、riscv64等平台。

< ">GOOS用于指定编译的目标系统,如darwin、linux。

< ">本篇中,我们构建能够在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:='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


腾讯云多平台容器镜像构建(上)

上一篇:腾讯云镜像批量迁移利器:image-transfer
下一篇:Google Play:打造适用于 Daydream 和 Google Cardboard 的


版权声明:以上主题为“腾讯云多平台容器镜像构建(上)"的内容可能是本站网友自行发布,或者来至于网络。如有侵权欢迎联系我们客服QQ处理,谢谢。
相关内容
推荐内容
扫码咨询
    腾讯云多平台容器镜像构建(上)
    打开微信扫码或长按识别二维码

小提示:您应该对本页介绍的“腾讯云多平台容器镜像构建(上)”相关内容感兴趣,若您有相关需求欢迎拨打我们的服务热线或留言咨询,我们尽快与您联系沟通腾讯云多平台容器镜像构建(上)的相关事宜。

关键词:腾讯云多平台容器镜像构

关于 | 业务 | 案例 | 免责 | 隐私
客服邮箱:sales@1330.com.cn
电话:400-021-1330 | 客服QQ:865612759
沪ICP备12034177号 | 沪公网安备31010702002418号