真正实现Docker镜像仓库深度清理需构建“先删引用、再跑回收、最后验证释放”闭环:先启用delete功能并按digest删除manifest,再停服执行garbage-collect,最后验证空间释放。

要在 Docker 中真正实现镜像仓库后端存储的深度清理,关键不是“一键开启 GC”,而是构建一个“先删引用、再跑回收、最后验证释放”的闭环流程。GC 本身不识别标签、不按时间删旧镜像,它只清理那些已被 API 明确删除、但底层 blob 仍残留的文件。
必须先启用删除功能
Registry 默认禁止删除操作,不打开就无法触发任何回收:
- 修改 config.yml 的 storage 段,添加:
delete:<br> enabled: true
- 或使用环境变量(K8s/Compose 更常用):
REGISTRY_STORAGE_DELETE_ENABLED=true - 配置生效后需重启 registry 容器,并确认无写入中
- 验证是否生效:用
curl -X DELETE http://reg/v2/repo/manifests/sha256:xxx测试,返回 202 表示成功;405 则说明未启用
按 digest 批量删除 manifest(GC 前提)
GC 不认 tag 名,只认 manifest digest。同一个 tag 可能被覆盖,必须按 digest 删除才安全:
- 获取某仓库所有 tag:
curl "http://reg/v2/myapp/tags/list" - 对每个 tag 发送 HEAD 请求:
curl -I "http://reg/v2/myapp/manifests/<tag>"</tag>,提取响应头中的 Docker-Content-Digest - 按策略筛选 digest:例如保留最近 3 个
dev-开头的,或按时间戳排序后删掉前 N 个 - 调用 DELETE 接口清理:
curl -X DELETE "http://reg/v2/myapp/manifests/sha256:abc123" - 推荐用
regctl简化操作:regctl manifest delete myreg:5000/myapp@sha256:abc123
停服执行垃圾回收(真正释放空间)
GC 必须在 registry 完全无写入时运行,否则会跳过被引用的 blob,导致清理无效:
- 停 registry:
docker stop registry或kubectl scale deploy registry --replicas=0 - 先预览(强烈建议):
docker exec registry /bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml - 确认无误后执行真实回收:
docker exec registry /bin/registry garbage-collect /etc/docker/registry/config.yml - 若磁盘空间未下降,检查 storage driver:filesystem 类型需额外
sync;NFS 后端注意是否支持原子 rename
生产环境加固要点
避免误删和并发冲突,这几个细节不能省:
- 加锁机制:脚本开头创建临时锁文件,结束时删除;多个清理任务同时跑时可互斥
- 引用检查:删除前用
regctl blob mount或查引用关系,确认该 digest 确实无其他仓库复用 - 日志与调试:加
--debug参数查看 GC 实际处理了哪些 blob,便于排查“没删干净”问题 - 定期验证:配合
docker system df对比清理前后 RECLAIMABLE 数值变化


















