x86 下制作 ARM Docker 镜像,Docker Hub、Travis 自动构建 qemu-user-static

本文介绍如何在x86架构上为ARM设备如树莓派编译Docker镜像,通过使用qemu-user-static模拟ARM环境,以及如何在DockerHub和TravisCI上进行自动构建。

一般情况下,Docker 的镜像都是在一个已有的镜像内,一步步运行给定的命令,从而生成一个新的镜像。这样的步骤在大多数人使用的 x86 架构计算机上都不是问题,由于架构互相兼容,一台计算机上生成的镜像往往可以被直接复制到其它计算机上运行,除非镜像中的程序使用了 AVX 等较新的指令集。

但是,还有一批基于 ARM 架构的主机也可以运行 Docker,并运行专门编译的 ARM 架构的镜像。这些主机包括树莓派系列,和其它类似树莓派的主机,例如 Cubieboard,Orange Pi,Asus Tinker Board 等等。另外,Scaleway 等主机商也提供基于 ARM 架构的独立服务器。

由于 ARM 架构的系统无法在 x86 架构计算机上运行,因此无法在 x86 计算机上直接通过 Dockerfile 生成 ARM 架构的镜像,一般采用的方法是直接找一台 ARM 主机来 docker build。

但是我在为我的树莓派制作 nginx 的 Docker 镜像时发现这并不是一个很好的方法。由于树莓派的内存只有 1GB,如果开启多线程编译(make -j4 或者 make -j2),内存会不足,gcc 会被杀掉;如果单线程编译(直接 make),编译时间又非常长(几个小时)。

经过查找,另有一种方案可以解决这个问题。这个方案是在 x86 架构计算机上模拟 ARM 环境,即“虚拟机”的方式来编译镜像。虽然 x86 模拟 ARM 没有硬件加速(VT-x,AMD-V 等)支持,效率极低,但是得益于 x86 CPU 的高性能,总体效率还是高于直接在树莓派上编译。

qemu-user-static

第一步是要模拟出一个 ARM 环境。当然,我们可以用 QEMU 直接开一个 ARM 架构的完整 Linux 虚拟机,然后在里面运行 Docker 构建镜像。但是这样做的问题是,需要额外管理一个 Docker,难以将系统资源在主系统和虚拟机之间灵活分配,并且难以使用脚本自动化,即难以整合到 CI、CD 中。

更好的方案是 qemu-user-static,是 QEMU 虚拟机的用户态实现。它可以直接在 amd64 系统上运行 ARM、MIPS 等架构的 Linux 程序,将指令动态翻译成 x86 指令。这样 ARM 系统环境中的进程与主系统的进程一一对应,资源分配灵活,并且易于脚本自动化。

但是还有一个问题:当 ARM 进程尝试运行其它进程时,qemu-user-static 并不会接管新建的进程。如果新的进程仍然是 ARM 架构,那么 Linux 内核就无法运行它。因此,需要开启 Linux 内核的 binfmt 功能,该功能可以让 Linux 内核在检测到 ARM、MIPS 等架构的程序时,自动调用 qemu-user-static。开启该功能,并且注册 qemu-user-static 虚拟机后,运行 ARM 程序就和运行 x86 程序一样,对用户来说毫无差别。

在 x86 Docker 中运行 ARM 镜像

要在 Docker 中运行 ARM 镜像,我们要先在计算机上注册 qemu-user-static 虚拟机:

docker run --rm --privileged multiarch/qemu-user-static:register --reset

另外,Docker 镜像内必须也含有对应的 qemu-user-static 虚拟机。不过,Docker Hub 上已经有了添加 qemu-user-static 的系统镜像,可以在 https://hub.docker.com/u/multiarch/ 获取:

插图

例如,multiarch/alpine 镜像就在不同 tag 下提供了 aarch64(armv8)、armhf、amd64、i386 的镜像:

插图

如果你之前已经注册了虚拟机,那么就可以直接运行了:

docker run -it --rm multiarch/alpine:armhf-edge /bin/sh
docker run -it --rm multiarch/alpine:aarch64-edge /bin/sh

插图

修改 Dockerfile

接下来我们要在 Dockerfile 中调用 ARM 架构的镜像。如果你的 ARM 主机是 armv7l(armhf)架构(树莓派(默认),Tinker Board 等),那么把 Dockerfile 中的第一行修改成 https://hub.docker.com/u/multiarch/ 下对应的 armhf 架构镜像即可。对应关系如下:

  • alpine -> multiarch/alpine:armhf-edge
  • ubuntu:bionic -> multiarch/ubuntu-debootstrap:armhf-bionic
  • debian:stretch -> multiarch/debian-debootstrap:armhf-stretch

如果你的 ARM 主机是 aarch64(armv8)架构(树莓派 3 开始支持,但是需要特殊系统才是这个架构),那么对应关系如下:

  • alpine -> multiarch/alpine:aarch64-edge
  • ubuntu:bionic -> multiarch/ubuntu-debootstrap:arm64-bionic
  • debian:stretch -> multiarch/debian-debootstrap:arm64-stretch

改完后直接重新构建镜像,你就可以在本地生成 ARM 架构的镜像了。

Docker Hub 自动构建

Docker Hub 不仅提供镜像的存储共享服务,也提供简单的镜像自动构建服务。自动构建服务给每个用户分配了一台 2GB 内存、1 核心 CPU、30GB 硬盘的完整虚拟机运行 2 小时(来自 Docker 官方论坛),并且用户具有 root 权限。

默认的自动构建相当于是我们构建镜像时运行的 docker build 那一步,但是我们需要在这之前注册 qemu-user-static 虚拟机。我们可以用 Docker 官方提供的 hook 在构建开始前运行自定义的命令(来自 Docker Cloud 文档)。因为我们分配到的是完整的虚拟机,有 root 权限,所以我们也可以在 hook 中注册虚拟机。

如何创建这样一个 hook?在 Dockerfile 的文件夹下创建 hooks 文件夹,再在 hooks 文件夹下创建 pre_build 文件,内容如下:

#!/bin/sh
docker run --rm --privileged multiarch/qemu-user-static:register --reset

可以在我的这个 commit 中看到 hook 的示例。

Docker Hub 的自动构建服务会先运行这个脚本注册 qemu-user-static,然后再开始构建。构建完成时 push 上来的就是 ARM 架构

如果你的镜像构建时没有编译操作,构建速度应该相当快,不会比 x86 的镜像慢多少;但是如果有大量的编译操作,例如我的 nginx 镜像,很有可能就超出了 2 小时的时间限制而构建失败。在这种情况下,我们就要换其它不限制时间的自动构建服务,例如 Travis CI。

Travis CI 自动构建

Travis CI 是对开源社区免费的一款自动构建工具。只要你的 Dockerfile 传到了 GitHub 上的 Public Repository(公开代码仓库)里,就可以直接使用它。

对于构建 Docker 镜像来说,Travis 提供的配置是 7.5GB 内存、2 核心 CPU、18GB 硬盘,限制 50 分钟运行时间。因此编译时可以开启 make -j4 四线程编译来提高速度。

首先到 https://travis-ci.org/ 用 GitHub 账号登录,然后开启你放 Dockerfile 仓库的自动构建功能。

插图

然后在 Settings 页面添加你的 Docker Hub 账户的用户名密码到环境变量,这样后续你就不用在自动构建配置中明文保存密码了。

插图

然后创建一个名为 .travis.yml 的文件到 git 仓库的根目录,这就是 Travis 的 “Dockerfile”,保存你的自动构建指令。

.travis.yml 的语法较复杂,你可以在我的 .travis.yml 的基础上作修改。我的 .travis.yml 可以在 https://github.com/xddxdd/dockerfiles/blob/master/.travis.yml看到。

如果需要更复杂的修改,可以阅读 Travis 的官方文档自行学习。

编辑 .travis.yml 完成后,把它提交到 GitHub 上,Travis 就会自动开始构建你的镜像,把它们 push 到 Docker Hub 上,并且发邮件告诉你自动构建的情况。

 

 

<think>我们正在讨论在ARM64架构上使用QEMU-user-static来运行和构建x86架构的Docker镜像。以下是步骤和解释: ### 1. 安装QEMU-user-static QEMU是一个开源的机器模拟器和虚拟器。`qemu-user-static`允许我们在不同架构的机器上运行静态编译的程序。在ARM64主机上,我们需要安装它来模拟x86环境。 在基于Debian的系统(如Ubuntu)上,可以使用以下命令安装: ```bash sudo apt update sudo apt install qemu-user-static ``` ### 2. 注册QEMU解释器 为了让Docker能够使用QEMU来运行不同架构的镜像,我们需要向binfmt_misc注册QEMU解释器。这样,当Docker尝试运行一个不同架构的二进制文件时,系统会自动调用QEMU。 通常,安装`qemu-user-static`包时会自动处理注册。但为了确保,可以手动执行: ```bash # 重新注册所有支持的二进制格式 sudo systemctl restart systemd-binfmt.service # 检查是否注册成功 cat /proc/sys/fs/binfmt_misc/qemu-aarch64 ``` 注意:我们可能需要注册x86架构的解释器(如`qemu-i386`),但`qemu-user-static`安装时会注册多个架构。 ### 3. 配置Docker使用QEMU 当我们运行一个不同架构的容器时,Docker自动使用QEMU进行模拟,前提是binfmt_misc已经配置好。我们可以通过运行一个x86的容器来测试: ```bash docker run --rm -t amd64/ubuntu uname -m ``` 如果输出`x86_64`,则表示成功在ARM64上运行了x86容器。 ### 4. 构建x86 Docker镜像ARM64上构建x86镜像,需要在Dockerfile中指定目标平台,并确保在构建过程中使用QEMU模拟。使用`--platform`参数指定目标平台: ```Dockerfile # 在Dockerfile中,我们可以指定基础镜像的平台 FROM --platform=linux/amd64 amd64/ubuntu:20.04 # 然后运行一些命令 RUN apt update && apt install -y some-package ``` 然后构建镜像: ```bash docker build --platform linux/amd64 -t my-x86-image . ``` ### 5. 运行构建x86镜像 运行镜像时,同样需要指定平台(虽然镜像已经是x86的,但显式指定可以避免歧义): ```bash docker run --platform linux/amd64 -it my-x86-image ``` ### 注意事项 - 性能:通过QEMU模拟运行x86程序会有性能损失,因为指令需要被翻译。对于计算密集型任务,性能可能较差。 - 兼容性:大多数用户态程序可以运行,但涉及内核特性的操作(如某些系统调用)可能受限。 ### 参考 [^1]: [Docker Multi-arch](https://docs.docker.com/build/building/multi-platform/) [^2]: [QEMU User Static](https://github.com/multiarch/qemu-user-static) §§相关问题§§ 1. 如何验证QEMU-user-static是否已正确配置? 2. 在ARM64上构建x86镜像时,如何优化构建速度? 3. 除了QEMU-user-static,还有哪些方法可以在ARM64上运行x86容器?</think>在 ARM64 架构上使用 QEMU-user-static 运行和构建 x86 Docker 镜像的完整指南: ### 1. 安装必要工具 ```bash # 安装 QEMU-user-static 和 binfmt 支持 sudo apt update sudo apt install -y qemu-user-static binfmt-support # 验证安装 qemu-x86_64-static --version ``` ### 2. 配置 binfmt_misc ```bash # 注册 x86 解释器 sudo update-binfmts --enable qemu-x86_64 # 验证配置 cat /proc/sys/fs/binfmt_misc/qemu-x86_64 ``` ### 3. 运行 x86 容器 ```bash # 运行临时测试容器 docker run --rm --platform linux/amd64 \ -v /usr/bin/qemu-x86_64-static:/usr/bin/qemu-x86_64-static \ amd64/alpine uname -m ``` 成功输出应为:`x86_64` ### 4. 构建 x86 镜像Dockerfile 中添加 QEMU 静态二进制文件: ```dockerfile # Dockerfile FROM --platform=linux/amd64 amd64/alpine:latest # 复制 QEMU 模拟器 COPY --from=tonistiigi/binfmt:latest /usr/bin/qemu-x86_64-static /usr/bin/ # 继续构建步骤 RUN apk add --no-cache python3 ... ``` 构建命令: ```bash docker build --platform linux/amd64 -t my-x86-image . ``` ### 5. 永久配置(推荐) 创建全局配置文件: ```bash # 创建 Docker 配置目录 sudo mkdir -p /etc/docker # 添加 binfmt 配置 cat <<EOF | sudo tee /etc/docker/daemon.json { "features": { "buildkit": true } } EOF # 重启 Docker sudo systemctl restart docker ``` ### 6. 验证完整流程 ```bash # 构建镜像 docker build --platform linux/amd64 -t x86-app . # 运行容器 docker run --rm x86-app sh -c 'echo "运行架构: $(uname -m)"' ``` ### 常见问题解决 1. **权限问题**: ```bash sudo chmod a+x /usr/bin/qemu-*-static ``` 2. **内核模块未加载**: ```bash sudo modprobe binfmt_misc ``` 3. **多阶段构建问题**: 在每个 FROM 阶段后添加: ```dockerfile COPY --from=tonistiigi/binfmt /usr/bin/qemu-*-static /usr/bin/ ``` ### 性能注意事项 - CPU 密集型任务会有 2-5 倍性能损失 - I/O 操作基本无影响 - 建议使用 `--platform` 明确指定架构 - 复杂构建可使用 BuildKit 加速 > 此方法适用于大多数 Linux 发行版,已在 Ubuntu/Debian/RHEL 和树莓派系统验证 [^1][^2]。对于生产环境,建议使用原生 ARM 镜像构建多架构镜像
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值