Docker 29 执行 buildx prune 后下一次构建全部 cache miss 时,核对 builder、containerd image store、清理记录、恢复步骤与 GC 回退。
执行 docker buildx prune 或 docker builder prune 后,如果下一次构建突然所有步骤都变成 cache miss,先停止定时重复执行部分清理。先确认清理命令是否真的删除了记录、当前是否使用 Docker 29 的 containerd image store,以及同一 Dockerfile 在清理前是否能稳定命中缓存。2026 年 9 月 25 日的 BuildKit 上游 Issue 记录了一个特定现场:基础镜像先由 Docker Engine 拉取时,只要一次部分 prune 实际删除了任意记录,未被删除的构建记录也可能不再被复用。这个现场不能证明所有缓存失效都是同一缺陷,但能解释“删除少量无关缓存,下一次却全量重建”的异常。
本文面向在云服务器或 CI 构建机上使用 Docker 29、Buildx 和默认 docker driver 的用户。只读检查不会删除镜像或业务数据。重新构建会消耗 CPU、磁盘、网络流量和依赖下载请求;手工 prune 会永久删除选中的构建缓存,不能撤销,因此不要在生产构建机上为了复现再次执行 prune。
先确认是不是同一个故障现场
先保存一次正常或异常构建的 plain 日志,再读取版本、builder 和存储后端。下面命令只读取状态:
docker version
docker buildx version
docker buildx ls
docker info -f 'driver={{.Driver}} status={{json .DriverStatus}}'
docker buildx du --verbose
若 docker info 出现 driver-type io.containerd.snapshotter.v1,说明当前使用 containerd image store。Docker 官方说明,新安装的 Docker Engine 29 默认使用这个后端;从旧版本升级的主机可能仍是 classic overlay2,所以不能只看 Engine 版本猜存储后端。
docker buildx ls 用来确认实际 builder 和 driver。故障报告针对默认 docker driver;若 CI 使用 docker-container、Kubernetes 或远程 builder,应先去对应 builder 节点取证。docker buildx du --verbose 会列出记录的 ID、是否可回收、大小和最后使用时间。记录还在列表中,不等于下一次一定能匹配到它。
用构建日志区分全量失效与正常变更
使用原 Dockerfile、相同构建上下文和相同参数连续构建两次。把 IMAGE_NAME 替换成测试标签;不要在此步骤加入 --no-cache:
docker buildx build --progress=plain --load -t IMAGE_NAME:test .
docker buildx build --progress=plain --load -t IMAGE_NAME:test .
第二次日志中,未变化步骤应显示 CACHED。如果只有 COPY 及其后续步骤重跑,先检查源文件、文件时间、构建参数和 Dockerfile 顺序;这通常是正常的缓存链失效。如果清理前第二次构建能命中,而某次 prune 输出显示删除了记录,清理后的相同构建却从基础步骤开始全部重跑,才接近本文场景。
还要保存 prune 的真实输出。命令返回成功但 Total: 0B,说明没有实际删除记录,不能据此归因。定时任务日志缺失时,检查 CI 配置、systemd timer 或 cron 是否运行过 docker builder prune、docker buildx prune 或 docker system prune。不要立即再跑一次来“确认”。
为什么少量删除也可能让其余记录不再命中
上游 Issue #7193 在 Docker 29.1.3 / BuildKit 0.26.2 和 Docker 29.8.1 / BuildKit 0.33.0 上复现:基础镜像先由 Engine 的 docker pull 进入 containerd image store,之后部分 prune 只删除一条无关记录,下一次构建仍变成 0% cached。报告中的分析认为,prune 完成后的未引用记录释放流程拿到了已取消的 context,使部分仍存在的结果被解除关联;BuildKit 自己拉取基础镜像的对照组没有复现。
这是上游报告及代码分析,不是已经进入某个正式版本的修复承诺。文章核验时 Issue 仍为 Open,Docker 发布说明也没有可据此承诺的修复项。因此不要写成“升级到某版本必定解决”,也不要把所有 prune 后变慢都归因于该缺陷。
已经发生后怎样安全恢复
缓存本身不能靠回滚命令恢复。未命中的步骤需要重新执行,成功构建会逐步建立新的可用记录。恢复顺序如下:
- 暂停所有定时手工 prune,避免每次构建后再次打断缓存复用。
- 确认磁盘仍有足够空间;空间不足时先扩容或迁移非构建数据,不要在证据未保存前继续清缓存。
- 在业务低峰完成一次相同构建,记录依赖下载、每一步耗时和最终镜像 digest。
- 立即再构建一次,确认稳定步骤重新显示
CACHED。 - 连续观察后续 CI;若没有 prune 仍反复全量重建,转查构建上下文、动态 ARG、基础镜像标签和 builder 是否被替换。
全量重建可能拉取基础镜像和依赖,产生公网流量、镜像仓库请求与构建时长。若业务对构建延迟敏感,先在非关键 runner 预热,再恢复正式流水线。不要删除 /var/lib/docker 或 /var/lib/containerd;这会把缓存问题扩大成镜像和容器数据事故。
后续清理策略怎么选
Docker 官方将手工 prune 与周期性 BuildKit garbage collection(GC)分开:prune 立即执行,GC 按策略和空间阈值周期运行。官方也说明默认 GC 对多数用户已经足够。当前缺陷现场还指出自动 GC 路径没有复现相同问题,因此默认建议是停止高频手工部分 prune,先让 GC 管理缓存。
| 现场 | 建议 | 代价与边界 |
|---|---|---|
| 磁盘空间充足 | 取消每次构建后的手工 prune,保留默认 GC | 缓存占用会波动,需要监控磁盘 |
| 构建盘经常逼近容量 | 调整 GC 的保留空间、最大使用量和最小空闲量 | 阈值过低会增加重建与下载成本 |
| 必须手工腾出空间 | 先用 buildx du 记录现场,在维护窗口清理并接受一次缓存重建 | 删除的缓存不可恢复;不要承诺未删记录一定继续命中 |
| 多台临时 CI runner | 评估注册表或对象存储外部缓存 | 会增加存储、请求费用和凭据管理,需先做小范围验证 |
调整默认 docker driver 的 GC 前,先备份 /etc/docker/daemon.json,不要覆盖已有配置。Docker 官方提供 defaultKeepStorage 简化配置;修改 daemon 配置可能需要重启 Docker,运行中的容器可能中断。生产环境应在维护窗口完成格式校验、重启和回退准备。
怎样验收修复与回退
验收不看“命令成功”四个字,而看三项结果:相同输入的第二次构建出现预期 CACHED;最终镜像 digest 与功能检查符合预期;停止手工 prune 后的多次 CI 不再周期性回到 0% cached。把构建时长与缓存命中作为观察指标,但不要用一次快速构建宣称根因已经确认。
若调整 GC 后磁盘增长过快,恢复备份的 daemon 配置,并在维护窗口重启 Docker。若使用外部缓存后命中率或费用不理想,移除对应 --cache-from/--cache-to 参数即可回到本地缓存;不要删除仍被其他 runner 使用的远端缓存对象。有关磁盘容量本身的排查,可参考Docker no space left on device 排查;若问题发生在拉取权限,参考Docker pull access denied 排查;生产部署基础见Docker Compose 生产部署;镜像推送末尾超时则见Docker push 提交超时排查。
官方与上游来源
- BuildKit Issue #7193:部分 prune 后下一次构建失去缓存,核验日期:2026-09-28。
- BuildKit 官方问题列表:核对 Issue 当前状态与相邻缺陷,核验日期:2026-09-28。
- BuildKit 官方 Releases:核对正式版本变更,核验日期:2026-09-28。
- Docker Docs:docker buildx prune,核验日期:2026-09-28。
- Docker Docs:docker buildx du,核验日期:2026-09-28。
- Docker Docs:Build garbage collection,核验日期:2026-09-28。
- Docker Docs:containerd image store,核验日期:2026-09-28。
本稿未发现需要补充确认的黑鲨云业务事实。

