Docker 执行 buildx prune 后缓存全失效:怎样判断并恢复 BuildKit 命中

先确认部分清理是否实际删除记录,再核对 containerd image store、构建日志与 GC 策略

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 后变慢都归因于该缺陷。

已经发生后怎样安全恢复

缓存本身不能靠回滚命令恢复。未命中的步骤需要重新执行,成功构建会逐步建立新的可用记录。恢复顺序如下:

  1. 暂停所有定时手工 prune,避免每次构建后再次打断缓存复用。
  2. 确认磁盘仍有足够空间;空间不足时先扩容或迁移非构建数据,不要在证据未保存前继续清缓存。
  3. 在业务低峰完成一次相同构建,记录依赖下载、每一步耗时和最终镜像 digest。
  4. 立即再构建一次,确认稳定步骤重新显示 CACHED。
  5. 连续观察后续 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 提交超时排查。

官方与上游来源

本稿未发现需要补充确认的黑鲨云业务事实。

云服务器教程Docker push 报 timeout awaiting response headers:怎样排查 containerd 镜像提交超时2026-09-27云服务器教程NGINX Gateway Fabric 的 HTTPRoute 状态正常却返回 503:怎样定位 fallback upstream2026-09-26云服务器教程Google Cloud P100 停止支持后怎么办?迁移到 G2 或 G4 的检查与切换步骤2026-09-25

加入开发者交流社区

与全球开发者、运维和工作室一起交流技术、分享经验、配置、账号与最新优惠信息

  • 云平台使用交流
  • 资源优惠信息
  • 最新教程与资讯
  • 开发者经验分享
联系 Telegram 客服
加入开发者交流社区