Docker 23.0.5+Buildkit环境下悬空镜像消失,去向何处?
背景
之前使用传统Docker构建器时,若Dockerfile中某条指令执行失败,会保留最后一次成功步骤对应的悬空镜像(REPOSITORY和TAG均为<none>),可通过该镜像调试失败的指令。
比如这个Dockerfile:
FROM alpine # Step 1 RUN true # Step 2: 执行成功 RUN false # Step 3: 执行失败 RUN echo "Next instructions..." # 后续步骤
构建失败后,docker images会显示类似结果:
$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE <none> <none> 5e2dfc857e73 1 min ago 261.6 MB ^^^^^^^^^^^^ 最后成功步骤对应的悬空镜像ID
此时可通过该镜像ID启动容器调试:
$ docker run -it --rm --entrypoint bash 5e2dfc857e73 root@c6b4f3bf1d5d:/# false # 在此调试失败的命令
但使用Docker 23.0.5+和BuildKit构建时,docker images -a或docker image ls -a中不再显示这类悬空镜像,到底去哪了?
原因与解决方案
核心原因
BuildKit作为新一代构建引擎,默认会将构建过程中的中间产物(包括失败后的成功步骤镜像)作为私有构建缓存存储,不会将其标记为常规的悬空镜像(<none>),因此不会出现在docker images的列表中。这种设计是为了更高效地管理缓存和减少磁盘占用。
调试方法
你可以通过以下几种方式获取并调试失败前的中间状态:
查看构建过程的中间哈希
构建时添加--progress=plain参数,强制输出详细的构建日志,其中会包含每个成功步骤对应的镜像哈希值:docker build --progress=plain .找到最后一次成功步骤对应的
sha256:格式哈希,例如步骤2完成后输出的sha256:abc123def456...,然后用该哈希启动容器:docker run -it --rm --entrypoint bash sha256:abc123def456...直接调试构建容器
BuildKit在构建失败时会保留对应的临时容器,你可以通过docker ps -a找到这些容器(名称通常包含buildkit关键字),然后进入容器调试:docker ps -a | grep buildkit docker exec -it <容器ID> bash临时禁用BuildKit
若你更习惯传统的悬空镜像方式,可在构建前临时禁用BuildKit:DOCKER_BUILDKIT=0 docker build .构建失败后,就能在
docker images -a中看到熟悉的<none>悬空镜像,按原有方式调试即可。
内容的提问来源于stack exchange,提问作者alleen1

