Docker构建和管理异构镜像

举报
AlexLiu 发表于 2020/07/14 15:28:39 2020/07/14
【摘要】 在Docker Swarm/Kubernetes/Openshift这类容器编排引擎进行服务部署时,简单地定义和使用一个Yaml文件进行跨平台混合部署将成为一个很大的问题(例如需要进行节点架构识别后,拉取指定镜像名称)。为了解决这个问题,Docker其实提出了一个异构镜像构建的基础原则,并推荐开发者均采用这种方式进行调整,以便在这类场景达到足够好的通用性。

为什么需要进行合理的异构镜像管理

"Build Once, Deploy anywhere"是Docker白皮书中写的非常漂亮的一句话,但是一旦想要在一些ARM机器上部署容器实例,比如说树莓派、鲲鹏实例甚至于i386架构的机器时,"Deploy anywhere"现在算是一个很棘手的问题,基本需要针对这些平台构建各自的镜像。因此,在Docker Swarm/Kubernetes/Openshift这类容器编排引擎进行服务部署时,简单地定义和使用一个Yaml文件进行跨平台混合部署将成为一个很大的问题(例如需要进行节点架构识别后,拉取指定镜像名称)。

为了解决这个问题,Docker其实提出了一个异构镜像构建的基础原则,并推荐开发者均采用这种方式进行调整,以便在这类场景达到足够好的通用性。

image.png

什么是异构镜像Docker Manifest

直接从镜像内容来直观的看Docker Manifest:

  • 一个无异构镜像的Manifest

      $ docker manifest inspect --verbose rustlang/rust:nightly-slim  {
      "Ref": "docker.io/amd64/rust:1.42-slim-buster",
      "Descriptor": {
          "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
          "digest": "sha256:1bf29985958d1436197c3b507e697fbf1ae99489ea69e59972a30654cdce70cb",
          "size": 742,
          "platform": {
          "architecture": "amd64",
          "os": "linux"
          }
      },
      "SchemaV2Manifest": {
          "schemaVersion": 2,
          "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
          "config": {
          "mediaType": "application/vnd.docker.container.image.v1+json",
          "size": 4830,
          "digest": "sha256:dbeae51214f7ff96fb23481776002739cf29b47bce62ca8ebc5191d9ddcd85ae"
          },
          "layers": [
          {
          "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
          "size": 27091862,
          "digest": "sha256:c499e6d256d6d4a546f1c141e04b5b4951983ba7581e39deaf5cc595289ee70f"
          },
          {
              "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
              "size": 175987238,
              "digest": "sha256:e2f298701fbeb02568c3dcb9822f8488e24ef12f5430bc2e8562016ba8670f0d"
          }
          ]
      }
      }

    上面这段显示了一个特定镜像TAG(rust:nightly-slim)的Manifest,可以看到Manifest内只有amd64架构的镜像。

  • 一个有异构镜像的Manifest

      $ docker manifest inspect ‐‐verbose rust:1.42-slim-buster  [
      {
          "Ref": "docker.io/library/rust:1.42-slim-buster@sha256:1bf29985958d1436197c3b507e697fbf1ae99489ea69e59972a30654cdce70cb",
          "Descriptor": {
          "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
          "digest": "sha256:1bf29985958d1436197c3b507e697fbf1ae99489ea69e59972a30654cdce70cb",
          "size": 742,
          "platform": {
              "architecture": "amd64",
              "os": "linux"
          }
          },
          "SchemaV2Manifest": { ... }
      },
      {
          "Ref": "docker.io/library/rust:1.42-slim-buster@sha256:116d243c6346c44f3d458e650e8cc4e0b66ae0bcd37897e77f06054a5691c570",
          "Descriptor": {
          "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
          "digest": "sha256:116d243c6346c44f3d458e650e8cc4e0b66ae0bcd37897e77f06054a5691c570",
          "size": 742,
          "platform": {
              "architecture": "arm",
              "os": "linux",
              "variant": "v7"
          }
          },
          "SchemaV2Manifest": { ... }
      ...  ]

    上面这段显示了一个特定镜像TAG(rust:1.42-slim-buster)的Manifest清单,每一段内包含了不同架构的镜像及其真实的TAG Hash。在一个Manifest内有包含了多个架构镜像的打包方式,使得混合部署以及"一次编译多处运行"成为了可能。

Manifest以清单的形式提供可见是非常有好处的,能够减少很多的麻烦,提高通用性,一般来说可以使用Docker manifest或buildx实验特性来达成目的。

预备操作:实验特性开启方法

Docker Manifest和Buildx目前还是实验特性,需要用户手动开启(可以配置文件的方式开启,也可以环境变量配置),其他的一些实验特性也可以通过这种方法开启。

  1. 配置文件开启方法

     $ vim ~/.docker/config.json {
         "experimental": "enabled"
     }

    配置新增Docker配置文件,并重启Docker服务(systemctl daemon-reload && systemctl restart docker

  2. 环境变量配置开启方法

     $ export DOCKER_CLI_EXPERIMENTAL=enabled

方案一:怎么用Docker Manifest来构建异构镜像

docker manifest命令相对来说比较硬核,对原有的编译和镜像上传没有实质性的流程改变。原流程中使用Docker build + docker push构建了基于某种架构的容器镜像,并且上传镜像至Docker Hub镜像库,如下使用docker manifest命令可以将上传的几个镜像合并至一个manifest list中去。

  1. 编译各平台镜像

     # 构建amd64镜像
     $ docker build -t xxxxx/xxxxx:xxx-amd64 --build-arg ARCH=amd64/ .
     $ docker push xxxxx/xxxxx:xxx-amd64
    
     # 构建arm64镜像
     $ docker build -t xxxxx/xxxxx:xxx-arm64 --build-arg ARCH=arm64/ .
     $ docker push xxxxx/xxxxx:xxx-arm64
    
     # 构建ppc64le镜像
     $ docker build -t xxxxx/xxxxx:xxx-ppc64le --build-arg ARCH=ppc64le/ .
     $ docker push xxxxx/xxxxx:xxx-ppc64le
  2. 合并镜像列表

     # 创建manifest列表,并加入本列表涵盖的镜像
     $ docker manifest create xxxxx/xxxxx:xxx-latest \
         --amend xxxxx/xxxxx:xxx-amd64 \
         --amend xxxxx/xxxxx:xxx-arm64 \
         --amend xxxxx/xxxxx:xxx-ppc64le \
    
     # 将manifest列表上传
     $ docker manifest push xxxxx/xxxxx:xxx-latest

这种方法比较适合构建脚本较为复杂,当前构建不便改动,但是对于构建流程管理主动性较好的项目。仅需要在所有镜像构建完成后,添加manifest的创建、合并与上传工作,即可完成异构镜像的列表合并。

方案二:怎么用Buildx来构建异构镜像

docker buildx命令构建异构镜像是一个一劳永逸的方法,但是对于Dockerfile有一定的要求。

docker buildx build \
    --push \
    --platform linux/arm64,linux/amd64,linux/ppc64le \
    --tag xxxxx/xxxxx:xxx-latest .

仅需上述一条命令,即可完成构建、合并和上传操作;但用户需要对代码库进行一定的评估,构建过程是否可以抽象成一个Dockerfile能描述的内容,兼顾可读性和便捷性。

实践:生产环境集成(以Github Action为例)

现在开源项目可以依托于平台的CI(例如Github的Action)来进行自动化构建异构镜像并上传DockerHub。

  1. docker buildx方法示例

    一个简单的Github Workflow如下所示:

     name: build our image on:
     push:
         branches: master jobs:
     build:
         runs-on: ubuntu-latest     steps:
         - name: checkout code         uses: actions/checkout@v2     - name: install buildx         id: buildx         uses: crazy-max/ghaction-docker-buildx@v1         with:
             version: latest     - name: build the image     run: |
             docker buildx build \
             --tag your-username/multiarch-example:latest \
             --platform linux/amd64,linux/arm/v7,linux/arm64 .
  2. docker manifest方法示例

    基于原有构建Workflow的最小化修改如下所示:

     name: Build arm64 Image on:
     push:
         branches:
         - master     - release-*     paths-ignore:
         - 'docs/**'
         - 'yamls/**'
    
     jobs:
     build:
         name: Build     runs-on: ubuntu-latest     steps:
         - name: Set up Go 1.x         uses: actions/setup-go@v2         with:
             go-version: ^1.14         id: go     - name: Check out code into the Go module directory         uses: actions/checkout@v2     - name: Docker Buildx         uses: crazy-max/ghaction-docker-buildx@v1.4.0     - name: Build         run: |
             go get -u github.com/securego/gosec/cmd/gosec
             make release-arm
         - name: Push         if: ${{ github.ref == 'refs/heads/master' || contains(github.ref, 'release') }}
             env:
             DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
             DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
             COMMIT: ${{ github.sha }}
             run: |
             TAG=$(cat VERSION)
             export DOCKER_CLI_EXPERIMENTAL=enabled
             echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
             docker tag xxxxx/xxxxx:$TAG xxxxx/xxxxx:$TAG-arm64
             docker tag kubeovn/xxxxx:$TAG xxxxx/xxxxx:$COMMIT-arm64
             docker images
             docker push xxxxx/xxxxx:$TAG-arm64
             docker push xxxxx/xxxxx:$COMMIT-arm64
             docker manifest create xxxxx/xxxxx:$TAG --amend xxxxx/xxxxx:$TAG-arm64 --amend xxxxx/xxxxx:$TAG-amd64
             docker manifest create xxxxx/xxxxx:$COMMIT --amend xxxxx/xxxxx:$COMMIT-arm64 --amend xxxxx/xxxxx:$COMMIT-amd64
             docker manifest push xxxxx/xxxxx:$TAG
             docker manifest push xxxxx/xxxxx:$COMMIT

一点呼吁:遵循异构镜像标准

当前一些主流容器镜像(如Nginx等)均已采用这种方式进行异构镜像的归档管理,但是仍有一些镜像尚未镜像异构镜像的归档管理(1. 只有一个架构的容器镜像;2. 有多个架构镜像,但是未合并到一个tag下,仍采用tag-arch的结构进行区分)。

在当前异构计算和边缘计算KubeEdge发展迅速的场景下,对于异构镜像的容器创建、扩容和安全设计等已经成为了头等问题。为了解决最基础的部署问题,还是呼吁开源组件的开发者们遵循异构镜像标准进行镜像构建。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。