多平台构建

多平台构建是指一次构建调用,它针对多个不同的操作系统或 CPU 架构组合。在构建镜像时,这允许您创建可以在多个平台(例如 linux/amd64linux/arm64windows/amd64)上运行的单个镜像。

为什么选择多平台构建?

Docker 通过将应用程序及其依赖项打包到容器中来解决“在我的机器上能工作”的问题。这使得在不同环境(例如开发、测试和生产)中运行相同的应用程序变得容易。

但容器化本身只解决了问题的一部分。容器共享主机内核,这意味着在容器内部运行的代码必须与主机的架构兼容。这就是为什么您不能在 arm64 主机上运行 linux/amd64 容器(不使用模拟),也不能在 Linux 主机上运行 Windows 容器。

多平台构建通过将同一应用程序的多个变体打包到单个镜像中来解决此问题。这使您可以在不同类型的硬件上运行相同的镜像,例如运行 x86-64 的开发机器或云中基于 ARM 的 Amazon EC2 实例,而无需模拟。

单平台和多平台镜像的区别

多平台镜像与单平台镜像的结构不同。单平台镜像包含一个指向单个配置和一组层的清单。多平台镜像包含一个清单列表,指向多个清单,每个清单都指向不同的配置和一组层。

Multi-platform image structure

当您将多平台镜像推送到注册表时,注册表会存储清单列表和所有单独的清单。当您拉取镜像时,注册表会返回清单列表,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 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/amd64linux/arm64

$ docker buildx build --platform linux/amd64,linux/arm64 .

策略

您可以根据用例使用三种不同的策略构建多平台镜像:

  1. 通过QEMU使用模拟
  2. 使用具有多个原生节点的构建器
  3. 在多阶段构建中使用交叉编译

QEMU

如果您的构建器已经支持,使用 QEMU 模拟构建多平台镜像是最简单的入门方式。使用模拟无需更改 Dockerfile,BuildKit 会自动检测可用于模拟的架构。

注意

使用 QEMU 模拟可能比原生构建慢得多,特别是对于计算密集型任务,如编译和压缩或解压缩。

如果可能,请改用多个原生节点交叉编译

Docker Desktop 默认支持在模拟下运行和构建多平台镜像。无需配置,因为构建器使用 Docker Desktop VM 中捆绑的 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 安装完成并且可执行文件类型在主机操作系统上注册,它们就可以在容器内部透明地工作。您可以通过检查 F 是否在 /proc/sys/fs/binfmt_misc/qemu-* 的标志中来验证您的注册。

多个原生节点

使用多个原生节点可以更好地支持 QEMU 无法处理的更复杂情况,并且还提供更好的性能。

您可以使用 --append 标志向构建器添加其他节点。

以下命令从名为 node-amd64node-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

交叉编译

根据您的项目,如果您使用的编程语言对交叉编译有很好的支持,您可以利用多阶段构建来从构建器的原生架构为目标平台构建二进制文件。特殊的构建参数,例如 BUILDPLATFORMTARGETPLATFORM,在您的 Dockerfile 中自动可用。

在以下示例中,FROM 指令被固定到构建器的原生平台(使用 --platform=$BUILDPLATFORM 选项)以防止模拟启动。然后,预定义的 $BUILDPLATFORM$TARGETPLATFORM 构建参数在 RUN 指令中进行插值。在这种情况下,值只是用 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 镜像存储

步骤

  1. 创建空目录并导航到该目录

    $ mkdir multi-platform
    $ cd multi-platform
    
  2. 创建一个简单的 Dockerfile,打印容器的架构

    # syntax=docker/dockerfile:1
    FROM alpine
    RUN uname -m > /arch
  3. linux/amd64linux/arm64 构建镜像

    $ docker build --platform linux/amd64,linux/arm64 -t multi-platform .
    
  4. 运行镜像并打印架构

    $ docker run --rm multi-platform cat /arch
    
    • 如果您在 x86-64 机器上运行,您应该会看到 x86_64
    • 如果您在 ARM 机器上运行,您应该会看到 aarch64

使用 Docker Build Cloud 构建多平台 Neovim

此示例演示了如何使用 Docker Build Cloud 运行多平台构建,以编译并导出 linux/amd64linux/arm64 平台的 Neovim 二进制文件。

Docker Build Cloud 提供托管的多节点构建器,支持原生多平台构建,无需模拟,这使得 CPU 密集型任务(如编译)的速度大大加快。

先决条件

步骤

  1. 创建空目录并导航到该目录

    $ mkdir docker-build-neovim
    $ cd docker-build-neovim
    
  2. 创建一个构建 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 /
  3. 使用 Docker Build Cloud 为 linux/amd64linux/arm64 构建镜像

    $ docker build \
       --builder <cloud-builder> \
       --platform linux/amd64,linux/arm64 \
       --output ./bin .
    

    此命令使用云构建器构建镜像并将二进制文件导出到 bin 目录。

  4. 验证二进制文件是否为两个平台构建。您应该会看到 linux/amd64linux/arm64nvim 二进制文件。

    $ tree ./bin
    ./bin
    ├── linux_amd64
    │   └── nvim
    └── linux_arm64
        └── nvim
    
    3 directories, 2 files
    

交叉编译 Go 应用程序

此示例演示了如何使用多阶段构建为多个平台交叉编译 Go 应用程序。该应用程序是一个简单的 HTTP 服务器,监听端口 8080 并返回容器的架构。此示例使用 Go,但相同的原理适用于其他支持交叉编译的编程语言。

使用 Docker 构建进行交叉编译的工作原理是利用一系列预定义(在 BuildKit 中)的构建参数,这些参数为您提供有关构建器平台和构建目标的信息。您可以使用这些预定义参数将平台信息传递给编译器。

在 Go 中,您可以使用 GOOSGOARCH 环境变量来指定要构建的目标平台。

先决条件

  • Docker Desktop 或 Docker Engine

步骤

  1. 创建空目录并导航到该目录

    $ mkdir go-server
    $ cd go-server
    
  2. 创建构建 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,构建器将尝试使用模拟为指定平台构建镜像。

  3. 要添加交叉编译支持,请更新 Dockerfile 以使用预定义的 BUILDPLATFORMTARGETPLATFORM 构建参数。当您在 docker build 命令中使用 --platform 标志时,这些参数在 Dockerfile 中自动可用。

    • 使用 --platform=$BUILDPLATFORM 选项将 golang 镜像固定到构建器平台。
    • 为 Go 编译阶段添加 ARG 指令,使 TARGETOSTARGETARCH 构建参数可用于此阶段的命令。
    • GOOSGOARCH 环境变量设置为 TARGETOSTARGETARCH 的值。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"]
    
  4. linux/amd64linux/arm64 构建镜像

    $ docker build --platform linux/amd64,linux/arm64 -t go-server .
    

此示例演示了如何使用 Docker 构建为多个平台交叉编译 Go 应用程序。交叉编译的具体步骤可能因您使用的编程语言而异。请查阅您的编程语言文档,了解有关为不同平台交叉编译的更多信息。

提示

您可能还想查看xx - Dockerfile 交叉编译助手xx 是一个 Docker 镜像,其中包含使 Docker 构建更容易进行交叉编译的实用脚本。

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.