Docker Registry 2.0 - 如何删除未使用的镜像?

95
我们已将私有docker注册表更新为官方Registry 2.0版本。此版本现在可以通过哈希标记删除docker镜像(见https://docs.docker.com/registry/spec/api/#deleting-an-image),但我仍然没有看到清理旧镜像的方法。
由于我们的CI服务器不断生产新镜像,我需要一种方法来删除私有注册表中不再由命名标签识别的所有镜像。
如果没有内置的方法来实现此目的,我认为可能可以使用自定义脚本,但是我也没有看到v2 API方法来列出镜像的所有存储的哈希标签..
如何保持私有注册表的干净整洁呢?有什么提示吗?

实际情况比你想象的要糟糕。规范尚未实现;调用DELETE返回400错误代码。请参见https://github.com/docker/distribution/issues/422。 - David Resnick
私有注册表还有很长的路要走。 - xdays
7
你好,我来自未来(四年后)- 现在有什么好的方法来处理这个问题吗?选择似乎和此问题活跃时差不多。 - emmdee
9个回答

69

删除镜像(保留最后10个版本,就像我在我的CI中所做的那样)需要三个步骤:

  1. 通过设置环境变量 REGISTRY_STORAGE_DELETE_ENABLED: "true" 并传递给 docker-registry 启用镜像删除

  2. 运行下面的脚本(它会删除所有镜像和标签但保留最后10个版本)

    registry.py -l user:pass -r https://example.com:5000 --delete --num 10

  3. 运行垃圾回收(您可以将其放入每日 cron 任务中)

    docker-compose -f [path_to_your_docker_compose_file] run registry bin/registry garbage-collect /etc/docker/registry/config.yml

registry.py 可以从以下链接下载,它还允许列出镜像、标签和层,以及删除特定的镜像和/或标签。

https://github.com/andrey-pohilko/registry-cli

在进行垃圾回收之前,我的注册表文件夹大小为7 GB,之后运行上述步骤后,它缩小到1 GB。


这个解决方案的问题在于它无法处理未标记的修订版本。也就是那些你推送到注册表中而没有指定标签的版本。@anoxis 你能确认一下吗?我通过删除那些版本成功地释放了120GB的空间。但是还存在一些问题,我准备在那个答案下添加一条评论。 - x-yuri
@x-yuri 可能你是对的。我的脚本并不打算清理所有修订版本,就像你使用的解决方案一样。已知该脚本的问题是无法从注册表中删除整个图像。但是,正如您所看到的,原始问题是关于如何从 CI 服务器清除旧标记,而不是所有修订版本或所有存储库。因此,两种解决方案都很好,它们只是服务于不同的目的。 - anoxis
我不想删除所有的修订版本(那基本上就是删除一个标签),更不用说所有的存储库了。你的脚本基本上是通过删除标签本身来删除所有标签的修订版本。我绝不是在贬低你的回答,只是在添加关于我的情况的细节。从我所看到的registry.py使用API(/v2/_catalog 用于存储库 - x-yuri
...使用/v2/REPO/tags/list获取标签。但是,每个标签通常都有多个版本。可以在/var/lib/docker/volumes/registry/_data/docker/registry/v2/repositories/REPO/_manifests/tags/TAG/index中查看这些版本。可以使用DELETE /v2/REPO/manifests/sha256:HASH删除它们。 - x-yuri
但是文件系统似乎不能准确地反映事态,这可能会导致您删除标签而不仅仅是标签的旧版本(您删除除最后一个版本之外的所有版本,但最后一个版本仅存在于磁盘上)。至少,这是我迄今为止得到的关于出了什么问题的最好解释。希望官方解决方案能够做到我所想的。 - x-yuri

60

关于您的问题:

我需要一种方法来删除私有注册表中所有不再由命名标签识别的图像

一个新版本的Docker注册表在distribution/registry:master 上具有这个很好的功能!但是,您无法通过API触发它

无论如何,您将能够清理您注册表中的所有未标记的清单,这意味着每个覆盖的标记不会在注册表中留下旧的清单和二进制文件。每个“未使用”的层都将被注册表垃圾回收器清除。

您只需要运行一个docker exec命令:

docker exec ${container_id} registry garbage-collect \ 
  /path/to/your/registry/config.yml \
  --delete-untagged=true

看这个垃圾回收二进制文件的帮助:

Usage: 
  registry garbage-collect <config> [flags]
Flags:
  -m, --delete-untagged=false: delete manifests that are not currently referenced via tag
  -d, --dry-run=false: do everything except remove the blobs
  -h, --help=false: help for garbage-collect
你可以查看github PR,它已经被合并并可用于distribution/registrymaster标签截至2018-02-23。 它取代了docker/docker-registry项目,具有新的API设计,重点是安全和性能...我今天使用了此功能,并恢复了注册表空间的89%(5.7 GB vs. 55 GB)。之后我切换回稳定的registry

4
PR所做的更改应该已经在官方镜像中体现了,现在可以查看。 - x-yuri
3
现在这个应该被接受为答案。它完全符合要求。如果有人正在运行私有仓库,唯一需要做的就是拉取新镜像 docker pull registry:2,停止容器 docker-compose stop registry,删除容器 docker-compose rm registry 并重新创建它 docker-compose up -d registry。以上步骤适用于使用 docker-compose 并在带有 docker-compose.yml 的目录中执行,并且您的容器名称为 registry :) - Krystian
垃圾回收器现在已经在Docker Registry v2.7+中,因此任何新安装的Docker Registry都应该具备它。 - Zoltan Fedor
2
这是一个很好的答案。但是我不知道我的“注册表文件”在哪里或是什么。我通过执行 docker exec -it <docker-registry-container-name> /bin/sh 找到了它的位置,然后执行了 find / -iname "*.yml",找到了该文件的位置。 - Dave
这个可以以某种方式自动完成吗? - WoodyWoodsta

32

我使用名称为 docker-registry_registry_1 的Docker容器托管registry:2镜像仓库。

我刚刚运行了garbage-collect命令,并加上了-m选项。

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

1
谢谢!运行得非常顺利! - Aranya Sen
1
这太棒了! - Danny Sofftie

21

虽然不太美观,但是这个可行。您需要运行(我认为)注册表2.3或更高版本,并启用删除(REGISTRY_STORAGE_DELETE_ENABLED=True环境变量或等效项)。下面的示例命令假定在/srv/docker-registry中有一个本地文件存储,但我相信对于其他存储后端也可以找到类似的解决方案。

对于您希望整理的每个存储库,您需要枚举不再需要的摘要引用。最简单的方法是按标签进行,以此示例中的latest为例,您可以执行以下操作:

ls -1tr /srv/docker-registry/v2/repositories/<repo>/_manifests/tags/latest/index/sha256 \
| tail -n +3

这将列出除最近三个推送到 latest 标签之外的所有摘要。或者,如果您不太关心 标签,只想保留最近几个推送的引用,可以这样操作:

ls -1t /srv/docker-registry/v2/repositories/<repo>/_manifests/revisions/sha256 \
| tail -n +3

然后,您只需删除不需要的引用即可:

for hash in $(ls -1t /srv/docker-registry/v2/repositories/<repo>/_manifests/tags/latest/index/sha256 | tail -n +3); do
  curl -X DELETE https://registry:5000/v2/<repo>/manifests/sha256:$hash
done

最后,您需要进行GC运行,因为注册表实现了“软删除”,它实际上并未删除任何内容,而只是使其无法使用:

docker exec docker-registry /bin/registry \
  garbage-collect /path/to/config.yml

没错,这一切都非常混乱,在后端存储区域里翻找,因为没有API方法可以枚举与给定标签关联的所有摘要,但这就是现实。


小澄清:标记是 REGISTRY_STORAGE_DELETE_ENABLED,而不是 ..._STORE_...。更多详情请见 https://docs.docker.com/registry/configuration/#/override-specific-configuration-options。 - Dave Foster
3
在v2.6中,目录结构为.../_manifest/tags/<tag>/index/... - David Pärsson
@womble,某种方式删除了我的一张图片的标签(“latest”)。此外,文件系统的状态似乎并不反映事态发展的情况。在删除了不必要的清单并运行垃圾收集器之后,我仍然可以看到至少有一些应该消失在/srv/docker-registry/v2/repositories/<repo>/_manifests/tags/latest/index/sha256的清单。 - x-yuri
1
自从我写下那个答案以来,事情发生了变化并不让我感到惊讶。我不再使用Docker注册表来处理任何我绝对不必要的事情。 - womble
我不确定它们是否已经改变。我可能只是幸运地遇到了它不能正常工作的情况。我的猜测是文件系统没有完全反映事态。当你删除除最后一个修订版本之外的所有内容时,后者可能仅存在于磁盘上。因此,您刚刚删除了所有修订版本,并且标签也消失了。希望官方解决方案能够做到我所想的那样。顺便问一下,你用什么? - x-yuri

7

1
假设我们的CI服务器每天会消耗超过50GB的磁盘空间,你有什么建议的解决方案?那个IRC频道看起来对我来说也很安静 :) - Kristof Jozsa
简短来说,这里有一些建议:如果那是“可抛弃的”内容,你可以偶尔清空整个存储空间。如果这不可接受,每天50GB的使用量在三个月内就会达到5T - 或许可以考虑一些廉价的存储解决方案?此外,我预计在接下来的几个月里会有一个真正的解决方案出现。很抱歉不能立即提供更好的解决方案... - Mangled Deutz
@MangledDeutz 这个有什么消息吗?已经过去几个月了,谢谢! - yorch
@MangledDeutz 太好了!谢谢,希望它能尽快合并回来。 - yorch
垃圾回收机制很好,但实际上没有删除任何引用,这些引用可能会导致垃圾回收运行时无法清除任何东西。 - womble
显示剩余2条评论

4

我整理了这个帖子的不同部分,并创建了一个易于使用的bash清理脚本。

你可以在这个Gist链接中查看:cleanup.sh


1
感谢您的脚本。在进行了一些修改后,它对我很有用:1)for hash in 循环假定仓库名称是单个字符串,而我的仓库名称为 project/repo,因此我不得不添加另一个内部循环来导航额外的文件夹级别。2)使用 -u usr:pwd 在 curl 命令中添加身份验证。3)将 REGISTRY_STORAGE_DELETE_ENABLED=true 添加到运行注册表的环境变量中。 - foz
1
@foz 最好使用“-n,--netrc”选项代替“-u”。 - x-yuri

3

我在查找注册表v2 api中相同的功能,但只找到了软删除,这不是我要找的。在研究过程中,我发现了Github项目delete-docker-registry-image,它通过bash脚本从挂载的卷中删除实际文件。没有测试过,也许有用...


在我的环境中似乎行得通了 :) 由于我在容器内运行注册表,所以我不得不创建一个符号链接'ln -s /var/lib/docker/volumes/c49189f29d8bd93f644438dee774685790687a67c576eb1349cbfe218e14fc20/_data /opt/registry_data',而这个神秘的字符串是容器的挂载点(可以通过命令docker inspect --format '{{range .Mounts}}{{.Name}}{{end}}' <containername>检索)。 - Juergen Klasen

2

要删除未使用的镜像,需要按照以下三个步骤:

  1. docker rmi -f **imageid**

  2. rm -Rf /home/**homedirectory**/docker-registry/data/docker/registry/v2/repositories/**yoursystemname**/**yourimagename**/_manifests/tags/**image version**/

  3. docker exec $(docker ps -q) bin/registry garbage-collect /etc/docker/registry/config.yml -m

*注意事项:

** 请在测试环境中执行上述命令,以免在执行过程中出现错误或未理解某个步骤而损坏生产环境。

** 您可以使用root用户的crontab定时执行上述命令。在第3步中,您必须删除"-it",即:docker exec $(docker ps -q) bin/registry garbage-collect /etc/docker/registry/config.yml -m`。

这对我已经有效运行了6个月以上。


0
Kubernetes等效命令:
kubectl exec --tty --stdin registry-0 -n devops -- registry garbage-collect /etc/docker/registry/config.yml --delete-untagged=true

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