Docker 构建(docker build)失败时如何调试?
调试构建失败五步走:读报错定位失败层、用 --progress=plain 看完整输出、--no-cache 排除缓存干扰、将失败前内容单独构建为命名阶段,再起临时容器手动执行、检查 .dockerignore 与上下文。本文详解每一步。
调试思路:构建失败可能来自指令执行、语法解析、上下文、网络或存储等阶段,不一定有命令退出码。按顺序做:①仔细看报错中失败的指令与输出;②--progress=plain 拿完整日志;③--no-cache 排除缓存干扰;④利用缓存到失败点之前,起一个临时容器手动执行失败命令交互排查;⑤检查构建上下文与 .dockerignore。
第一步:读报错信息
RUN 命令失败时,报错通常会写明指令和退出码:
ERROR: process "/bin/sh -c apt-get install -y curl" did not complete successfully: exit code: 100
退出码上方那段输出才是真正的错误(如包不存在、网络超时)。
第二步:拿完整输出
BuildKit 默认的进度输出会折叠日志,加参数看全文:
docker build --progress=plain -t myapp .
第三步:排除缓存干扰
缓存层可能掩盖或引入问题:
docker build --no-cache --pull -t myapp .
--no-cache 不复用普通构建层,但不会自动清空 cache mount 内容;--pull 请求检查基础镜像的新版本。
第四步:进临时容器手动复现(杀手锏)
利用缓存——失败点之前的层都是现成的。BuildKit 日志中的缓存记录 ID 不保证可直接运行。可把失败点之前的内容命名为 debug-base 阶段,再单独构建:
# Dockerfile 中将失败前内容组织为 debug-base 阶段
# 失败命令放入后续 FROM debug-base AS final 阶段
docker buildx build --load --target debug-base -t myapp:debug .
docker run --rm -it --entrypoint /bin/sh myapp:debug
# 容器里手动执行失败的命令,交互式排查
apt-get install -y curl
运行时容器不等于构建沙箱,构建参数、secret/cache mounts、网络和平台配置仍需对应核对;镜像也必须包含所用 shell。
第五步:检查上下文与常见原因
| 常见原因 | 表现 | 处理 |
|---|---|---|
| COPY 的文件不在上下文 | not found |
确认文件在构建上下文内,查 .dockerignore 是否误排除 |
| 网络/源问题 | apt/pip 超时 | 换国内镜像源、配代理 |
| 架构不匹配 | exec format error | 选择目标平台,并确认构建节点或仿真支持;仅加 --platform 不保证可执行 |
| 指令语法错 | unknown instruction |
检查大小写、续行符 |
| 磁盘满 | no space left | docker system prune |
常见问题(FAQ)
Q:本地能构建、CI 上失败?
A:多数是本地缓存掩盖(用 --no-cache 复现)、上下文不同(CI 的 checkout 少了文件)或架构不同(ARM Mac vs x86 CI)。
Q:多阶段构建里前面阶段失败怎么看?
A:--target 阶段名 只构建到该阶段,分段定位。
Q:如何验证 Dockerfile 语法但不构建?
A:用 docker build --check(BuildKit 提供的 lint),或 hadolint 工具静态检查。