跨平台

在本指南的这一部分之前,您一直在构建 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 在模拟下为每个指定的平台运行所有构建步骤。有效地将构建分叉到多个并发进程中。

Build pipelines using emulation

但是,使用模拟运行跨平台构建有一些缺点

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

作为模拟的替代方案,下一步将探讨交叉编译。交叉编译使跨平台构建更快、更灵活。

使用交叉编译构建

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

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

平台构建参数

此方法涉及使用几个预定义的构建参数,您可以在 Docker 构建中使用它们:BUILDPLATFORMTARGETPLATFORM(以及衍生参数,如 TARGETOS)。这些构建参数反映您传递给 --platform 标志的值。

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

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

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

Build pipelines using cross-compilation

更新 Dockerfile

要使用交叉编译技术构建应用程序,请更新 Dockerfile,如下所示

  • --platform=$BUILDPLATFORM 添加到初始 base 阶段的 FROM 指令中,将 golang 镜像的平台固定到与主机机器的架构匹配。
  • 为 Go 编译阶段添加 ARG 指令,使 TARGETOSTARGETARCH 构建参数可用于此阶段中的命令。
  • GOOSGOARCH 环境变量设置为 TARGETOSTARGETARCH 的值。Go 编译器使用这些变量进行交叉编译。
  # syntax=docker/dockerfile:1
  ARG GO_VERSION=1.21
  ARG GOLANGCI_LINT_VERSION=v1.59
- 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 构建指南的最后一部分。下一页包含一些关于下一步去哪里的提示。