多平台构建
多平台构建是指一个构建调用可以同时针对多个不同的操作系统或 CPU 架构组合。构建镜像时,这允许您创建一个可在多个平台上运行的单个镜像,例如 `linux/amd64`、`linux/arm64` 和 `windows/amd64`。
为什么选择多平台构建?
Docker 通过将应用程序及其依赖项打包到容器中来解决“在我的机器上可以运行”的问题。这使得在不同的环境(例如开发、测试和生产)中轻松运行相同的应用程序。
但是,容器化本身只解决了部分问题。容器共享主机内核,这意味着容器内运行的代码必须与主机的架构兼容。这就是为什么您不能在 arm64 主机上运行 `linux/amd64` 容器(不使用模拟),或在 Linux 主机上运行 Windows 容器。
多平台构建通过将同一应用程序的多个变体打包到单个镜像中来解决此问题。这使您能够在不同类型的硬件上运行相同的镜像,例如运行 x86-64 的开发机器或云中的基于 ARM 的 Amazon EC2 实例,而无需模拟。
单平台镜像和多平台镜像的区别
多平台镜像的结构与单平台镜像不同。单平台镜像包含一个指向单个配置和一组层的单个清单。多平台镜像包含一个清单列表,指向多个清单,每个清单都指向不同的配置和一组层。
将多平台镜像推送到注册表时,注册表会存储清单列表和所有单个清单。拉取镜像时,注册表会返回清单列表,Docker 会根据主机的架构自动选择正确的变体。例如,如果您在基于 ARM 的 Raspberry Pi 上运行多平台镜像,Docker 会选择 `linux/arm64` 变体。如果您在 x86-64 笔记本电脑上运行相同的镜像,Docker 会选择 `linux/amd64` 变体(如果您使用的是 Linux 容器)。
先决条件
要构建多平台镜像,您首先需要确保您的 Docker 环境已设置为支持它。您可以通过两种方式做到这一点
- 您可以从“经典”镜像存储切换到 containerd 镜像存储。
- 您可以创建和使用自定义构建器。
Docker Engine 的“经典”镜像存储不支持多平台镜像。切换到 containerd 镜像存储可确保您的 Docker Engine 可以推送、拉取和构建多平台镜像。
创建使用具有多平台支持的驱动程序(例如 `docker-container` 驱动程序)的自定义构建器,将允许您在不切换到其他镜像存储的情况下构建多平台镜像。但是,您仍然无法将构建的多平台镜像加载到 Docker Engine 镜像存储中。但是,您可以使用 `docker build --push` 将它们直接推送到容器注册表。
启用 containerd 镜像存储的步骤取决于您使用的是 Docker Desktop 还是独立的 Docker Engine
如果您使用的是 Docker Desktop,请在Docker Desktop 设置中启用 containerd 镜像存储。
如果您使用的是独立的 Docker Engine,请使用守护程序配置文件启用 containerd 镜像存储。
要创建自定义构建器,请使用 `docker buildx create` 命令创建一个使用 `docker-container` 驱动程序的构建器。
$ docker buildx create \
--name container-builder \
--driver docker-container \
--bootstrap --use
注意
使用 `docker-container` 驱动程序的构建不会自动加载到您的 Docker Engine 镜像存储中。有关更多信息,请参阅构建驱动程序。
如果您使用的是独立的 Docker Engine 并需要使用模拟构建多平台镜像,您还需要安装 QEMU,请参阅手动安装 QEMU。
构建多平台镜像
触发构建时,使用 `--platform` 标志定义构建输出的目标平台,例如 `linux/amd64` 和 `linux/arm64`
$ docker buildx build --platform linux/amd64,linux/arm64 .
策略
您可以使用三种不同的策略构建多平台镜像,具体取决于您的用例
QEMU
如果您的构建器已支持,那么在 QEMU 模拟环境下构建多平台镜像是最简单的方法。使用模拟无需更改您的 Dockerfile,BuildKit 会自动检测可用于模拟的架构。
注意
使用 QEMU 模拟的速度可能比原生构建慢得多,尤其对于计算密集型任务(如编译、压缩或解压缩)而言。
Docker Desktop 默认支持在模拟环境下运行和构建多平台镜像。无需任何配置,因为构建器使用 Docker Desktop 虚拟机中捆绑的 QEMU。
手动安装 QEMU
如果您使用的是 Docker Desktop 之外的构建器(例如,如果您在 Linux 上使用 Docker Engine 或自定义远程构建器),则需要安装 QEMU 并注册主机操作系统上的可执行文件类型。安装 QEMU 的前提条件是:
- Linux 内核版本 4.8 或更高版本
binfmt-support
版本 2.1.7 或更高版本- QEMU 二进制文件必须是静态编译的,并使用
fix_binary
标志注册
使用tonistiigi/binfmt
镜像,可以使用单个命令安装 QEMU 并注册主机上的可执行文件类型。
$ docker run --privileged --rm tonistiigi/binfmt --install all
这将安装 QEMU 二进制文件并使用binfmt_misc
注册它们,使 QEMU 能够执行用于模拟的非原生文件格式。
安装 QEMU 并注册主机操作系统上的可执行文件类型后,它们将在容器内透明地工作。您可以通过检查/proc/sys/fs/binfmt_misc/qemu-*
中的标志中是否包含F
来验证您的注册情况。
多个原生节点
使用多个原生节点可以更好地支持 QEMU 无法处理的更复杂的情况,并且性能也更好。
您可以使用--append
标志向构建器添加其他节点。
以下命令根据名为node-amd64
和node-arm64
的 Docker 上下文创建一个多节点构建器。此示例假设您已经添加了这些上下文。
$ docker buildx create --use --name mybuild node-amd64
mybuild
$ docker buildx create --append --name mybuild node-arm64
$ docker buildx build --platform linux/amd64,linux/arm64 .
虽然这种方法比模拟具有优势,但管理多节点构建器会带来一些设置和管理构建器集群的额外开销。或者,您可以使用 Docker Build Cloud,这是一项在 Docker 的基础设施上提供托管多节点构建器的服务。使用 Docker Build Cloud,您可以获得原生的多平台 ARM 和 X86 构建器,而无需维护它们。使用云构建器还可以提供其他好处,例如共享构建缓存。
注册 Docker Build Cloud 后,将构建器添加到您的本地环境并开始构建。
$ docker buildx create --driver cloud <ORG>/<BUILDER_NAME>
cloud-<ORG>-<BUILDER_NAME>
$ docker build \
--builder cloud-<ORG>-<BUILDER_NAME> \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
--tag <IMAGE_NAME> \
--push .
更多信息,请参见Docker Build Cloud。
交叉编译
根据您的项目,如果使用的编程语言对交叉编译有良好的支持,您可以利用多阶段构建从构建器的原生架构为目标平台构建二进制文件。特殊的构建参数,例如BUILDPLATFORM
和TARGETPLATFORM
,会在您的 Dockerfile 中自动可用。
在下面的示例中,FROM
指令固定到构建器的原生平台(使用--platform=$BUILDPLATFORM
选项)以防止模拟启动。然后在RUN
指令中内插预定义的$BUILDPLATFORM
和$TARGETPLATFORM
构建参数。在本例中,值只是使用echo
打印到标准输出,但这说明了如何将它们传递给编译器进行交叉编译。
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log
示例
以下是一些多平台构建的示例
使用模拟的简单多平台构建
此示例演示如何使用 QEMU 模拟构建简单的多平台镜像。该镜像包含一个打印容器架构的单个文件。
先决条件
- Docker Desktop 或安装了QEMU的 Docker Engine
- 已启用 containerd 镜像存储
步骤
创建一个空目录并导航到它
$ mkdir multi-platform $ cd multi-platform
创建一个简单的 Dockerfile,打印容器的架构
# syntax=docker/dockerfile:1 FROM alpine RUN uname -m > /arch
为
linux/amd64
和linux/arm64
构建镜像$ docker build --platform linux/amd64,linux/arm64 -t multi-platform .
运行镜像并打印架构
$ docker run --rm multi-platform cat /arch
- 如果您在 x86-64 机器上运行,则应该看到
x86_64
。 - 如果您在 ARM 机器上运行,则应该看到
aarch64
。
- 如果您在 x86-64 机器上运行,则应该看到
使用 Docker Build Cloud 的多平台 Neovim 构建
此示例演示如何使用 Docker Build Cloud 运行多平台构建,以编译和导出Neovimlinux/amd64
和linux/arm64
平台的二进制文件。
Docker Build Cloud 提供托管的多节点构建器,支持原生多平台构建,无需模拟,因此可以更快地完成编译等 CPU 密集型任务。
先决条件
步骤
创建一个空目录并导航到它
$ mkdir docker-build-neovim $ cd docker-build-neovim
创建一个构建 Neovim 的 Dockerfile。
# syntax=docker/dockerfile:1 FROM debian:bookworm AS build WORKDIR /work RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update && apt-get install -y \ build-essential \ cmake \ curl \ gettext \ ninja-build \ unzip ADD https://github.com/neovim/neovim.git#stable . RUN make CMAKE_BUILD_TYPE=RelWithDebInfo FROM scratch COPY --from=build /work/build/bin/nvim /
使用 Docker Build Cloud 为
linux/amd64
和linux/arm64
构建镜像$ docker build \ --builder <cloud-builder> \ --platform linux/amd64,linux/arm64 \ --output ./bin .
此命令使用云构建器构建镜像并将二进制文件导出到
bin
目录。验证是否为这两个平台构建了二进制文件。您应该看到
linux/amd64
和linux/arm64
的nvim
二进制文件。$ tree ./bin ./bin ├── linux_amd64 │ └── nvim └── linux_arm64 └── nvim 3 directories, 2 files
交叉编译 Go 应用程序
此示例演示如何使用多阶段构建为多个平台交叉编译 Go 应用程序。该应用程序是一个简单的 HTTP 服务器,侦听端口 8080 并返回容器的架构。此示例使用 Go,但相同的原理也适用于其他支持交叉编译的编程语言。
Docker 构建中的交叉编译是通过利用一系列预定义的(在 BuildKit 中)构建参数来实现的,这些参数提供了有关构建器和构建目标平台的信息。您可以使用这些预定义的参数将平台信息传递给编译器。
在 Go 中,您可以使用GOOS
和GOARCH
环境变量来指定要构建的目标平台。
先决条件
- Docker Desktop 或 Docker Engine
步骤
创建一个空目录并导航到它
$ mkdir go-server $ cd go-server
创建一个构建 Go 应用程序的基本 Dockerfile
# syntax=docker/dockerfile:1 FROM golang:alpine AS build WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . RUN go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
此 Dockerfile 尚未能够进行交叉编译的多平台构建。如果您尝试使用
docker build
构建此 Dockerfile,构建器将尝试使用模拟来为指定的平台构建镜像。要添加交叉编译支持,请更新 Dockerfile 以使用预定义的
BUILDPLATFORM
和TARGETPLATFORM
构建参数。当您在docker build
中使用--platform
标志时,这些参数会在 Dockerfile 中自动可用。- 使用
--platform=$BUILDPLATFORM
选项将golang
镜像固定到构建器的平台。 - 为 Go 编译阶段添加
ARG
指令,使TARGETOS
和TARGETARCH
构建参数可用于此阶段的命令。 - 将
GOOS
和GOARCH
环境变量设置为TARGETOS
和TARGETARCH
的值。Go 编译器使用这些变量进行交叉编译。
# syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM golang:alpine AS build ARG TARGETOS ARG TARGETARCH WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
# syntax=docker/dockerfile:1 FROM golang:alpine AS build WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . RUN go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
# syntax=docker/dockerfile:1 -FROM golang:alpine AS build +FROM --platform=$BUILDPLATFORM golang:alpine AS build +ARG TARGETOS +ARG TARGETARCH WORKDIR /app ADD https://github.com/dvdksn/buildme.git#eb6279e0ad8a10003718656c6867539bd9426ad8 . -RUN go build -o server . RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o server . FROM alpine COPY --from=build /app/server /server ENTRYPOINT ["/server"]
- 使用
为
linux/amd64
和linux/arm64
构建镜像$ docker build --platform linux/amd64,linux/arm64 -t go-server .
此示例演示了如何使用 Docker 构建为多个平台交叉编译 Go 应用程序。交叉编译的具体步骤可能因您使用的编程语言而异。请参阅您的编程语言的文档,了解有关为不同平台交叉编译的更多信息。
提示
您可能还需要考虑查看xx - Dockerfile 交叉编译助手。
xx
是一个包含实用程序脚本的 Docker 镜像,使 Docker 构建的交叉编译更容易。