构建多CPU架构支持的Docker镜像

ぃ、小莉子 提交于 2019-12-07 15:21:27

作者简介:
胡晓亮目前就职于IBM Platform Computing 系统科技部云计算部门,担任云计算开发部工程师。自2013年8月开始参与OpenStack,然后又转战Spark,目前工作在K8S相关领域。
责编:钱曙光(qianshg@csdn.net)
声明:本篇为原创内容,CSDN独家发布,禁止未经授权的任何形式转载。

Docker简介

由于基于Hypervisor虚拟化技术仍然存在一些性能和资源使用效率方面的问题,近3年来容器技术席卷了整个互联网应用乃至IT界的技术革新,成为一种被大家广泛认可的服务器资源共享技术。它从根本上变革了应用程序的开发和发布方式,经过爆炸式的成长,容积技术的翘楚Docker实际上已经成为了容器格式和运行时的标准,大量流行的开源应用程序都宣布支持容器化,并且一些企业级的应用程序也开始往容器上迁移, Docker容器世界欣欣向荣。

Multi architecture Docker镜像

Multi architecture Docker镜像的支持让Docker向自己宣称的口号 “Build, Ship, and Run Any App, Anywhere” 更近了一大步。 那什么是Multi architecture Docker镜像呢?我们可以通过一个例子来说明。我们知道JAVA语言编写的应用程序是 “Write once, run anywhere”,它体现了JAVA跨平台的特性,但是JAVA程序的运行环境JVM(JAVA 虚拟机)却不是跨平台的,它是JAVA应用程序和OS平台之间的一个桥梁,不同的类型的OS和CPU架构机器需要安装相应的JVM才能运行JAVA程序。而 Docker image也是一样的,它也是依赖运行平台的,我们不能把一个httpd的ppc64le/linux架构的Docker镜像运行在amd64/linux 架构的环境中,我们也不能把一个Windows镜像运行在Linux平台上,否则你会遇到 “exec format error”。早期Docker镜像存储的设计没有充分考虑到镜像Multi architecture的支持,而是简单的使用镜像存储库的前缀来区分相同应用的不同平台,并建议开发者将不同平台的镜像应该push到相对应的Docker hub的镜像仓库中,而这种糟糕的设计依然保留在最新的Docker设计文档中如下:

例如运行busybox在多个平台上,按照Docker社区的方法如下:

x86-64:     docker run -it --rm busybox or docker run -it --rm amd64/busybox
ppc64le:   docker run -it --rm ppc64le/busybox 
s390x:      docker run -it --rm s390x/busybox 

基于Docker社区的做法,对于我们自己的应用程序Docker镜像多平台支持,我们还可以采取类似方法:

1. 基于tag

x86-64:    docker run -it --rm myapp:x86-64
ppc64le:  docker run -it --rm myapp:ppc64le
s390x:      docker run -it --rm myapp:s390x

2. 基于image 名称的后缀

x86-64:    docker run -it --rm myapp_x86-64
ppc64le:  docker run -it --rm myapp_ppc64le
s390x:      docker run -it --rm myapp_s390x

不管是基于镜像存储库的前缀,或者是基于tag或image名称后缀来区分不同的运行平台来pull相应的Docker镜像都不是好的设计,而好的设计是应该用户只要执行docker pull myapp就行了而不用关心容器的运行平台,Docker engine根据运行环境来pull相关的镜像,真正的做到 “Run Any App, Anywhere”。Docker社区已经注意到了这个问题,通过重新定义 v2.2 Image specification format(PR #1068)并在 Implement schema2 manifest formats(PR #1281)实现了Multi architecture Docker镜像功能。从Docker registry v2.3和Docker 1.10 开始,Docker hub就可以pull multi architecture Docker镜像了.

例如有2个Linux host,一个host(kvm-014009)arch是x86_64,另一个host(power807l )arch是ppc64le,我们分别在两个host 上运行 busybox 容器并且打印容器中基础镜像的arch, 看看是不是pull相同的镜像但是可以打印出与平台相关的输出。

x86_64 host :

[root@kvm-014009 ~]# docker run -it --rm busybox arch

Unable to find image 'busybox:latest' locally

latest: Pulling from library/busybox

67a0688b88df: Pull complete 

Digest: sha256:ac2fc418f3348815e68e266a5aa1b60bc522581c96964912560a0baacc4f5c06

Status: Downloaded newer image for busybox:latest

x86_64
ppc64le host:

[root@power807l ~]# docker run -it --rm busybox arch

Unable to find image 'busybox:latest' locally

latest: Pulling from library/busybox

219992aaea40: Pull complete 

Digest: sha256:ac2fc418f3348815e68e266a5aa1b60bc522581c96964912560a0baacc4f5c06

Status: Downloaded newer image for busybox:latest

ppc64le

我们run了相同的命令 “docker run -it –rm busybox arch” 在不同CPU architecture的host都得到了正确的运行结果,这是用户体验的一个极大的提升,用户根本不用关心 image的CPU arch 和OS的类型了。除了busybox镜像,httpd,nginx, tomcat和etcd等主流开源应用都有了Multi architecture Docker镜像支持,大家快快试试吧。

原理分析

如上文我们提到的自从Docker registry v2.3和Docker 1.10开始,Docker通过支持新的image Media 类型 manifest list 实现了Multi architecture Docker镜像功能:

1. 一个image manifest list 包含指向已经存在镜像的manifest对象列表

这里写图片描述

2. 一个image manifest list包含已经存在镜像的manifest对象的平台特性(CPU arch和OS类型)特征

这里写图片描述

根据manifest list对象定义,我们可以通过下面的流程了解Docker是如何支持Multi architecture Docker镜像的:

这里写图片描述

动手实验

制作一个multi architecture Docker镜像最重要的是定义image的manifest list,虽然image manifest list的标准已经确定也实现了,可是客户端如何push这个image的manifest list到Docker registry依然在Docker社区争论不休,有兴趣的同学可以看一下ticket Add manifest command了解一下细节。虽然还没有Docker official的客户端工具支持,但是广大的开源爱好者们还是自己根据 v2.2 Image specification format中的定义实现一些小工具可以帮助我们制作multi architecture Docker镜像。首先给大家介绍两个小工具,他们都可以push Docker image的manifest list到Docker registry中。

1. Enhanced Docker CLI

这是加强版的Docker CLI,他的code base 是基于社区的Docker CLI,并且marge了 Docker官方的Add manifest command这个pull request,它很好的集成了现有的Docker CLI, 并且增加了docker manifest的sub command 支持。

2. Manifest-tool

这是一个独立的小工具,它也实现了v2.2 Image specification format,并且支持查看,创建和push manifests list 对象到Docker registry,并且更加方便易用,因为它可以使用yaml文件定义image manifest list对象。

实验机器

Host CPU arch OS Docker version
kvm-014009 x86_64 Linux rhel 7.3 Server: 17.03.2-ce
power807l ppc64le Linux rhel 7.4 Server: 17.05.0-ce

先决条件

  1. 从source code编译出两个小工具的可执行文件 docker 和 manifest-tool,我们可以参考两个小工具github中提供的步骤,或者直接使用我编译好的x86_64平台的可执行文件
  2. 有一个可用的Docker hub 账号用来push镜像,假设这个账户是huxl,并且已经docker login成功

实验目的

假设有一个叫myapp是跨平台的应用,支持x86_64和ppc64le CPU arch 和Linux, 为了简单,在假设myapp就是一个文本文件,里面只有一句话:I am x86_64 application (for x86_64) 或者 I am ppc64le application (for ppc64le), Dockerfile也很简单如下,用户可以使用一致的命令run这个myapp应用在不同的平台上。

FROM alpine

WORKDIR /opt

COPY myapp .

实验步骤:

  • 使用Enhanced Docker CLI构建multi architecture Docker镜像

a. 构建myapp的x86_64镜像,并且push到Docker hub上

            [root@kvm-014314 muti-arch]# docker build -t huxl/myapp-x86_64:v1 .

            Sending build context to Docker daemon 3.072 kB

            Step 1/3 : FROM alpine

            latest: Pulling from library/alpine

            ff3a5c916c92: Pull complete 

            Digest: sha256:7df6db5aa61ae9480f52f0b3a06a140ab98d427f86d8d5de0bedab9b8df6b1c0

            Status: Downloaded newer image for alpine:latest

             ---> 3fd9065eaf02

            Step 2/3 : WORKDIR /opt

             ---> 204fec0aa91b

            Removing intermediate container 49747c6124e8

            Step 3/3 : COPY myapp .

             ---> d89721162bd0

            Removing intermediate container b63c71aebd49

            Successfully built d89721162bd0

            [root@kvm-014314 muti-arch]# docker push huxl/myapp-x86_64:v1 

            The push refers to a repository [docker.io/huxl/myapp-x86_64]

            723ba289c543: Pushed 

            7b2f1ea92884: Pushed 

            cd7100a72410: Mounted from library/alpine 

            v1: digest: sha256:ffac5d9918844da37c707e5b8690ce6f75563611bb6326daa5620fcb853bb590 size: 941

b. 构建myapp的ppc64le镜像,并且push到Docker hub上

            [root@power807l muti-arch]# docker build -t huxl/myapp-ppc64le:v1 .

            Sending build context to Docker daemon  3.072kB

            Step 1/3 : FROM alpine

            latest: Pulling from library/alpine

            0da653ea85b5: Pull complete 

            9fd90b777cc3: Pull complete 

            Digest: sha256:7df6db5aa61ae9480f52f0b3a06a140ab98d427f86d8d5de0bedab9b8df6b1c0

            Status: Downloaded newer image for alpine:latest

             ---> 5675e84fb0f7

            Step 2/3 : WORKDIR /opt

             ---> fe7e5331969e

            Removing intermediate container 91c27a07b969

            Step 3/3 : COPY myapp .

             ---> e2e2fd0b2abb

            Removing intermediate container c24a46b56e1e

            Successfully built e2e2fd0b2abb

            Successfully tagged huxl/myapp-ppc64le:v1

            [root@power807l muti-arch]# docker push huxl/myapp-ppc64le:v1 

            The push refers to a repository [docker.io/huxl/myapp-ppc64le]

            4abe83ad0ae2: Pushed 

            30b0e74edb8c: Pushed 

            d8f649dc98c5: Pushed 

            22963f19a0a3: Mounted from library/alpine 

            v1: digest: sha256:102f93c9ae146cbf2d5247db82e4429f5964978cfda1df72a9c7e444a4840437 size: 1148

            [root@power807l muti-arch]# 

c. 创建指向myappp的x86_64和ppc64le的镜像的manifests list对象并将它push到docker hub

            [root@kvm-014314 build]# pwd

            /root/cli/build

            [root@kvm-014314 build]# ./docker manifest create huxl/myapp:v1 huxl/myapp-x86_64:v1 huxl/myapp-ppc64le:v1

            Created manifest list docker.io/huxl/myapp:v1

            [root@kvm-014314 build]# ./docker manifest annotate huxl/myapp:v1 huxl/myapp-x86_64:v1 --os linux --arch amd64

            [root@kvm-014314 build]# ./docker manifest annotate huxl/myapp:v1 huxl/myapp-ppc64le:v1 --os linux --arch ppc64le

            [root@kvm-014314 build]# ./docker manifest push huxl/myapp:v1

            Pushed ref docker.io/huxl/myapp@sha256:102f93c9ae146cbf2d5247db82e4429f5964978cfda1df72a9c7e444a4840437 with digest: sha256:102f93c9ae146cbf2d5247db82e4429f5964978cfda1df72a9c7e444a4840437

            Pushed ref docker.io/huxl/myapp@sha256:ffac5d9918844da37c707e5b8690ce6f75563611bb6326daa5620fcb853bb590 with digest: sha256:ffac5d9918844da37c707e5b8690ce6f75563611bb6326daa5620fcb853bb590

            sha256:b1b4c2219f5f514fe5a752d158c76b3aa010fab2b1e1dbfa808755ff0848abfa

            [root@kvm-014314 build]#

d. 验证run myapp 镜像在两个host上,我们得到了不同的结果

            [root@kvm-014314 build]# docker run -it  --rm huxl/myapp:v1 cat /opt/myapp

            Unable to find image 'huxl/myapp:v1' locally

            v1: Pulling from huxl/myapp

            Digest: sha256:b1b4c2219f5f514fe5a752d158c76b3aa010fab2b1e1dbfa808755ff0848abfa

            Status: Downloaded newer image for huxl/myapp:v1

            I am x86_64 application

            [root@kvm-014314 build]# 


            [root@power807l muti-arch]# docker run -it  --rm huxl/myapp:v1 cat /opt/myapp

            Unable to find image 'huxl/myapp:v1' locally

            v1: Pulling from huxl/myapp

            Digest: sha256:b1b4c2219f5f514fe5a752d158c76b3aa010fab2b1e1dbfa808755ff0848abfa

            Status: Downloaded newer image for huxl/myapp:v1

            I am pcc64le application

            [root@power807l muti-arch]#

e. 从Docker hub上删除huxl/myapp:v1,后面我们使用Manifest-tool重新创建它
f. 删除本地huxl/myapp:v1镜像,为了不影响后续的实验

            [root@kvm-014314 manifest-tool]# docker rmi -f huxl/myapp:v1

            Untagged: huxl/myapp:v1

            Untagged: huxl/myapp@sha256:b1b4c2219f5f514fe5a752d158c76b3aa010fab2b1e1dbfa808755ff0848abfa

            [root@kvm-014314 manifest-tool]# 


            [root@power807l ~]# docker rmi -f huxl/myapp:v1

            Untagged: huxl/myapp:v1

            Untagged: huxl/myapp@sha256:b1b4c2219f5f514fe5a752d158c76b3aa010fab2b1e1dbfa808755ff0848abfa

            [root@power807l ~]# 
  • 使用Manifest-tool 构建multi architecture docke的镜像

a. 创建一个yaml文件用来表述支持multi architecture的镜像myapp:v1

            [root@kvm-014314 manifest-tool]# pwd

            /root/manifest-tool

            [root@kvm-014314 manifest-tool]# cat myapp.yaml 

            image: huxl/myapp:v1

            manifests:

              -

                image: huxl/myapp-ppc64le:v1

                platform:

                  architecture: ppc64le

                  os: linux

              -

                image: huxl/myapp-x86_64:v1

                platform:

                  architecture: amd64

                  os: linux

            [root@kvm-014314 manifest-tool]# 

b. push myapp.yaml到docker hub

            [root@kvm-014314 manifest-tool]# pwd

            /root/manifest-tool

            [root@kvm-014314 manifest-tool]# ./manifest-tool push from-spec myapp.yaml  

            Digest: sha256:dcf22c78689691df6d68117d55c4a4ce6b745caa38b1f31e882fc358a742c3c5 744

            [root@kvm-014314 manifest-tool]#

c. 验证run myapp 镜像在两个host上,我们都得到了正确的结果

            [root@kvm-014314 manifest-tool]# docker run -it  --rm huxl/myapp:v1 cat /opt/myapp

            Unable to find image 'huxl/myapp:v1' locally

            v1: Pulling from huxl/myapp

            Digest: sha256:dcf22c78689691df6d68117d55c4a4ce6b745caa38b1f31e882fc358a742c3c5

            Status: Downloaded newer image for huxl/myapp:v1

            I am x86_64 application

            [root@kvm-014314 manifest-tool]# 


            [root@power807l muti-arch]# docker run -it  --rm huxl/myapp:v1 cat /opt/myapp

            Unable to find image 'huxl/myapp:v1' locally

            v1: Pulling from huxl/myapp

            Digest: sha256:dcf22c78689691df6d68117d55c4a4ce6b745caa38b1f31e882fc358a742c3c5

            Status: Downloaded newer image for huxl/myapp:v1

            I am pcc64le application

            [root@power807l muti-arch]#
Note:我们可以使用命令docker manifest inspect huxl/myapp:v1 或者 manifest-tool inspect huxl/mycool-app:v1看到这个multi architecture Docker镜像的manifest list,这里就不在赘述了。

总结

  1. Push multi architecture Docker镜像到Docker registry并不会push image的layers,它只会push一个引用,这个引用指向了已经存在的多个Docker镜像;
  2. 只有multi architecture Docker镜像的创建者才需要知道这个引用背后指向具体的Docker镜像,而对最终的用户是透明的,最终用户只需要知道镜像的名字和tag就足够了;
  3. 有了multi architecture Docker镜像支持,我们再也不用在脚本里面判断OS的类型和CPU的arch而去pull相应的镜像,提高CICD代码的简洁和优雅;
  4. Image manifest list是极好的兼容性设计,它不但支持multi architecture Docker镜像,而且不会影响已经在的simple architecture Docker镜像的使用方式, 用户不用做二选一的艰难的决定。

这里写图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!