共计 6771 个字符,预计需要花费 17 分钟才能阅读完成。
写在前面
随着 ARM 架构服务器的普及和容器化部署的发展, 我们经常需要为不同架构构建镜像。以前这个过程相当繁琐, 需要在不同架构的机器上分别构建。Docker 19.03 引入的 Buildx 插件彻底改变了这个局面, 让跨平台构建变得简单高效。
环境准备
安装 Docker
如果你的系统还没有安装 Docker, 可以使用这个一键安装脚本:
bash <(curl -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/DockerInstallation.sh)
检查 Buildx 是否已安装
Docker 19.03 及以上版本通常已经内置了 Buildx 插件。首先检查当前是否已安装:
docker buildx version
如果能看到版本号输出, 说明已经安装, 可以跳过下面的安装步骤, 直接进入 "配置跨架构模拟环境" 部分。
安装 Buildx 插件(仅当未安装时)
方式一: 通过包管理器安装(推荐)
这是官方推荐的安装方式, 可以自动接收安全更新:
# Debian/Ubuntu 系统
sudo apt-get update
sudo apt-get install docker-buildx-plugin
# RHEL/CentOS 系统
sudo yum install docker-buildx-plugin
# Fedora 系统
sudo dnf install docker-buildx-plugin
Docker Desktop 用户: macOS 和 Windows 用户使用 Docker Desktop 时已经内置 Buildx, 无需额外安装。
方式二: 二进制安装
从 Docker 官方 GitHub 仓库下载最新版 buildx(替换版本号和架构为你的实际情况):
wget https://github.com/docker/buildx/releases/download/v0.30.1/buildx-v0.30.1.linux-amd64
安装并配置二进制文件
# 创建插件目录(确保存在)sudo mkdir -p /usr/libexec/docker/cli-plugins/
# 移动二进制文件,添加执行权限
sudo mv docker-buildx /usr/libexec/docker/cli-plugins/docker-buildx
sudo chmod +x /usr/libexec/docker/cli-plugins/docker-buildx
# 验证文件权限
ls -l /usr/libexec/docker/cli-plugins/docker-buildx
让 Docker 默认使用 buildx 进行构建:
# 启用 buildx 为默认构建器
docker buildx install
# 验证安装
docker buildx version
配置跨架构模拟环境
macOS 和 Windows 的 Docker Desktop 已经内置了跨架构支持, 可以跳过这一步。Linux 用户需要手动配置 QEMU 模拟器:
docker run --privileged --rm tonistiigi/binfmt --install all
这个命令会安装 QEMU 模拟器并配置 binfmt_misc, 让系统能够识别和执行不同架构的二进制文件。
验证配置:
[root@centos ~]# ls -al /proc/sys/fs/binfmt_misc/
total 0
drwxr-xr-x 2 root root 0 Jan 6 11:17 .
dr-xr-xr-x 1 root root 0 Nov 20 09:23 ..
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-aarch64
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-arm
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-loongarch64
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-mips64
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-mips64el
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-ppc64le
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-riscv64
-rw-r--r-- 1 root root 0 Jan 6 11:17 qemu-s390x
--w------- 1 root root 0 Jan 6 11:17 register
-rw-r--r-- 1 root root 0 Jan 6 11:17 status
能看到这些 qemu-* 文件就说明配置成功。
实战场景
场景一: 提取构建产物
在多阶段构建中, 我们经常需要从中间阶段提取编译好的二进制文件。这里有个技巧。
先看一个简单的 Go 程序构建:
FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go
错误做法
直接导出 build 阶段:
docker buildx build --target build --output ./test1 .
这样会把整个 golang:1.21 基础镜像的文件系统都导出来:
bad-output/
├── bin/ (bash, sh, 等系统命令)
├── usr/ (系统库和工具)
├── lib/ (系统库)
├── go/ (整个 Go 工具链,几百 MB)
├── app/
│ └── myapp ← 你真正想要的文件
└── ... 还有更多系统文件
我们真正需要的只是那个小小的 myapp 可执行文件。
正确做法
使用 FROM scratch 作为过滤层:
FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go
FROM scratch AS export
COPY --from=build /app/myapp /myapp
scratch 是 Docker 官方提供的空镜像, 它不是一个实际存在的镜像, 而是一个特殊的标记, 代表完全空的文件系统。通过它我们可以精确控制导出的内容:
docker buildx build --target export --output ./good-output .
这次输出目录就只有需要的文件了:
good-output/
└── myapp ← 完美!只有这一个文件
多阶段构建的意义在于: 构建阶段的所有内容 (Go 编译器、系统文件等) 仅用于编译, 不会进入最终输出。
高级技巧: 自定义目录结构
如果需要组织更复杂的输出结构:
FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go
FROM scratch AS organized
COPY --from=build /app/myapp /bin/myapp
COPY --from=build /app/main.go /src/main.go
执行导出:
docker buildx build --target organized --output ./organized-output .
导出后会得到规范的目录结构:
organized-output/
├── bin/
│ └── myapp
└── src/
└── main.go
场景二: 多架构构建
这是 Buildx 最强大的功能。准备好 Dockerfile:
FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go
FROM scratch AS multi-arch
COPY --from=build /app/myapp /myapp
尝试执行多架构导出:
docker buildx build
--platform linux/amd64,linux/arm64
--target multi-arch
--output ./dist
.
你会遇到这个错误:
[root@centos sun]# docker buildx build
> --platform linux/amd64,linux/arm64
> --target multi-arch
> --output ./dist
> .
[+] Building 0.0s (0/0) docker:default
ERROR: Multi-platform build is not supported for the docker driver.
Switch to a different driver, or turn on the containerd image store, and try again.
Learn more at https://docs.docker.com/go/build-multi-platform/
这是因为默认的 docker 驱动只能构建与宿主机架构一致的镜像, 多平台构建需要使用 docker-container 驱动。
创建多平台构建器
# 创建构建器配置目录
mkdir -p /etc/docker/buildx
# 配置私有仓库(如果有的话)
cat > /etc/docker/buildx/config.toml << EOF
[registry."192.168.100.189"]
http = true
insecure = true
EOF
# 创建新的构建器
docker buildx create
--name multiarch-builder
--driver docker-container
--config /etc/docker/buildx/config.toml
--use
# 查看构建器状态
docker buildx ls
输出结果:
[root@centos sun]# docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
multiarch-builder* docker-container
_ multiarch-builder0 _ unix:///var/run/docker.sock inactive
default docker
_ default _ default running v0.13.2 linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
注意 multiarch-builder 的状态是 inactive。这是因为 docker-container 驱动的构建器本质是一个后台运行的 BuildKit 容器, 创建后默认不会自动启动。
启动并初始化构建器:
docker buildx inspect multiarch-builder --bootstrap
同时确保 QEMU 模拟器已安装:
docker run --privileged --rm tonistiigi/binfmt --install all
安装输出:
[root@centos sun]# docker run --privileged --rm tonistiigi/binfmt --install all
Unable to find image 'tonistiigi/binfmt:latest' locally
latest: Pulling from tonistiigi/binfmt
f4700b809f99: Pull complete
2adec5d296ac: Pull complete
Digest: sha256:30cc9a4d03765acac9be2ed0afc23af1ad018aed2c28ea4be8c2eb9afe03fbd1
Status: Downloaded newer image for tonistiigi/binfmt:latest
installing: mips64 OK
installing: loong64 OK
installing: arm64 OK
installing: arm OK
installing: ppc64le OK
installing: riscv64 OK
installing: s390x OK
installing: mips64le OK
{
"supported": [
"linux/amd64",
"linux/amd64/v2",
"linux/amd64/v3",
"linux/amd64/v4",
"linux/arm64",
"linux/riscv64",
"linux/ppc64le",
"linux/s390x",
"linux/386",
"linux/mips64le",
"linux/mips64",
"linux/loong64",
"linux/arm/v7",
...
]
}
输出方式对比
1. 导出到本地文件系统
现在重新执行多架构构建:
docker buildx build
--platform linux/amd64,linux/arm64
--target multi-arch
--output ./dist
.
输出结果:
[root@centos sun]# tree dist/
dist/
├── linux_amd64
│ └── myapp
└── linux_arm64
└── myapp
这种方式适合需要手动分发二进制文件的场景。
2. 加载到本地 Docker(仅支持单架构)
注意:--load 只支持单架构构建。
构建 amd64 版本:
docker buildx build
--platform linux/amd64
-t myimage:amd64
--load
.
查看结果:
[root@centos sun]# docker images | grep myimage
myimage amd64 dbeefa0220ea 7 minutes ago 2.54MB
构建 arm64 版本:
docker buildx build
--platform linux/arm64
-t myimage:arm64
--load
.
查看结果:
[root@centos sun]# docker images | grep myimage
myimage arm64 1ad9de4d7e24 6 minutes ago 2.51MB
myimage amd64 dbeefa0220ea 7 minutes ago 2.54MB
这种方式适合本地测试不同架构的镜像。
3. 推送到镜像仓库(支持多架构)
这是最常用的方式:
docker buildx build
--platform linux/amd64,linux/arm64
-t 192.168.100.189/test/myimage:v1
--push
.
一次推送就包含了两个架构的镜像。镜像仓库 (如 Harbor) 只会显示一个镜像条目, 但它实际上是一个 manifest list, 包含了多个架构的镜像索引。这是完全正常的现象。
4. 导出为 OCI 归档
docker buildx build
--platform linux/amd64,linux/arm64
-t myimage:v3
--output type=oci,dest=./myimage.tar
.
# 查看导出的文件
ls -lh myimage.tar
这种方式适合离线分发或迁移场景。
实践经验
- 版本要求: 确保 Docker 版本不低于 19.03。现代版本已经默认集成 Buildx, 无需额外配置。
- 构建器状态管理:
docker-container驱动的构建器是个独立容器, 重启系统后需要重新 bootstrap。 - 缓存优化: Buildx 的缓存机制很强大, 合理利用可以大幅提升构建速度。推荐使用
--cache-from和--cache-to参数。 - 私有仓库配置: 如果使用私有仓库, 一定要在创建构建器时配置好
config.toml, 否则推送时会失败。 - 架构选择: 不是所有基础镜像都支持所有架构。构建前最好先确认基础镜像支持你需要的目标架构。
- 性能考虑: 跨架构构建需要 QEMU 模拟, 速度会比原生构建慢很多。生产环境建议使用原生架构的 CI/CD runner。
- 安全更新: 生产环境优先通过包管理器安装 Buildx, 可以自动接收安全补丁。
总结
Buildx 让多架构镜像构建变得简单可控。掌握了这些技巧, 无论是导出构建产物还是构建多平台镜像, 都能游刃有余。建议在实际项目中多实践, 找到最适合自己场景的工作流。