从私有Docker仓库中删除标签

4

我是一名新的Docker用户,正在尝试学习有关Docker的知识。我拥有一个私有的Docker注册表,里面有一些镜像(例如:I1、I2、I3、I4、I5)。许多镜像内部都有标签。以下是我们要考虑的一些标签:

Image I1 - Tags: T11, T12, T13
Image I2 - Tags: T21, T22, T23, T24
Image I3 - Tags: T31, T32
Image I4 - Tags: T41, T42, T43, vT44
Image I5 - Tags: T51, T52

我想从Docker Registry中删除标签为T12,T22和T52的镜像,该如何实现呢?我已经阅读了很多文章,但还是没有弄清楚。以下这些文章似乎有一些有价值的内容:https://docs.docker.com/registry/spec/api/#deleting-an-imageWay to delete the images from Private Docker Registry,但即使阅读过后,我仍然无法找到解决办法。如果有人能指点我正确的方向,我将不胜感激。
3个回答

8

如果你想删除一个标签而不是一个镜像清单,这比表面上看起来要复杂一些。首先,你的注册表需要允许删除镜像,这在 registry:2 镜像中默认未开启。简单的方法是在容器上设置环境变量 REGISTRY_STORAGE_DELETE_ENABLED=true

接下来,要意识到标签和镜像清单之间的区别。镜像清单由摘要表示,是指向图像配置和层的JSON数据。标签指向清单,但多个标签可能指向同一清单,而一个清单可能没有任何标签指向它。如果你删除一个清单,那也会删除所有指向该清单的标签,所以在删除标签时要小心,以免意外删除你想保留的标签所引用的清单。

因此,普通的做法存在问题。普通的做法是查询注册表以获取你想要删除的清单的摘要,然后删除该摘要。你可以从标头中获取该摘要。

acceptM="application/vnd.docker.distribution.manifest.v2+json"
acceptML="application/vnd.docker.distribution.manifest.list.v2+json"
curl -H "Accept: ${acceptM}" \
     -H "Accept: ${acceptML}" \
     -I -s "https://registry.example.org/v2/${repo}/manifests/${tag}" 

然后对该摘要发出的删除请求将删除清单以及所有指向它的标签:
curl -H "Accept: ${acceptM}" \
     -H "Accept: ${acceptML}" \
     -X DELETE -s "https://registry.example.org/v2/${repo}/manifests/${digest}" 

然而,如果您只想删除标签,distribution-spec中有一个删除标签的API,但是很少有注册表实现了它。删除的操作如下:

curl -H "Accept: ${acceptM}" \
     -H "Accept: ${acceptML}" \
     -X DELETE -s "https://registry.example.org/v2/${repo}/manifests/${tag}" 

对于不支持此功能的注册表,我找到的最佳解决方案是推送一个替换标签的虚拟证书,然后删除该虚拟证书。使用curl处理这些内容会变得有点繁琐,还有一些媒体类型头部我没有包含进去,而且这并没有提到身份验证。为了应对这些挑战,我转向使用Go语言编写。我自己的工具是regclient,还有类似skopeo和crane的其他工具存在。从regclient中,执行此操作的regctl命令如下:

regctl tag rm registry.example.org/image1:T12
regctl tag rm registry.example.org/image2:T22
regctl tag rm registry.example.org/image5:T22

删除镜像后,你可能希望清理已用的存储空间,并且需要在没有其他推送正在进行时运行垃圾收集(有些会禁用注册表,或者等到知道上传不会运行的时间)。对于registry:2镜像,GC命令如下:

docker exec registry /bin/registry garbage-collect \
  /etc/docker/registry/config.yml --delete-untagged

将删除所有未标记的清单,以及任何未引用的blob。

注意:分发注册表中的未标记清单当前包括多平台镜像的所有子清单。这意味着如果您的注册表中有多平台镜像,则删除未标记的清单可能会导致数据丢失。有一个3178问题来跟踪何时解决此问题。


您可以删除摘要,所有指向该摘要的标签都将被删除,或者您可以使用regctl仅删除您想要删除的一个标签。 - BMitch
好的。所以,如果我使用curl命令使用摘要进行删除:curl -H "Accept: ${acceptM}"
-H "Accept: ${acceptML}"
-X DELETE -s "https://registry.example.org/v2/${repo}/manifests/${digest}",那么我是否还应该使用命令docker exec registry /bin/registry garbage-collect
/etc/docker/registry/config.yml --delete-untagged来清理未标记的内容?
- Fr0zt
我执行了这个过程并删除了清单,然后进行了垃圾回收。现在当我使用头部信息对其进行curl时,我得到一个HTTP/2 404错误。很好。然而,我的标签仍然存在。我的流水线目前的操作是检查一系列标签,选择最旧的一个,然后删除它的清单+进行垃圾回收。如果在清理之后标签仍然存在,那么流水线将持续尝试移除此标签,但现在清单返回404错误。我应该运行一个单独的步骤来清理标签,还是这表明出现了更深层次的问题? - Blaisem
1
@Blaisem,你是怎么删除标签的?你是如何列出标签的?原始清单的媒体类型是什么?你使用了哪些接受头部?提供一个最小可复现示例(MCVE)会很有帮助,最好在一个单独的问题中提出。 - BMitch
我可能找到了解决方法。最终,我通过将HEAD更改为GET来删除了标签,这样就可以获得一个带有修订键的JSON。使用上面的删除命令和修订键的SHA256清除了条目。可能在开发我的清理脚本时,我搞砸了删除操作(起初我忘记了头部),导致某些内容损坏,只有修订版本的SHA才能正常工作。我也无法再次推送相同的图像,所以最后我还是删除了整个注册表,从头开始。现在看起来一切都正常了。感谢您的精彩文章。 - Blaisem
显示剩余8条评论

4

需要完成两个步骤:

  • 删除标签
  • 运行垃圾回收以释放空间

标签删除

使用HTTP/REST API有更干净的方法,但您可以使用以下命令执行控制删除旧标签(>30天):

find /var/lib/registry/docker/registry/v2/repositories/*/_manifests/tags -type d -mtime +30 -maxdepth 1 -exec rm -rf {} \;

这将有效地“取消标记”图像。我强烈建议您先运行不带-exec rmfind命令,以确保您要删除的内容!

find /var/lib/registry/docker/registry/v2/repositories/*/_manifests/tags -type d -mtime +30 -maxdepth 1

Kubernetes示例(从主机运行):

kubectl exec -it deploy/registry-docker-registry -- \  
  sh -c 'find /var/lib/registry/docker/registry/v2/repositories/*/_manifests/tags -type d -mtime +30 -maxdepth 1 -exec rm -rf {} \;'

垃圾回收

最后,运行garbage-collection -m可执行文件。

/bin/registry garbage-collect -m /etc/docker/registry/config.yml

或者,您可以从主机机器内以以下方式运行它(Kubernetes示例):

kubectl exec -it deploy/registry-docker-registry -- \  
  /bin/registry garbage-collect -m /etc/docker/registry/config.yml

1

虽然接受的答案是一个好的终极解决方案,但有一种更简单的方法可以实现此目的,而不使用API:只需手动从注册表存储中删除标记dir,然后运行garbage-collect即可。

首先要确定您的注册表存储数据的位置。默认情况下,它在容器内的/var/lib/registry目录中,但可能已以某种方式绑定到您的主机上。我使用docker-compose和数据卷,因此在我的情况下,它位于:/var/lib/docker/volumes/registry_data/_data/registry

你会在那里发现以你项目标签命名的dir,相对于上面的路径: v2/repositories/<repo_name>/_manifests/tags

只需删除您不再需要的标记的dirs。例如:

rm -rf /var/lib/docker/volumes/registry_data/_data/registry/v2/repositories/I5/_manifests/tags/T12

完成后,运行垃圾回收:

docker exec <registry_container_name> registry garbage-collect /etc/docker/registry/config.yml --delete-untagged

你的注册表配置文件可能在其他路径中,但这是默认设置。从文档中阅读更多关于 垃圾回收器 的信息,但要点是:容器内部的注册表二进制文件包括一个垃圾回收命令,但必须自己运行它。当然,你也可以让crontab来执行它。


我们的 registry:2.7.1 中没有 /var/lib/docker/volumes 目录,但是在这里的每个存储库中都有“标签”目录 /var/lib/registry/docker/registry/v2/repositories/*/_manifests/tags/ - Marc
默认情况下,它会保存在/var/lib/registry,但这取决于你的设置。我在容器内运行注册表并带有卷,因此所有注册表数据都在卷内。/var/lib/docker/volumes是Docker卷的标准路径。 - ruuter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接