ARM64 主机构建 Docker 镜像时,/bin/sh 报 exec format error
ARM64 Linux 主机构建镜像时,第一条 RUN 就报 /bin/sh: exec format error,可先核对构建节点、基础镜像平台与模拟能力,再选择原生 ARM64 或明确的 amd64 构建路径。
同一份 Dockerfile 在 x86-64 主机可以构建,换到 ARM64 Linux 主机后,第一条 RUN apt-get update 就失败:
exec /bin/sh: exec format error
failed to solve: executor failed running [/bin/sh -c apt-get update]: exit code: 1
这段日志首先指向 /bin/sh 无法执行。此时还没进入正常的 apt 更新过程,换软件源通常无法解决这一层错误。应先核对基础镜像的平台、实际构建节点,以及构建是否依赖跨架构模拟。
下面针对 Linux 容器和 Buildx 构建流程,尤其是 ARM64 节点使用了 amd64 基础镜像的情况。exec format error 并不只由架构不匹配引起;若镜像平台一致,或失败对象是自己添加的脚本,需要另查文件格式、解释器及实际构建产物,不能只凭这一行报错就认定架构问题。
apt 命令之前,还要先启动镜像里的 shell
Linux Dockerfile 的 shell 形式 RUN 默认通过 /bin/sh -c 执行,除非配置另一个 SHELL。这个 shell 来自当前构建阶段的基础镜像,不是宿主机上那个可用的 shell。
例如,下面的结构并不是从空环境构建所有软件,而是在指定镜像上继续执行命令:
FROM registry.example.com/team/base:release
RUN apt-get update
镜像地址是占位值,只有基础镜像采用 apt 时,这条更新命令才合适。如果基础镜像中的 /bin/sh 是 amd64 可执行文件,而构建节点是 ARM64 且没有可用的对应模拟支持,shell 就可能在启动时失败。
容器封装了用户空间依赖,不会自动转换其中机器码的架构。给镜像换名字、加标签,或者把 RUN apt-get update 换成另一条 shell 命令,都没有改变这个前提。
对齐构建节点与基础镜像的平台
先查看 Docker 服务端信息和构建器列表:
docker info
docker buildx ls
docker info 的服务端部分包含架构信息;docker buildx ls 列出构建节点的端点与平台。采用远程构建器时,真正执行 RUN 的可能是另一台机器,所以不能只看当前电脑的 CPU。
确认本次实际选中的构建器,以及调用命令是否另外指定了 --builder。若节点离线或平台信息无法读取,应先解决连接问题,不把缺失信息当成“不支持 ARM64”的结论。输出可能带有内部主机名和端点,对外分享前先脱敏。
然后检查 Dockerfile 中每一个相关阶段的 FROM,记录完整标签或 digest,包括经由构建参数传入的镜像引用。查询仓库中的镜像信息:
docker buildx imagetools inspect registry.example.com/team/base:release
用自己的基础镜像替换占位地址。如果结果是多平台索引,核对是否包含目标 linux/arm64。如果是单平台镜像,需要继续确认其镜像配置中的架构;不能因为没有列出多项 Platform 就当成查询失败。
已在当前 Docker Engine 镜像存储中的基础镜像,也可以查看本地元数据:
docker image inspect registry.example.com/team/base:release
关注 Os、Architecture 等字段,并区分“本地已保存的镜像”和“仓库当前标签指向的镜像”。两者可能不是同一份内容;本地不存在时,该命令的失败不能证明仓库没有 ARM64 版本。
还要检查构建命令及 FROM --platform=... 是否固定了另一平台。构建命令的 --platform=linux/arm64 会为没有自行指定平台的 FROM 选择目标平台,不会覆盖每一处显式的平台约束,也不会为仓库凭空生成缺失的 ARM64 镜像。
原生 ARM64 部署,优先采用匹配的基础镜像
如果应用计划部署到 ARM64,优先使用同一基础镜像产品中受支持、包含 ARM64 的发布版本。某些仓库把不同架构分成独立标签,另一些仓库用同一个标签发布多平台索引,应以当前实际查询结果为准,不根据标签名字猜架构。
换基础镜像前保存 Dockerfile、构建参数和原镜像引用,确认运行时版本、系统库、包管理器与应用依赖兼容。不要为了消除架构报错,直接把专用运行时镜像换成一个缺少所需组件的空白系统镜像。
确认选定构建器支持 ARM64,并修正基础镜像后,可以在隔离的构建环境中生成一个新的测试标签:
docker buildx build \
--platform linux/arm64 \
--progress plain \
--load \
-t local-app:arm64-check .
该命令会实际执行当前目录 Dockerfile 中的构建步骤,并把单平台结果加载到本地 Docker Engine。应保留项目原本必要的 -f、构建参数与上下文路径;示例中的 . 只有在它确实是项目构建上下文时才适用。使用新的测试标签,避免覆盖正在使用的镜像标签。
--load 用于单平台结果,不代表镜像已经发布到仓库,也不代表生产容器已经更新。若应用中还复制了宿主机预先编译的二进制文件,那些文件也必须适配目标平台;基础镜像正确,并不能替它们重新编译。
必须构建 amd64 时,检查的是模拟能力
有时目标部署平台本来就是 amd64,只是构建机器换成了 ARM64。这时应保留 amd64 目标,采用能够执行 amd64 构建步骤的原生节点或已配置的模拟环境。
docker buildx build \
--platform linux/amd64 \
--progress plain \
--load \
-t local-app:amd64-check .
执行前要确认选定构建器支持该平台。--platform 表达目标,不负责自动解决所有模拟器安装和注册问题。即使构建成功,得到的 amd64 镜像若要在 ARM64 上运行,也仍需要对应的运行时模拟支持。
Docker Desktop 与独立 Linux Engine、不同来源的 BuildKit 包,其模拟支持不能混为一谈。当前官方 BuildKit 发行版通常附带用户态模拟器;不要把旧环境中“安装某个 QEMU 包后成功”的办法推广成所有系统的必做步骤。
如果确实缺少跨架构执行能力,优先使用匹配架构的构建节点;需要补齐模拟时,在专门的构建环境中按所用发行版和 BuildKit 的要求配置。注册宿主机二进制格式处理器涉及系统级变更,不应为了一个构建错误直接在业务宿主机运行来历不明的特权容器。模拟构建还可能明显慢于原生构建,尤其是编译与压缩阶段。
验收不要停在第一条 RUN 通过
修正后先看完整构建日志,确认 /bin/sh 已能执行,后续依赖安装和应用构建也完成。若报错变成 apt 仓库、包名或依赖错误,那是后续阶段的问题,应按新错误继续处理。
再检查新镜像的平台:
docker image inspect local-app:arm64-check
如果走的是 amd64 路径,相应替换标签。元数据检查通过后,还需在目标平台的测试环境中完成应用启动和实际功能检查,确认没有混入另一架构的原生扩展、可执行文件或依赖。不能把某一次构建成功直接当成跨平台部署成功。
需要撤回时,恢复保存的 Dockerfile、构建参数与原镜像引用。原配置若只能在 amd64 节点构建,应回到匹配的节点使用,不能指望恢复文件后就在 ARM64 原生执行。整个排查不需要清空 Docker 数据目录、删除业务卷或重建生产实例。


