使用buildx构建多平台可用Docker镜像

在日常的开发中,我们会有让程序在不同平台运行的需求。除了常用的Windows系统,Linux系统以及Mac OS系统外,我们甚至还希望让程序运行在以树莓派为代表的ARM平台下,或者是跑在嵌入式设备的路由器上。要构建出适合不同平台的镜像并不是一件容易的事情,除非你直接到目标平台下构建,或者模拟出目标平台下的环境来进行构建。

最近我的开源项目GoDNS就有用户提了个issue,GoDNS的Docker镜像在树莓派平台下运行失败,从错误提示来看,就知道是镜像的问题。之前只考虑了镜像在Linux或者Mac OS下运行,完全没考虑兼容树莓派平台。解决的办法,最直接的就是在树莓派中去打包和构建镜像。不过,我手头又没有树莓派,难道要去买一个?这成本也有点太高了点。

幸好,新版的Docker从版本19.03后已经开始支持一个新的命令行工具,叫做buildx,目地就是为了解决我们在一个平台下,一次性构建出多个平台可用镜像的需求。buildx是一个基于命令行的Docker扩展插件。在MacOS下,如果你使用桌面版的Docker,非常容易启用这个插件,只需要打开Experimental features这个选项即可。

https://xiaozhou.net/pics/buildx/1.png

启用这个新特性之后,需要重启Docker,然后在命令行进行验证:

1
2
> docker buildx version
github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7

看到显示的版本号,表示buildx已经启用成功。默认情况下,Docker不会启用多平台架构的构建器,需要我们自己创建一个新的构建器,并激活和启动这个新的构建器:

1
> docker buildx create --name mybuilder

创建完成后,启用新的构建器:

1
> docker buildx inspect mybuilder --bootstrap

最后,再次验证构建器是否正常运行:

1
2
3
4
5
6
docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
mybuilder * docker-container
mybuilder0 unix:///var/run/docker.sock stopped
default docker
default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

在此可以看到,新的构建器已经支持其他多种平台。接下来,我们需要实战操作一下,用新的构建器,构建支持多系统平台的多个镜像。以我的项目GoDNS为例,Dockerfile如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM golang:alpine AS builder
RUN mkdir /godns
ADD . /godns/
WORKDIR /godns
RUN go build -o godns cmd/godns/godns.go

FROM alpine
RUN apk add --update ca-certificates
RUN mkdir /usr/local/godns
COPY --from=builder /godns/godns /usr/local/godns
RUN chmod +x /usr/local/godns/godns
RUN rm -rf /var/cache/apk/*
WORKDIR /usr/local/godns
ENTRYPOINT ["./godns", "-c", "/usr/local/godns/config.json"]

Dockerfile使用多步构建并打包镜像。随后,我们使用docker buildx一次性构建出多CPU平台的镜像,并push到DockerHub:

1
docker buildx build --platform linux/amd64,linux/386,linux/arm64,linux/arm/v7 -t timothyye/godns:latest . --push

在DockerHub上,我们能看到同一个版本的镜像下,用digest区分出了基于不同平台版本的镜像:

https://xiaozhou.net/pics/buildx/2.png

而当不同系统的用户,通过docker pull命令去拉取镜像的时候,docker会自动pull跟当前系统所匹配的镜像。这样一来,我们再也不用为构建不同CPU架构的镜像而犯愁了。

支持原创技术分享,据说打赏我的人,都找到了女朋友!