构建支持多平台的docker镜像

1个月前
# 构建支持多平台的docker镜像 ## 一、思路 近期团队在进行服务/软件的多平台(多CPU架构,例如:arm、mips等)改造,由于服务已经docker化,所以只需制作对应平台的镜像即可。 最先想到的就是在tag名这里做文章,例如: ``` bash docker.open-open.com/test:1.0-x86_64 docker.open-open.com/test:1.0-arm64 docker.open-open.com/test:1.0-mips64 ``` 或者 ``` bash docker.open-open.com/x86_64/test:1.0 docker.open-open.com/arm64/test:1.0 docker.open-open.com/mips64/test:1.0 ``` 在每台目标机上构建镜像,并通过架构名来区分。这种方式,在运维层面需要额外的工作,例如运行脚本、编排文件等都需要维护多套。 我们在查看[docker hub](https://hub.docker.com/)的tags时,可以看到 docker 镜像仓库是支持多个平台的 ![docker tag](https://simg.open-open.com/show/e43e327c2c3a4c0dbf9578e14823e24c.png) 使用时,也只需简单的 docker pull 镜像名、docker run 镜像名,docker cli会根据本机CPU架构选择合适的镜像文件。 **要制作支持多平台的docker镜像,有2种方式** - docker manifest 需要 docker 版本不低于 18 - buildlx 需要 docker 版本不低于 19.03 ## 二、开启实验性功能 docker manifest和buildlx 都需要开启实验性功能,默认是关闭的。 ### 2.1 添加docker client的使用参数 ``` bash $ mkdir ~/.docker/ -p $ vim ~/.docker/config.json ``` 添加如下内容 ``` json { "experimental": "enabled" } ``` 如果 ~/.docker/config.json 文件已经存在,只需添加`"experimental": "enabled"` ### 2.2 验证是否开启 #### 执行 docker version ``` bash $ docker version | grep -Pzo 'Client(.|\n)*\n\n' ``` >Client: Docker Engine - Community  Version: 19.03.5  API version: 1.40  Go version: go1.12.12  Git commit: 633a0ea  Built: Wed Nov 13 07:25:41 2019  OS/Arch: linux/amd64  **Experimental: true** Experimental: true 表示已开启 #### 执行 docker --help ``` bash $ docker --help | grep -iE 'manifest|buildx' ``` > buildx*   Build with BuildKit (Docker Inc., v0.3.1-tp-docker) manifest  Manage Docker image manifests and manifest lists 出现 buildx 和 manifest 2行说明,就表示可以使用buildx、manifest ## 三、使用 docker manifest ### 3.1 创建 manifest 列表 - 使用格式 ``` bash $ docker manifest create --help Usage: docker manifest create MANIFEST_LIST MANIFEST [MANIFEST...] ``` - 示例 ``` bash $ docker manifest create docker.open-open.com/service_dev:1.0.0 \ docker.open-open.com/service_dev:1.0.0-x86_64 \ docker.open-open.com/service_dev:1.0.0-arm64 \ docker.open-open.com/service_dev:1.0.0-mips64 ``` 注意: manifest 对应的镜像,需要在目标机上构建,并上传到镜像仓库中 ### 3.2 上传 manifest 列表 - 使用格式 ``` bash $ docker manifest push --help Usage: docker manifest push [OPTIONS] MANIFEST_LIST ``` - 示例 ``` bash $ docker manifest push docker.open-open.com/service_dev:1.0.0 sha256:c62005140cd5368a8a18bdb0e0dcbcb9e24a04ceb1e22518dd9501cd979c9c50 ``` ### 3.3 查看 manifest 清单 - 使用格式 ``` bash $ docker manifest inspect --help Usage: docker manifest inspect [OPTIONS] [MANIFEST_LIST] MANIFEST ``` - 示例 ``` bash $ docker manifest inspect docker.open-open.com/service_dev:1.0.0 ``` ``` json { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", "manifests": [ { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 4724, "digest": "sha256:f36faf79ac9f430f6e212d21bed24a4edc20e46b22ae422e83a14329b2300fbc", "platform": { "architecture": "arm64", "os": "linux" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 4724, "digest": "sha256:74b99f5527450769579f93613c06cf67acf33f932d2a566d206e855b55ed484e", "platform": { "architecture": "amd64", "os": "linux" } }, { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 4724, "digest": "sha256:3578489111d40b42d422b415c3584c831e56ca9e8fbf6c416ff49814e2bd088c", "platform": { "architecture": "mips64le", "os": "linux" } } ] } ``` ## 四、使用 buildlx ### 4.1 安装buildx - 下载buildx 官方地址:https://github.com/docker/buildx/releases ``` bash $ wget https://github.com/docker/buildx/releases/download/v0.4.1/buildx-v0.4.1.linux-amd64 ``` - 安装 ``` bash $ mkdir ~/.docker/cli-plugins/ $ cp buildx-v0.4.1.linux-amd64 ~/.docker/cli-plugins/docker-buildx $ chmod +x ~/.docker/cli-plugins/docker-buildx ``` - 查看 ``` bash $ docker buildx version github.com/docker/buildx v0.4.1 bda4882a65349ca359216b135896bddc1d92461c ``` ### 4.2 添加多架构支持 - 默认支持的架构 ``` bash $ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS default * docker default default running linux/amd64, linux/386 ``` 注: 每个架构下面执行的结果不一样 - 新增 ``` bash $ docker run --privileged --rm tonistiigi/binfmt --install all $ docker buildx create --use --name mybuilder $ docker buildx inspect --bootstrap ``` - 查看 ``` bash $ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS mybuilder * docker-container mybuilder0 unix:///var/run/docker.sock running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6 default docker default default running linux/amd64, linux/386 ``` **注意** **如果重启了机器,需要重新执行** ``` $ docker run --privileged --rm tonistiigi/binfmt --install all ``` **tonistiigi/binfmt 不支持misp**,要自行修改代码 cmd/binfmt/config.go 中添加mips的支持 ``` go "mips64el": { binary: "qemu-mips64el", magic: `\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00`, mask: `\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff`, }, ``` ### 4.3 构建镜像 Dockerfile文件不需要改的 构建命令用 docker buildx build 代替 docker build **示例** ``` bash $ docker buildx build --platform linux/arm64,linux/amd64,linux/mips64le . \ -t docker.open-open.com/service_dev:1.0.0 --push ``` 参数` --push` 表示构建成功后,上传镜像 成功后可以用`docker manifest inspect 镜像名` 或者 `docker buildx imagetools inspect 镜像名`查看 **示例** ``` $ docker buildx imagetools inspect docker.open-open.com/service_test:1.0.34r Name: docker.open-open.com/service_test:1.0.34r MediaType: application/vnd.docker.distribution.manifest.list.v2+json Digest: sha256:a615d286c00fee23654a135f53c3d5dbd3f2d3621d3cd99d22ba98c6f81b05f9 Manifests: Name: docker.open-open.com/service_test:1.0.34r@sha256:de698defff054380a4ea792c678007cfb2ccf209457c729402979e4a6b753f67 MediaType: application/vnd.docker.distribution.manifest.v2+json Platform: linux/arm64 Name: docker.open-open.com/service_test:1.0.34r@sha256:bb56ac0b075634b29d20bdbc6b630a6ca9579920605f0dfdd62d67422dd16c19 MediaType: application/vnd.docker.distribution.manifest.v2+json Platform: linux/amd64 Name: docker.open-open.com/service_test:1.0.34r@sha256:54a989e94e9b1a23475a202ead2aeab00c9d8b5963f371edc71ffbe87176cee7 MediaType: application/vnd.docker.distribution.manifest.v2+json Platform: linux/mips64le ``` ## 五、manifest 与 buildlx 选择 **buildlx 的优点:** - 可以不要目标平台的设备,在本机就可以构建出多个平台(例如:arm、mips等)的镜像 - 构建方式简洁,只需修改构建命令 **buildlx 的缺点:** - 由于 buildlx 使用 QEMU,构建时,如果需要编译(例如:c/c++),速度非常慢,往往10多分钟就可以编译好的,却要花费好几个小时 - 有时候会卡死(中断重新构建,可以解决问题) - 有时候会编译失败(无法解决) - 有时候编译出来的镜像,在目标机器上无法运行(无法解决) **manifest 的优点:** - 兼容性好 - 构建速度快 **manifest 的缺点:** - 需要有目标平台的设备 - 比 buildlx 略繁琐 - manifest 列表 create 后,无法删除内部的manifest。镜像变动了,tag必须也变动 我们在实际使用中优先采用的是 buildlx,只有 buildlx 无法编译或构建的镜像无法使用时才会采用manifest。 ## 六、基础镜像(base)的选择 要构建多平台的镜像,那么基础镜像也要支持相应的平台。这个可以到[docker hub](https://hub.docker.com/)去看,我们最终选择的是`debian:buster-slim`。 `debian:buster-slim` 支持的平台很全面,国内的龙芯、飞腾、鲲鹏都可以使用。体积也非常的小。 没有采用`Alpine`,因为有些项目在非x86_64上编译通不过。 ## 参考文章 https://docs.docker.com/engine/reference/commandline/manifest/ https://www.docker.com/blog/multi-arch-images/ https://github.com/docker/buildx/