西西软件园多重安全检测下载网站、值得信赖的软件下载站!
软件
软件
文章
搜索

首页 → 业内动态 → 业内资讯 → ARM 服务器实测:Docker 镜像换架构就崩,原因在这 3 处

ARM 服务器实测:Docker 镜像换架构就崩,原因在这 3 处

相关软件相关文章发表评论 来源:本站整理时间:2026/11/5 1:07:25字体大小:A-A+

作者:xiix点击:0次评论:0次标签: ARM架构 Docker镜像 跨架构编译 容器化部署 国产化适配

  • 类型:远程监控大小:198.1M语言:中文 评分:10.0
  • 标签:
立即下载

上个月公司要做信创改造,业务系统整体往国产 ARM 服务器(鲲鹏 920 / 飞腾 S2500 这类 aarch64 平台)上迁。我的机器还是原来那台 x86_64 的云主机,于是顺手把 Dockerfile 推上去跑了个遍构建过程没报错,容器也起来了,结果 docker ps 一看,状态直接是 Exited (126),日志里一行 exec format error。

这个问题说实话不新鲜,但它坑的地方在于「构建通过、运行失败」这个组合。镜像能拉、能存、能导出,看着一切正常,只有真正 exec 那个二进制的那一刻才炸。而且报错信息短得不像话,很多人第一反应是「镜像坏了」或者「权限不对」,其实方向完全错了。

如果你正在把服务往 ARM 服务器上迁,或者需要在国产化环境里把现有镜像跑起来,先看看这几家的裸金属与物理服务器报价和技术说明,选一台机器先把编译环境搭起来往往比反复换镜像省事:zji(独立服务器 / 裸金属)

先分清架构:uname -m 那行字才是真的

排查的第一步永远是确认目标机器的真实架构,不要相信控制台面板上写的,也别信云厂商镜像列表里的标签,直接在机器上敲:

uname -m

输出 x86_64 就是 Intel/AMD 生态,输出 aarch64(老内核里叫 arm64)才是 ARM。国产 ARM 服务器基本都是 aarch64。

然后把镜像的架构也查出来,注意命令里那个 --format 参数不能少:

docker image inspect 镜像名:标签 --format '{{.Architecture}}/{{.Os}}'

如果这里出来的是 amd64/linux,而目标机器是 aarch64/linux,那问题基本就定位了。ELF 文件头里的 machine 字段决定了能不能被当前内核执行:x86_64 的 ELF 头是 0x3E,aarch64 的是 0xB7,内核在 execve 的时候做了一次比对,不匹配就直接返回 ENOEXEC,翻译成人话就是 exec format error。

ARM 架构服务器与 x86 容器镜像架构不匹配示意图

原因一:基础镜像本身就是 x86 的

最常见的一种。写 Dockerfile 时习惯性用了 FROM node:18、FROM python:3.11 这类标签,没带架构后缀。Docker Hub 上这些官方镜像绝大多数同时维护了 amd64 和 arm64 两个版本,理论上拉的时候会自动挑对应架构的那个但前提是你的 Docker 客户端和容器运行时确实支持。

我那次出问题,是因为 Dockerfile 里有一行 FROM registry.internal/base:1.4,这是我们自己的私有镜像,用 Dockerfile FROM 指令构建出来的,中间某次 CI 只在 x86 机器上 build 了一次,产物里就固化成 amd64 了。这种「内网基础镜像」是最容易漏掉的,因为它的标签看着人畜无害。

解决办法有两种。一种是显式指定:

FROM --platform=linux/arm64 registry.internal/base:1.4

另一种更彻底,直接用 buildx 出多架构镜像,一次构建两个平台都带上:

docker buildx build --platform linux/amd64,linux/arm64 -t 你的仓库/app:1.0 --push .

如果 buildx 报「multiple platforms feature is currently not supported」,说明 Docker 版本太老或者没装 buildx 插件,先 docker buildx version 确认一下。

Docker buildx 多架构镜像构建流程图

原因二:多架构清单(manifest list)被压成了单架构

这个坑更隐蔽,也是我查了最久的那个。

带 manifest list 的镜像,导出的时候如果你用的是 docker save 而不是 docker buildx imagetools create 加 --push,很容易只导出一个平台的镜像文件。我把镜像 docker save 成 tar 包传到 ARM 机器上 docker load,结果 docker image inspect 显示出来还是 amd64load 的时候 Docker 根本没做架构匹配,它只负责把 layers 解出来塞进本地存储。

更坑的是,如果你用 docker pull 拉一个多架构镜像,Docker 会根据客户端所在机器的架构去选对应 manifest。问题在于客户端和真正跑容器的 daemon 可能不是同一台CI 机器是 x86,ARM 服务器上的 daemon 是 aarch64,pull 的时候按 x86 选了一个 amd64 的 manifest,再往 ARM 节点上推过去,自然对不上。

验证方法很直接,列出这个镜像到底带几个平台:

docker buildx imagetools inspect 你的仓库/app:1.0

正常应该输出 Manifests: 2,下面分别是 linux/amd64 和 linux/arm64。如果只有一个,多半是构建流程里少了 --push,或者中间某个 registry 代理把 manifest list 拍平了。

Docker 镜像 manifest list 多架构清单对比

原因三:依赖需要原生编译,ARM 上没有现成的二进制

前两个原因解决之后,我以为完事了,结果又撞上一个:某个 Python 依赖在启动时报 Illegal instruction,还有一个 Node 项目直接构建失败。

这类问题的共性是:镜像架构对了,但里面某个第三方库没有 arm64 的预编译产物。常见的有这么几种:

1. Python 里有 C 扩展的包(cryptography、numpy、lxml、grpcio 这类),pip 在 ARM 上找不到对应的 wheel,就会退回源码编译。源码编译本身不是问题,但 ARM 机器上经常缺 gcc、python3-dev、libffi-dev,于是构建直接失败。

2. Node 的原生模块(node-gyp 那一套),需要 node-gyp、python3、make、g++ 齐全,缺一个就 fail。

3. 少数老库压根就没做 aarch64 支持,源码里有 x86 专属的汇编(比如 cpuid 指令),编出来一跑就 Illegal instruction。这种只能换实现,或者用 QEMU 模拟跑 x86 版本。

我的处理办法是在 Dockerfile 里补齐编译链,做成一个多阶段构建的 builder 阶段,最后只把产物拷进精简的运行镜像:

FROM --platform=linux/arm64 python:3.11-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends build-essential python3-dev && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir -r requirements.txt

另外提醒一句:国产化环境下包管理器可能是 yum/dnf 而不是 apt,麒麟、统信这些系统还有自己的包名差异。我那次在麒麟机器上装 openssl-devel 就是踩了坑,国产系统里叫 openssl-devel 或 libssl-dev,得按目标系统单独写一份安装脚本,别试图用一份 Dockerfile 打天下。

顺带说说 QEMU 模拟:能跑,但别当生产方案

实在没有 ARM 硬件时,可以用 QEMU 用户态模拟跑 x86 镜像:

docker run --platform linux/amd64 -e QEMU_EMULATE=1 你的镜像

配合 tonistiigi/binfmt 那个镜像注册一下 binfmt_misc,Docker 就能自动识别。这种方式适合临时验证,别用于生产模拟执行的性能损失通常在 5 到 20 倍,一个 Go 服务的启动时间能从 200 毫秒变成 3 秒都是常事。我拿它跑过一次完整的构建流程,纯粹是救急,真上线还是得在真 ARM 机器上跑一遍。

ARM 服务器上容器部署的检查清单示意图

最后留一份排查清单

整个过程我整理成了四步,下次再遇到类似问题可以按顺序走:

1. uname -m 确认目标机器架构,别信控制台标签。

2. docker image inspect --format '{{.Architecture}}' 确认镜像架构。

3. docker buildx imagetools inspect 确认 manifest list 是否完整。

4. 报错是 Illegal instruction 就往原生依赖方向查,报错是 Exec format error 就往镜像架构方向查,两者定位完全不同,别混着浪费时间。

整个迁移从踩坑到跑通大概花了三天,其中一半时间在排查那个 Illegal instruction 上。现在想想,最开始真该做的一步就是先把架构这件事写进 CI 检查项里每次构建完自动 inspect 一下架构标签是否匹配目标平台,能省下后面大半天试错。

如果你也在做这类跨架构迁移,多架构基础镜像的选择、目标机器的机型配置、以及能不能直接上物理机做验证,这三件事建议一起考虑,别等迁移卡住了再回头换机器。需要远程登录多台 ARM 服务器做验证的话,lightlayer(独立物理服务器 / 多种机型与配置) 这类可以按预算选机型的方案也可以纳入比较。

    相关评论

    阅读本文后您有什么感想? 已有人给出评价!

    • 8 喜欢喜欢
    • 3 顶顶
    • 1 难过难过
    • 5 囧囧
    • 3 围观围观
    • 2 无聊无聊

    发表评论

    昵称:
    表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲