构建多平台容器镜像

2024/10/13

Tags: docker buildx buildkit

构建多平台容器镜像

Docker buildx 插件/子命令

buildx 是 Docker 的一个 CLI 插件,用于扩展来自于 Moby BuildKit 项目的构建功能。

注意:buildx 需要 Docker 19.03 或更高版本。

BuildKit

BuildKit是一个build引擎,它接收一个配置文件(Dockerfile),并转化成一个制品(容器镜像或其他制品)。相较与传统的build具有多阶段并发构建、更好的layer缓存支持等优点,Dockerfile中的RUN指令会被runc执行。

Docker Engine 从 23.0.0 版本开始默认在Linux上使用Buildx和BuildKit为builder。

Builder: a BuildKit daemon

一个 builder 是一个 BuildKit 守护进程,BuildKit是build引擎,它解决Dockerfile中的构建步骤,以生成容器镜像或其他制品。

Build drivers

Build 驱动有多种,例如 dockerdocker-containerkubernetesremote 等。

Build Drivers Comparison
Featuredockerdocker-containerkubernetesremote
Automatically load image
Cache export✓*
Tarball output
Multi-arch images
BuildKit configurationManaged externally
* The docker driver doesn't support all cache export options

默认的 Builder 实例

docker engine 会自动创建一个默认的 builder 实例,例如 default。默认的驱动是 docker,不支持多平台构建。

# docker buildx ls
NAME/NODE     DRIVER/ENDPOINT   STATUS    BUILDKIT   PLATFORMS
default*      docker                                 
 \_ default    \_ default       running   v0.16.0    linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386

创建 Builder 实例

下面创建一个 docker-container driver 的 builder 实例。

docker buildx create \
  --name container-builder \
  --driver docker-container \
  --platform linux/amd64,linux/arm64 \
  --bootstrap \
  --use

参数介绍:

创建后会发现多了一个容器 buildx_buildkit_container-builder0, 使用的镜像是 moby/buildkit:buildx-stable-1

# docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED          STATUS          PORTS   NAMES
6be2b567eaa3   moby/buildkit:buildx-stable-1   "buildkitd --allow-i…"   31 minutes ago   Up 31 minutes           buildx_buildkit_container-builder0

可以指定builder实例容器使用的容器镜像,--driver-opt image=name

可以指定buildkitd配置文件路径,--buildkitd-config /path/to/buildkitd.toml

buildkitd 可以配置镜像仓库的insecure等配置,参考buildkitd config,demo如下

# buildkitd.toml
[registry."registry.example.com"]
insecure = true
http = true

使用 buildx 构建多平台镜像

Buildx + Dockerfile 构建多平台镜像有三种方式

  1. 在内核中使用QEMU仿真支持, 利用的内核的 binfmt_misc 机制提供运行多平台二进制文件的能力
    • docker run --privileged --rm tonistiigi/binfmt --install all 注册所有平台的二进制文件格式到内核中
    • 上面的命令会将所有平台的二进制文件格式注册到内核中,例如 linux/arm64linux/amd64 等, 可以在 /proc/sys/fs/binfmt_misc/qemu-* 看到注册的二进制文件格式和对应的解释器路径
  2. 使用相同的构建器实例在多个不同平台节点上构建
  3. 使用Dockerfile中的一个阶段来交叉编译到不同的架构,需要语言支持交叉编译(Go语言等)或者平台无关(Java语言和前端静态文件等)

下面是使用 BuildKit 构建多平台镜像时,Dockerfile 中可以使用的ARGs, 参考Automatic platform args in the global scope

使用第一种多平台镜像构建方式是最简单的(如果构建使用的节点支持),下面这个实例使用第三种构建多平台镜像方式,在 Dockerfile 中使用一个阶段来交叉编译到不同的架构。

# Dockerfile
# 第一个阶段, 使用构建时所在平台的镜像环境中交叉编译, 最终复制到目标平台对应的镜像中使用
FROM --platform=$BUILDPLATFORM golang:1.23.1-alpine AS builder
ARG TARGETOS
ARG TARGETARCH
ENV GO111MODULE=on \
    CGO_ENABLED=0
WORKDIR /build
RUN apk --no-cache add tzdata
COPY . .
# 交叉编译
# docker buildx build --platform linux/amd64,linux/arm64 ... 等同于在 linux/amd64 平台下执行
# 1. GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o main
# 2. GOOS=linux GOARCH=arm64 go build -ldflags "-s -w" -o main
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags "-s -w" -o main

# 第二个阶段, 使用目标平台的镜像做为运行时镜像
# FROM 默认 --platform=$TARGETPLATFORM
FROM scratch
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /build/main /

ENTRYPOINT ["/main"]

构建镜像

docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/my-image:latest .

推送镜像

docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/my-image:latest --push .

参考