咨询:私有Docker Registry仅4个标签却有大量Manifests和Blobs的原因
我维护一个私有Docker Registry,执行垃圾回收(garbage-collection)无法释放存储空间。当前镜像仅包含4个标签,但运行garbage-collection --dry-run时,发现大量manifests和blobs被标记,且没有任何资源符合删除条件。
标签输出:
1 "0.0.18" 4 "0.0.16" 5 "0.0.19-SNAPSHOT" 6 "0.0.17"
垃圾回收输出(部分省略):
first-image first-image: marking manifest sha256:0097c9845b15c656fce4641342e2b326e876552e3ef62d2d00c60c3e60cdc1a5 first-image: marking blob sha256:a3d9a3da71d2604e1ea5b79aaeaaf11b3fc8bbd433cbb145df13342f4691fc88 first-image: marking blob sha256:9d19ee268e0d7bcf6716e6658ee1b0384a71d6f2f9aa1ae2085610cf7c7b316f first-image: marking blob sha256:aa5b63dfa01646dce4380e03beced61802400b00a00eb6c41a45612e6fb57e96 first-image: marking manifest sha256:00d35e06b03f5c1858f0f961de3d5f8a47af57b16c0a3d122ba3b8bd9da69b06 ...(省略大量条目) 30151 blobs marked, 0 blobs and 0 manifests eligible for deletion
实际约有70个manifest条目,旧的SNAPSHOT标签已删除但manifests仍留存;另有第二个镜像已删除所有标签,但垃圾回收仍显示存在无法删除的manifests和blobs。
核心原因
1. 存在未断链的Manifest引用
Docker Registry垃圾回收仅清理无任何引用链的资源。即使删除标签,以下情况会让Manifest持续被引用:
- 多架构镜像的
manifest list(索引清单)会引用单个架构的具体manifest,这些被引用的manifest不会被标记为废弃。 - 覆盖推送同名标签时,旧manifest不会自动删除,若被其他manifest(如manifest list)引用,就会被保留。
- 共享blob:只要有一个留存的manifest引用某个blob,该blob就会被标记为需保留。
2. 未清理的Manifest List
如果镜像曾推送过多架构版本,生成的manifest list会持续引用底层的架构manifest和blob。即使删除了标签,manifest list本身未被删除的话,所有关联资源都无法被回收。
3. 无标签Manifest未被标记为废弃
手动删除标签后,对应的manifest不会立即被标记为废弃(默认未启用自动清理)。这些"僵尸"manifest若被其他资源引用,会一直占用存储空间。
解决步骤
1. 排查并删除无引用的Manifest
通过Registry API列出所有无标签的manifest:
# 替换为你的Registry地址和镜像名 REGISTRY_URL="your-registry-domain" IMAGE_NAME="first-image" # 获取所有manifest的digest列表 curl -u username:password https://$REGISTRY_URL/v2/$IMAGE_NAME/manifests/list | jq '.manifests[].digest' # 逐个检查manifest是否关联标签 for digest in $(curl -u username:password https://$REGISTRY_URL/v2/$IMAGE_NAME/manifests/list | jq -r '.manifests[].digest'); do curl -u username:password https://$REGISTRY_URL/v2/$IMAGE_NAME/tags/list | jq '.tags' | grep -q $(curl -u username:password https://$REGISTRY_URL/v2/$IMAGE_NAME/manifests/$digest | jq -r '.config.digest') || echo "无引用Manifest: $digest" done
确认无引用后,手动删除这些manifest:
curl -u username:password -X DELETE https://$REGISTRY_URL/v2/$IMAGE_NAME/manifests/<目标digest>
2. 执行带删除标记的垃圾回收
先停止Registry服务(避免读写干扰),再执行完整回收:
docker stop registry-container-name docker run --rm -v /path/to/registry/data:/var/lib/registry registry:latest garbage-collect /etc/docker/registry/config.yml --delete-untagged
--delete-untagged参数会删除所有无标签且无其他引用的manifest及关联blob。
3. 启用自动清理机制
在Registry的config.yml中添加以下配置,开启定期自动清理:
garbagecollect: enabled: true interval: 24h dry-run: false delete-untagged: true
重启Registry后,会按配置自动清理废弃资源。
内容的提问来源于stack exchange,提问作者tomi

