目录
3.开启 binfmt_misc 来运行非本地架构 Docker 镜像
4.将默认 Docker 镜像构建器切换成多架构构建器(选做)
背景
公司有个项目需要私有化交付部署,但是客户的服务器的架构是ARM,我们目前手头的镜像都是x86 64的架构,且我们手头没有架构为ARM的实验服务器。
发现docker高版本有个叫docker buildx的工具可以满足我们的需求,让我们在架构为x86 64的实验服务器上构建出架构为ARM的镜像。
docker builx简介
docker buildx 是一个 docker CLI 插件,其扩展了 docker 命令。
在 Docker 19.03+ 版本中可以使用 docker buildx build命令,底层是基于BuildKit的api 构建镜像。builx命令支持 --platform参数可以同时构建支持多种系统架构的 Docker 镜像,大大简化了构建步骤。
回忆一下,我们平时使用docker镜像的场景,都是去dockerhub官网上,查到相关镜像,然后找到我们想要的tag,直接去我们的服务器上执行:docker pull xxx:version_xx。但是你有没有想过既然docker镜像有多架构版本,你执行pull拉取命名时,到底是拉取的哪个架构的版本?答案是:你执行docker pull命令的当前服务器的架构对应架构的镜像,即你的服务器的架构是什么就会去拉对应架构的镜像,如果dockerhub无对应架构的镜像就会拉取失败。下图是一张多架构镜像的示意图:
使用 docker buildx的前置条件
需要docker版本>=19.03,Linux内核版本>=4.8, binfmt-support >= 2.1.7。
1.linux内核升级
当前实验服务器是一台内核低于4.8、架构为x86 64的centos7.6。
[root@localhost download]# uname -r
3.10.0-957.el7.x86_64
[root@localhost download]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
[root@localhost download]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
[root@localhost download]# yum --enablerepo="elrepo-kernel" list --showduplicates | sort -r | grep kernel-ml.x86_64
kernel-ml.x86_64 5.14.13-1.el7.elrepo elrepo-kernel
kernel-ml.x86_64 5.14.12-1.el7.elrepo elrepo-kernel
[root@localhost download]# yum --enablerepo="elrepo-kernel" install kernel-ml-5.14.13-1.el7.elrepo.x86_64 -y
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* elrepo: hkg.mirror.rackspace.com
* elrepo-kernel: hkg.mirror.rackspace.com
* extras: mirrors.aliyun.com
* updates: mirrors.aliyun.com
Resolving Dependencies
--> Running transaction check
---> Package kernel-ml.x86_64 0:5.14.13-1.el7.elrepo will be installed
--> Finished Dependency Resolution
Dependencies Resolved
=====================================================================================================================================================================================================================================
Package Arch Version Repository Size
=====================================================================================================================================================================================================================================
Installing:
kernel-ml x86_64 5.14.13-1.el7.elrepo elrepo-kernel 55 M
Transaction Summary
=====================================================================================================================================================================================================================================
Install 1 Package
Total download size: 55 M
Installed size: 251 M
Downloading packages:
kernel-ml-5.14.13-1.el7.elrepo.x86_64.rpm | 55 MB 00:00:25
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Warning: RPMDB altered outside of yum.
Installing : kernel-ml-5.14.13-1.el7.elrepo.x86_64 1/1
Verifying : kernel-ml-5.14.13-1.el7.elrepo.x86_64 1/1
Installed:
kernel-ml.x86_64 0:5.14.13-1.el7.elrepo
Complete!
[root@localhost download]# grub2-set-default 0
[root@localhost download]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.14.13-1.el7.elrepo.x86_64
Found initrd image: /boot/initramfs-5.14.13-1.el7.elrepo.x86_64.img
Found linux image: /boot/vmlinuz-3.10.0-957.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-957.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-4c09ebdb718f43399d164036e825aecc
Found initrd image: /boot/initramfs-0-rescue-4c09ebdb718f43399d164036e825aecc.img
done
[root@localhost download]# grubby --default-kernel
/boot/vmlinuz-5.14.13-1.el7.elrepo.x86_64
[root@localhost download]# reboot
2.安装19.03版本的docker for linux
吐槽一下docker官网基于rpm方式安装docker的文档描述不全,具体文档如下:
Install Docker Engine on CentOS | Docker Documentation
须要注意的细节:
实际上需要在rpm所在目录,执行yum install xxx.rpm三次,分别是:
先装docker-ce-cli,再是containerd.io,最后装docker-ce;顺序不能乱,否则会打乱依赖关系,导致安装失败!
下载这三个rpm包的传送门如下:
待三个rpm安装完毕,执行:
#第一次安装完毕后的启动
systemctl start docker
#开机自启动
systemctl enable docker#安装完毕docker19.03的版本,发现执行docker buildx报错:
[root@localhost download]# docker buildx docker: 'buildx' is not a docker command. See 'docker --help'
出现报错新新:"docker: 'buildx' is not a docker command."。有两种方式可以解决:
#方式1(临时设置,只在当前终端管用,下次进入终端就不生效了)
export DOCKER_CLI_EXPERIMENTAL=enabled
#方式2(永久生效,直接修改/etc/docker/daemon.json的docker配置文件,新增一个键值对,就是我们平时设置docker国内镜像源的配置文件)
{"experimental": true}
再次,测试docker buildx成功:
[root@localhost download]# docker buildx
Usage: docker buildx COMMAND
Build with BuildKit
Management Commands:
imagetools Commands to work on images in registry
Commands:
bake Build from a file
build Start a build
create Create a new builder instance
inspect Inspect current builder instance
ls List builder instances
rm Remove a builder instance
stop Stop builder instance
use Set the current builder instance
version Show buildx version information
Run 'docker buildx COMMAND --help' for more information on a command.
3.开启 binfmt_misc 来运行非本地架构 Docker 镜像
如果你是 Mac 或者 Windows 版本 Docker 桌面版,可以跳过这个步骤,因为 binfmt_misc 默认开启。
如果你使用是 Linux 系统,需要设置 binfmt_misc。怎么设置?其实就是通过运行一个特权 Docker 容器,有了这个容器就相当于模拟了一个可以支持其他架构的镜像的运行环境。
具体操作如下:
[root@localhost ~]# docker run --rm --privileged tonistiigi/binfmt:latest --install all
installing: s390x OK
installing: mips64le OK
installing: arm OK
installing: ppc64le OK
installing: riscv64 OK
installing: mips64 OK
installing: arm64 OK
{
"supported": [
"linux/amd64",
"linux/arm64",
"linux/riscv64",
"linux/ppc64le",
"linux/s390x",
"linux/386",
"linux/arm/v7",
"linux/arm/v6"
],
"emulators": [
"jexec",
"qemu-aarch64",
"qemu-arm",
"qemu-mips64",
"qemu-mips64el",
"qemu-ppc64le",
"qemu-riscv64",
"qemu-s390x"
]
}
[root@localhost ~]# ls /proc/sys/fs/binfmt_misc/qemu-*
/proc/sys/fs/binfmt_misc/qemu-aarch64 /proc/sys/fs/binfmt_misc/qemu-mips64 /proc/sys/fs/binfmt_misc/qemu-ppc64le /proc/sys/fs/binfmt_misc/qemu-s390x
/proc/sys/fs/binfmt_misc/qemu-arm /proc/sys/fs/binfmt_misc/qemu-mips64el /proc/sys/fs/binfmt_misc/qemu-riscv64
4.将默认 Docker 镜像构建器切换成多架构构建器(选做)
这一步可以选做,不是必须的,意思是如果你想执行一次构建命令,打包出支持多个架构的镜像,那么就必须要做这一步,否则,当你--platform是多架构参数时,会报错:
docker buildx build --platform linux/amd64,linux/arm64/v8 -t harbor1.avlyun.org/g-project/paas-s9-front:longyan_arm64v8_amd64_test .
multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")
默认情况下,Docker 会使用旧的构建器,不支持多架构构建。
为了创建一个新的支持多架构的构建器,需要运行:
# 适用于国内环境
root@i-3uavns2y:~# docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master
# 适用于腾讯云环境(腾讯云主机、coding.net 持续集成)
root@i-3uavns2y:~# docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master-tencent
# 使用默认镜像
root@i-3uavns2y:~# docker buildx create --name mybuilder --driver docker-container
我们在此使用的是:
docker buildx create --use --name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master
效果如下图所示,看到没nginx构建带了--platform的多架构参数,没有报错了!
docker buildx实践
下面我们以nginx镜像为例子,来小试牛刀。
首先,之前AMD64(即AMD cpu的64位版本,本质上就是x86 64的架构)架构下的Dockerfile如下:
#第一阶段,npm构建前端源码
FROM node:12 as builder
COPY ./front-code/ /tmp/docker-build/frontend/code/
RUN cd /tmp/docker-build/frontend/code \
&& yarn \
&& yarn build
#第二阶段,nginx自定义配置文件、npm打包后的前端资源文件
FROM nginx:1.12.1-alpine
#添加nginx配置文件
COPY ./section9-config/prod-docker-config/config/nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./section9-config/prod-docker-config/config/nginx/conf.d/ /etc/nginx/conf.d
#构建源码(关键: tmp/docker-build/ frontend/code/build/ 这个是前端打包完毕的里层代码)
COPY --from=builder /tmp/docker-build/frontend/code/build/ /usr/share/nginx/html/front
发现:nginx:1.12.1-alpine 只有AMD架构,不支持ARM,如下图所示:
找了个1.12的版本(为了避免版本号的差异导致出现其他幺蛾子,新的ARM的基础镜像最好和之前的镜像保持一致),支持ARM架构,如下图:
接着,因为是多阶段构建,第一阶段涉及node的build,需要看一下node:12是不是支持ARM架构。
那么,修改以前Dockerfile,替换为可支持ARM架构的镜像,新的Dockerfile如下:
其实就是换了个nginx的镜像,以前的nginx镜像版本不支持ARM架构。
#第一阶段,npm构建前端源码
FROM node:12 as builder
COPY ./front-code/ /tmp/docker-build/frontend/code/
RUN cd /tmp/docker-build/frontend/code && \
yarn config set registry https://registry.npm.taobao.org && \
yarn && \
yarn build
#第二阶段,nginx自定义配置文件、npm打包后的前端资源文件
FROM nginx:1.12
#添加nginx配置文件
COPY ./config/nginx.conf /etc/nginx/nginx.conf
COPY ./config/conf.d/ /etc/nginx/conf.d
#构建源码(关键: tmp/docker-build/ frontend/code/build/ 这个是前端打包完毕的里层代码)
COPY --from=builder /tmp/docker-build/frontend/code/build/ /usr/share/nginx/html/front
试一下构建效果:
[root@localhost nginx-build]#docker buildx build --platform linux/amd64,linux/arm64/v8 -t harbor1.avlyun.org/g-project/paas-s9-front:longyan_arm64v8_amd64_test --output type=docker,dest=- . > nginx.tar -f ./Dockerfile
=> CACHED [linux/amd64 stage-1 4/4] COPY --from=builder /tmp/docker-build/frontend/code/build/ /usr/share/nginx/html/front 0.0s
=> ERROR exporting to oci image format 0.0s
------
> exporting to oci image format:
------
failed to solve: rpc error: code = Unknown desc = docker exporter does not currently support exporting manifest lists
注意:
>nginx.tar 表示镜像构建完毕后输出为一个tar包;
网上,关于docker buildx build都是带上的--push参数,表示构建完毕后会把镜像自动推送到dockerhub官网!
但是因为这里是我们公司的项目nginx镜像带了node build后的html、css、js,所以还是不要上传到dockerhub上,那样别人就可以拉取了。
此处的报错,是“docker exporter does not currently support exporting manifest lists”,猜测是因为我们没有使用的--push参数,而是使用>nginx.tar导出镜像为本地tar包,但是我们又指定了--platform为多架构,那么就会产出多个镜像,一个tar包只能存一个镜像,这就矛盾了!
如何解决?我们只需要在使用--platform时,只保留一个架构,应该就可以配合>nginx.tar来实现镜像打包到本地的需求!
以下测试一下:
docker buildx build --platform linux/arm64/v8 -t harbor1.avlyun.org/g-project/paas-s9-front:longyan_arm64v8_test --output type=docker,dest=- . > nginx.tar -f ./Dockerfile
[root@localhost nginx-build]# pwd
/home/download/nginx-build
[root@localhost nginx-build]# ls -l
total 12
drwxr-xr-x 3 root root 4096 Oct 21 14:33 config
-rw-r--r-- 1 root root 694 Oct 21 14:30 Dockerfile
drwxr-xr-x 5 root root 4096 Oct 21 14:53 front-code
docker buildx build --platform linux/arm64/v8 -t harbor1.avlyun.org/g-project/paas-s9-front:longyan_arm64v8_test --output type=docker,dest=- . > nginx.tar -f ./Dockerfile
[+] Building 17.3s (13/13) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 642B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/nginx:1.12 1.5s
=> [internal] load metadata for docker.io/library/node:12 2.5s
=> [builder 1/3] FROM docker.io/library/node:12@sha256:9b9e79251f73f999dd440a41714a91eea34996ec809953906009592def0d4380 0.0s
=> => resolve docker.io/library/node:12@sha256:9b9e79251f73f999dd440a41714a91eea34996ec809953906009592def0d4380 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 160.48kB 0.1s
=> [stage-1 1/4] FROM docker.io/library/nginx:1.12@sha256:72daaf46f11cc753c4eab981cbf869919bd1fee3d2170a2adeac12400f494728 0.0s
=> => resolve docker.io/library/nginx:1.12@sha256:72daaf46f11cc753c4eab981cbf869919bd1fee3d2170a2adeac12400f494728 0.0s
=> CACHED [stage-1 2/4] COPY ./config/nginx.conf /etc/nginx/nginx.conf 0.0s
=> CACHED [stage-1 3/4] COPY ./config/conf.d/ /etc/nginx/conf.d 0.0s
=> CACHED [builder 2/3] COPY ./front-code/ /tmp/docker-build/frontend/code/ 0.0s
=> CACHED [builder 3/3] RUN cd /tmp/docker-build/frontend/code && yarn && yarn build 0.0s
=> CACHED [stage-1 4/4] COPY --from=builder /tmp/docker-build/frontend/code/build/ /usr/share/nginx/html/front 0.0s
=> exporting to oci image format 14.6s
=> => exporting layers 13.3s
=> => exporting manifest sha256:6385ed41fc5808b97583135f0fac7634db9f441324d083f050b88beccf4e0814 0.0s
=> => exporting config sha256:e9f76b1785099bc1f6f1d3965a472e12a8cef2822c07ea83b66f7784e93acfe9 0.0s
=> => sending tarball 1.2s
[root@localhost nginx-build]# ls -l
total 62112
drwxr-xr-x 3 root root 4096 Oct 21 14:33 config
-rw-r--r-- 1 root root 603 Oct 21 22:55 Dockerfile
drwxr-xr-x 5 root root 4096 Oct 21 14:53 front-code
-rw-r--r-- 1 root root 63589376 Oct 22 00:15 nginx.tar
看到没?构建目录多出个nginx.tar的文件!!!
这个镜像tar包怎么给别人使用呢?直接load可以解包为镜像:
[root@localhost nginx-build]# docker load <nginx.tar
a4fa7dc4023b: Loading layer [==================================================>] 20.35MB/20.35MB
822227ce32fd: Loading layer [==================================================>] 21.33MB/21.33MB
1c5a2710f2fc: Loading layer [==================================================>] 204B/204B
90dd397c7b3e: Loading layer [==================================================>] 612B/612B
9c99ec8e4a9a: Loading layer [==================================================>] 1.459kB/1.459kB
b52121b20ccb: Loading layer [==================================================>] 21.89MB/21.89MB
Loaded image: harbor1.avlyun.org/g-project/paas-s9-front:longyan_arm64v8_test
[root@localhost nginx-build]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
harbor1.avlyun.org/g-project/paas-s9-front longyan_arm64v8_test e9f76b178509 37 minutes ago 167MB
tonistiigi/binfmt latest 00523b09b75a 5 weeks ago 35MB
dockerpracticesig/buildkit master 0e2e830dadfe 2 months ago 144MB
总结
docker buildx的工具,说白了就是给你提供一个能力,当你的宿主机是x86 64的架构时,你想构建镜像为ARM64的架构,就需要这个工具,给人的感觉有点类似交叉编译,诸如:go build的交叉编译,在win10下编译可执行程序,可用于特定linux平台!
架构标识说明
ARM 的系统架构架构标识:
arm64, armv8l, arm64v8, aarch64
arm, arm32, arm32v7, armv7, armv7l, armhf
arm32v6, armv6, armv6l, arm32v5, armv5, armv5l, armel, aarch32
Intel 和AMD的系统架构标识:
x86, 386, i386, i686
x86_64, x64, amd64
参考文档
使用 Docker Buildx 构建多种系统架构镜像 - 墨天轮