多平台

到目前为止,您已经构建了 Linux 二进制文件。本节介绍如何通过仿真和交叉编译使用多平台构建来支持其他操作系统和体系结构。

开始构建多个平台的最简单方法是使用仿真。通过仿真,您可以将应用程序构建到多个架构,而无需对 Dockerfile 进行任何更改。您需要做的就是将--platform标志传递给构建命令,指定您想要构建的操作系统和体系结构。

以下命令为linux/arm/v7平台构建服务器映像:

$ docker build --target=server --platform=linux/arm/v7 .

您还可以使用仿真同时为多个平台生成输出。但是,Docker Engine 中的默认镜像存储不支持构建和加载多平台镜像。您需要启用支持并发多平台构建的containerd镜像存储。

启用containerd镜像存储


要在 Docker Desktop 中启用 Containerd 映像存储,请转到“设置”并 在“常规”选项卡中选择“使用 Containerd 来拉取和存储映像”

请注意,更改映像存储意味着您将暂时无法访问经典映像存储中的映像和容器。这些资源仍然存在,但要查看它们,您需要禁用 containerd 映像存储。

如果您不使用 Docker Desktop,请通过将以下功能配置添加到/etc/docker/daemon.json配置文件来启用 containerd 映像存储。

{
  "features": {
    "containerd-snapshotter": true
  }
}

更新配置文件后重新启动守护进程。

$ systemctl restart docker

使用仿真构建

要运行多平台构建,请调用该docker build命令,并向其传递与之前相同的参数。只是这一次,还添加一个--platform指定多个架构的标志。

$ docker build \
    --target=binaries \
    --output=bin \
    --platform=linux/amd64,linux/arm64,linux/arm/v7 .

此命令使用仿真来运行相同的构建三次,每个平台一次。构建结果导出到bin目录。

bin
├── linux_amd64
│   ├── client
│   └── server
├── linux_arm64
│   ├── client
│   └── server
└── linux_arm_v7
    ├── client
    └── server

当您同时针对多个平台进行构建时,BuildKit 会在模拟下为您指定的每个平台运行所有构建步骤。有效地将构建分叉为多个并发进程。

使用仿真构建管道

然而,使用仿真运行多平台构建有一些缺点:

  • 如果您尝试运行上面的命令,您可能已经注意到它需要很长时间才能完成。对于 CPU 密集型任务,仿真可能比本机执行慢得多。
  • 仅当您使用的基础映像支持架构时,模拟才有效。本指南中的示例使用 Alpine Linux 版本的映像 golang,这意味着您只能以这种方式针对有限的 CPU 架构集构建 Linux 映像,而无需更改基础映像。

作为模拟的替代方案,下一步将探索交叉编译。交叉编译使多平台构建速度更快、用途更广。

使用交叉编译构建

使用交叉编译意味着利用编译器的功能来构建多个平台,而不需要模拟。

您需要做的第一件事是固定构建器以使用节点的本机架构作为构建平台。这是为了防止仿真。然后,构建器从节点的本机架构将应用程序交叉编译到许多其他目标平台。

平台构建参数

此方法涉及使用一些您可以在 Docker 构建中访问的预定义​​构建参数:BUILDPLATFORMTARGETPLATFORM(以及衍生物,例如TARGETOS)。这些构建参数反映了您传递给标志的值--platform

例如,如果您使用 调用构建--platform=linux/amd64,则构建参数解析为:

  • TARGETPLATFORM=linux/amd64
  • TARGETOS=linux
  • TARGETARCH=amd64

当您向平台标志传递多个值时,会为每个平台自动分叉使用预定义平台参数的构建阶段。这与在模拟下运行的构建形成鲜明对比,在仿真下,整个构建管道在每个平台上运行。

使用交叉编译构建管道

更新 Dockerfile

要使用交叉编译技术构建应用程序,请按如下方式更新 Dockerfile:

  • 添加--platform=$BUILDPLATFORMFROM初始 base阶段的指令,固定镜像的平台golang以匹配主机的架构。
  • 添加ARGGo 编译阶段的指令,使TARGETOSTARGETARCHbuild 参数可供此阶段的命令使用。
  • GOOS和环境变量设置为 和GOARCH的值。 Go编译器使用这些变量来进行交叉编译。TARGETOSTARGETARCH
  # syntax=docker/dockerfile:1
  ARG GO_VERSION=1.21
  ARG GOLANGCI_LINT_VERSION=v1.52
- FROM golang:${GO_VERSION}-alpine AS base
+ FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base
  WORKDIR /src
  RUN --mount=type=cache,target=/go/pkg/mod \
      --mount=type=bind,source=go.mod,target=go.mod \
      --mount=type=bind,source=go.sum,target=go.sum \
      go mod download -x

  FROM base AS build-client
+ ARG TARGETOS
+ ARG TARGETARCH
  RUN --mount=type=cache,target=/go/pkg/mod \
      --mount=type=bind,target=. \
-     go build -o /bin/client ./cmd/client
+     GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /bin/client ./cmd/client

  FROM base AS build-server
+ ARG TARGETOS
+ ARG TARGETARCH
  RUN --mount=type=cache,target=/go/pkg/mod \
      --mount=type=bind,target=. \
-     go build -o /bin/server ./cmd/server
+     GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /bin/server ./cmd/server

  FROM scratch AS client
  COPY --from=build-client /bin/client /bin/
  ENTRYPOINT [ "/bin/client" ]

  FROM scratch AS server
  COPY --from=build-server /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

  FROM scratch AS binaries
  COPY --from=build-client /bin/client /
  COPY --from=build-server /bin/server /

  FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION} as lint
  WORKDIR /test
  RUN --mount=type=bind,target=. \
      golangci-lint run

现在唯一要做的就是运行实际的构建。要运行多平台构建,请设置该--platform选项,并指定要为其构建的操作系统和体系结构的 CSV 字符串。以下命令说明了如何为 Mac (ARM64)、Windows 和 Linux 构建和导出二进制文件:

$ docker build \
  --target=binaries \
  --output=bin \
  --platform=darwin/arm64,windows/amd64,linux/amd64 .

构建完成后,您将在目录中找到所有选定平台的客户端和服务器二进制文件bin

bin
├── darwin_arm64
│   ├── client
│   └── server
├── linux_amd64
│   ├── client
│   └── server
└── windows_amd64
    ├── client
    └── server

概括

本节演示了如何开始使用模拟和交叉编译进行多平台构建。

相关信息:

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

下一步

本节是使用 Docker 构建指南的最后部分。以下页面包含一些下一步操作的指示。