registry garbage collection 不生效的主因是未停 registry 或未先调用 DELETE API 标记镜像,且需配置 delete.enabled=true;GC 仅清理已被 API 删除但 blob 残留的层,非自动扫描磁盘。

registry garbage collection 命令不生效的常见原因
直接执行 registry garbage-collect 却没删掉任何镜像?大概率是 registry 还在运行,或者没有先触发 DELETE 请求标记镜像为“可删”。Docker registry 的 GC 不是扫描磁盘自动清理,它只回收那些已被 API 删除、但底层 blob 还残留的层。
- 必须先停掉 registry 容器(或确保无写入),否则 GC 会跳过正在被引用的 blob
- 镜像得真被
DELETE /v2/调用过——单纯/manifests/ docker rmi或删本地镜像完全无效 - 配置里要开启
delete: enabled: true,否则 DELETE 接口返回 405,GC 就没东西可收 - GC 日志默认很安静,加
--dry-run=false --debug才能看到实际删了哪些sha256:xxx
如何安全触发旧镜像的 DELETE 操作
手动一个个调 DELETE API 不现实,真正能落地的是按 tag 规则批量清理,比如保留每项目最近 3 个 dev- 镜像。关键不是“怎么删”,而是“怎么选”——registry 自身不提供按时间/数量过滤的接口,得靠外部工具查 manifest 列表再比对 digest。
- 用
curl -X GET "http://reg/v2/拿到所有 tag,再逐个/tags/list" HEAD /v2/拿到/manifests/ Docker-Content-Digest头 - 注意:tag 可能被覆盖重推,同一个 tag 对应不同 digest,所以不能只删 tag 名,必须按 digest 删除
- 生产环境建议加锁机制,避免多个清理脚本并发操作导致误删;删除前用
registry blob mount检查 digest 是否还在被其他 repo 引用 - 推荐小工具:开源的
reg(reg delete)或skopeo配合 shell 脚本,比裸 curl 更稳
GC 后磁盘空间没释放?检查 storage driver 和 fsync
执行完 GC,du -sh /var/lib/registry 发现大小几乎不变——不是 GC 失败,而是底层文件系统没真正删文件,尤其用 filesystem 存储驱动时。
- registry 默认使用
fsync确保元数据落盘,但 blob 文件删除依赖宿主机文件系统行为;ext4 下需确认未挂载为noatime,nobarrier等影响删除效率的选项 - 如果 registry 挂载的是 overlay2 或 aufs 容器卷,GC 删除的是 volume 内部文件,但宿主机
df可能延迟更新,可尝试sync && echo 3 > /proc/sys/vm/drop_caches(仅测试环境) - 更隐蔽的情况:某些 NFS 存储后端不支持硬链接或原子 rename,导致 registry 的
delete流程卡在临时文件阶段,日志里会有failed to remove blob但不报错
用 Harbor 的 cleanup policy 替代原生 registry GC?
如果你用的是 Harbor,别碰原生 registry 的 GC 命令——Harbor 自带的清理策略(Retention Policy)已封装完整逻辑,且能跨项目统一控制,但默认不启用,也容易配错。
- 策略生效前提是镜像推送时带了
push_time时间戳(Harbor v2.0+ 默认有),否则按创建时间清理会误伤 - 规则匹配顺序很重要:Harbor 是从上到下逐条匹配,第一条命中即终止,别把“保留 latest”写在“删除 30 天前”下面
- 执行记录在 UI 的
Retentions > Executions里,失败时错误信息藏在job_logs容器日志中,关键词是retention_job - 策略只删 manifest 和关联 blob,不会动 project-level 的配置或 robot 账号权限,这点比手敲 curl 安全得多
GC 最容易被忽略的其实是镜像引用关系——一个 digest 被 5 个 repo 的 tag 同时引用,删其中 1 个 tag 不会触发 GC,必须等全部引用消失。所以清理前,先用 registry api /v2/_catalog 和 /v2/ 确认引用拓扑,比盲目跑脚本靠谱。










