Docker构建和管理异构镜像
为什么需要进行合理的异构镜像管理
"Build Once, Deploy anywhere"是Docker白皮书中写的非常漂亮的一句话,但是一旦想要在一些ARM机器上部署容器实例,比如说树莓派、鲲鹏实例甚至于i386架构的机器时,"Deploy anywhere"现在算是一个很棘手的问题,基本需要针对这些平台构建各自的镜像。因此,在Docker Swarm/Kubernetes/Openshift这类容器编排引擎进行服务部署时,简单地定义和使用一个Yaml文件进行跨平台混合部署将成为一个很大的问题(例如需要进行节点架构识别后,拉取指定镜像名称)。
为了解决这个问题,Docker其实提出了一个异构镜像构建的基础原则,并推荐开发者均采用这种方式进行调整,以便在这类场景达到足够好的通用性。
什么是异构镜像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目前还是实验特性,需要用户手动开启(可以配置文件的方式开启,也可以环境变量配置),其他的一些实验特性也可以通过这种方法开启。
配置文件开启方法
$ vim ~/.docker/config.json { "experimental": "enabled" }
配置新增Docker配置文件,并重启Docker服务(
systemctl daemon-reload && systemctl restart docker
)环境变量配置开启方法
$ export DOCKER_CLI_EXPERIMENTAL=enabled
方案一:怎么用Docker Manifest来构建异构镜像
docker manifest
命令相对来说比较硬核,对原有的编译和镜像上传没有实质性的流程改变。原流程中使用Docker build
+ docker push
构建了基于某种架构的容器镜像,并且上传镜像至Docker Hub镜像库,如下使用docker manifest
命令可以将上传的几个镜像合并至一个manifest list
中去。
编译各平台镜像
# 构建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
合并镜像列表
# 创建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。
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 .
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发展迅速的场景下,对于异构镜像的容器创建、扩容和安全设计等已经成为了头等问题。为了解决最基础的部署问题,还是呼吁开源组件的开发者们遵循异构镜像标准进行镜像构建。
- 点赞
- 收藏
- 关注作者
评论(0)