多平台构建

多平台版本是指将 多种不同的操作系统或 CPU 架构组合。什么时候 构建镜像,这允许您创建可在多个上运行的单个镜像 平台,例如 、 和 。linux/amd64linux/arm64windows/amd64

为什么选择多平台构建?

Docker 通过打包应用程序解决了“它可以在我的机器上运行”的问题 及其依赖项添加到容器中。这使得运行相同的操作变得容易 应用程序,例如开发、测试和 生产。

但容器化本身只能解决部分问题。器皿 共享主机内核,这意味着在 容器必须与主机的架构兼容。这就是您 无法在 arm64 主机上运行容器(不使用仿真), 或 Linux 主机上的 Windows 容器。linux/amd64

多平台 build 通过打包 相同的应用程序合并到单个镜像中。这使您能够在 不同类型的硬件,例如运行 x86-64 或 云中基于 ARM 的 Amazon EC2 实例,无需仿真。

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

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

多平台镜像结构

当您将多平台镜像推送到 registry 时,registry 会将 manifest list 和所有单独的清单。当您拉取镜像时, registry 返回清单列表,Docker 会自动选择 根据主机的架构进行正确的变体。例如,如果您运行 multi platform 镜像,Docker 选择变体。如果您在 x86-64 笔记本电脑上运行相同的镜像,则 Docker 选择变体(如果您使用的是 Linux 容器)。linux/arm64linux/amd64

先决条件

要构建多平台镜像,您首先需要确保您的 Docker 环境设置为支持它。有两种方法可以做到这一点:

  • 您可以从 “classic” 镜像存储切换到 containerd 镜像存储。
  • 您可以创建和使用自定义生成器。

Docker Engine 的 “classic” 镜像存储不支持多平台 镜像。切换到 containerd 镜像存储可确保您的 Docker 引擎 可以推送、拉取和构建多平台镜像。

创建使用具有多平台支持的驱动程序的自定义构建器, 比如 driver,会让你构建多平台镜像 而无需切换到其他镜像存储。但是,您仍然无法 将您构建的多平台镜像加载到 Docker Engine 镜像中 商店。但是,您可以使用 .docker-containerdocker build --push


启用 containerd 镜像存储的步骤取决于您是否 使用 Docker Desktop 或 Docker Engine 独立版:

  • 如果您使用的是 Docker Desktop,请在 Docker Desktop 设置中启用 containerd 镜像存储。

  • 如果您使用的是 Docker Engine 独立版,请启用 containerd 镜像存储 使用 daemon 配置文件

要创建自定义构建器,请使用命令创建一个 builder 的 Builder。docker buildx createdocker-container

$ docker buildx create \
  --name container-builder \
  --driver docker-container \
  --bootstrap --use

注意

使用驱动程序的构建不会自动加载到您的 Docker Engine 镜像存储。有关更多信息,请参阅构建 驱动程序docker-container


如果您使用的是 Docker Engine 独立版,并且需要构建多平台 镜像,还需要安装 QEMU,详见安装 QEMU 手动

构建多平台镜像

触发构建时,使用标志定义目标 构建输出的平台,例如 和 :--platformlinux/amd64linux/arm64

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

策略

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

  1. 通过 QEMU 使用仿真
  2. 使用具有多个原生节点的构建器
  3. 交叉编译与多阶段构建结合使用

QEMU

使用 QEMU 在仿真下构建多平台镜像是最简单的方法 如果您的生成器已经支持它,请开始使用。使用仿真不需要 更改,并且 BuildKit 会自动检测 可用于仿真的架构。

注意

使用 QEMU 进行仿真可能比原生构建慢得多,尤其是对于 计算密集型任务,如编译和压缩或解压缩。

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

Docker Desktop 支持在 emulation 的 intent 值。无需配置,因为构建器使用 捆绑在 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

以下命令从名为 和 的 Docker 上下文创建一个多节点构建器。此示例假定您已经添加了 那些背景。node-amd64node-arm64

$ 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 构建器,而无需承担 维护它们。使用 Cloud Builder 还可以提供额外的好处,例如 作为共享构建缓存。

注册 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

交叉编译

根据您的项目,如果您使用的编程语言具有良好的支持 对于交叉编译,您可以利用多阶段构建来构建二进制文件 对于构建器本机架构中的目标平台。特殊构建 参数(如 和 )会自动 可在 Dockerfile 中使用。BUILDPLATFORMTARGETPLATFORM

在以下示例中,该指令固定到本机 platform (使用选项) 防止仿真启动。然后,预定义参数和 build 参数在指令中插入。在 在这种情况下,值只是使用 StdOut 打印到 stdout,但是这个 说明了如何将它们传递给编译器进行交叉编译。FROM--platform=$BUILDPLATFORM$BUILDPLATFORM$TARGETPLATFORMRUNecho

# 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 运行多平台构建 用于编译和导出 Neovim 二进制文件的云 为和平台。linux/amd64linux/arm64

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 构建镜像并使用 Docker Build Cloud:linux/amd64linux/arm64

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

    此命令使用 Cloud Builder 构建镜像,并将 二进制文件添加到目录中。bin

  4. 验证二进制文件是否为这两个平台构建。您应该会看到 和 的二进制文件。nvimlinux/amd64linux/arm64

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

交叉编译 Go 应用程序

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

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

在 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

  3. 要添加交叉编译支持,请更新 Dockerfile 以使用 pre-defined 和 build 参数。这些 当您将标志与 .BUILDPLATFORMTARGETPLATFORM--platformdocker build

    • 使用选项将镜像固定到构建器的平台。golang--platform=$BUILDPLATFORM
    • 为 Go 编译阶段添加说明,以使 和 build 参数可用于 这个阶段。ARGTARGETOSTARGETARCH
    • 将 和 环境变量设置为 和 的值。Go 编译器使用这些变量来执行 交叉编译。GOOSGOARCHTARGETOSTARGETARCH

    # 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 .
    

此示例展示了如何为多个 平台。如何进行交叉编译的具体步骤 可能会因您使用的编程语言而异。查阅 编程语言的文档,了解有关交叉编译的更多信息 适用于不同的平台。

提示

您可能还需要考虑查看 xx - Dockerfile 交叉编译帮助程序。 是一个 Docker 镜像,其中包含实用程序脚本,可以更轻松地与 Docker 构建进行交叉编译。xx