我运行一个私有 docker 注册表,并且我想latest从存储库中删除所有图像。我不想删除整个存储库,只是其中的一些图像。API 文档没有提到这样做的方法,但肯定有可能吗?
15 回答
目前,您不能将 Registry API 用于该任务。它只允许您删除存储库或特定标签。
通常,删除存储库意味着删除与该存储库关联的所有标签。
删除标签意味着删除图像和标签之间的关联。
以上都不会删除单个图像。它们留在您的磁盘上。
解决方法
对于此解决方法,您需要将 docker 图像存储在本地。
您的解决方案的解决方法是删除除最新标签之外的所有标签,从而可能删除对关联图像的引用。然后,您可以运行此脚本以删除所有未由任何标签或任何已使用图像的祖先引用的图像。
术语(图像和标签)
考虑这样的图像图,其中大写字母 ( A, B, ...) 表示短图像 ID,<-表示图像基于另一个图像:
A <- B <- C <- D
现在我们给图片添加标签:
A <- B <- C <- D
| |
| <version2>
<version1>
在这里,标签<version1>引用图像C,标签<version2>引用图像D。
细化你的问题
在你的问题中你说你想删除
所有图像,但
latest
. 现在,这个术语并不完全正确。您混合了图像和标签。查看图表,我认为您会同意该标签<version2>代表最新版本。事实上,根据这个问题,你可以有一个代表最新版本的标签:
A <- B <- C <- D
| |
| <version2>
| <latest>
<version1>
由于<latest>标签引用了 imageD我问你:你真的要删除除 image 之外的所有内容D吗?可能不是!
如果删除标签会发生什么?
如果您<version1>使用 Docker REST API 删除标签,您将获得以下信息:
A <- B <- C <- D
|
<version2>
<latest>
请记住: Docker 永远不会删除图像!即使这样做了,在这种情况下它也不能删除图像,因为图像是被标记C图像的祖先的一部分。D
即使您使用此脚本,也不会删除任何图像。
何时可以删除图像
在您可以控制何时有人可以拉取或推送到您的注册表的条件下(例如,通过禁用 REST 接口)。如果没有其他图像基于该图像并且没有标签引用它,您可以从图像图中删除该图像。
请注意,在下图中,图像D不是基于,而是C基于B。因此,D不依赖于C. 如果您删除<version1>此图中的标签,则该图像C将不会被任何图像使用,并且此脚本可以将其删除。
A <- B <--------- D
\ |
\ <version2>
\ <latest>
\ <- C
|
<version1>
清理后,您的图像图如下所示:
A <- B <- D
|
<version2>
<latest>
这是你想要的吗?
我的注册表也遇到了同样的问题,然后我从博客页面尝试了下面列出的解决方案。有用。
第 1 步:列出目录
您可以通过调用此 url 列出您的目录:
http://YourPrivateRegistyIP:5000/v2/_catalog
响应将采用以下格式:
{
"repositories": [
<name>,
...
]
}
第 2 步:列出相关目录的标签
您可以通过调用此 url 列出目录的标签:
http://YourPrivateRegistyIP:5000/v2/<name>/tags/list
响应将采用以下格式:
{
"name": <name>,
"tags": [
<tag>,
...
]
}
第 3 步:列出相关标签的清单值
您可以在 docker 注册表容器中运行此命令:
curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://localhost:5000/v2/<name>/manifests/<tag> 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}'
响应将采用以下格式:
sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073
使用清单值运行下面给出的命令:
curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://127.0.0.1:5000/v2/<name>/manifests/sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073
第 4 步:删除标记的清单
在您的 docker registy 容器中运行此命令:
bin/registry garbage-collect /etc/docker/registry/config.yml
这是我的 config.yml
root@c695814325f4:/etc# cat /etc/docker/registry/config.yml
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
当前的v2注册表现在支持通过删除DELETE /v2/<name>/manifests/<reference>
请参阅:https ://github.com/docker/distribution/blob/master/docs/spec/api.md#deleting-an-image
工作用法: https ://github.com/byrnedo/docker-reg-tool
编辑:上面的清单<reference>可以从请求中检索到
GET /v2/<name>/manifests/<tag>
并检查 Docker-Content-Digest响应中的标头。
编辑 2:您可能必须使用以下环境集运行注册表:
REGISTRY_STORAGE_DELETE_ENABLED="true"
Edit3:您可能必须运行垃圾收集来释放此磁盘空间: https ://docs.docker.com/registry/garbage-collection/
问题 1
您提到它是您的私有 docker 注册表,因此您可能需要检查Registry API而不是Hub registry API doc,这是您提供的链接。
问题 2
docker registry API 是一个客户端/服务器协议,是否删除后端的图像取决于服务器的实现。(我猜)
DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*)
详细解释
下面我从您的描述中演示它现在是如何工作的,作为我对您问题的理解。
我运行一个私人 docker 注册表。我使用默认的,并监听 port 5000。
docker run -d -p 5000:5000 registry
然后我标记本地图像并推入它。
$ docker tag ubuntu localhost:5000/ubuntu
$ docker push localhost:5000/ubuntu
The push refers to a repository [localhost:5000/ubuntu] (len: 1)
Sending image list
Pushing repository localhost:5000/ubuntu (1 tags)
511136ea3c5a: Image successfully pushed
d7ac5e4f1812: Image successfully pushed
2f4b4d6a4a06: Image successfully pushed
83ff768040a0: Image successfully pushed
6c37f792ddac: Image successfully pushed
e54ca5efa2e9: Image successfully pushed
Pushing tag for rev [e54ca5efa2e9] on {http://localhost:5000/v1/repositories/ubuntu/tags/latest}
之后,我可以使用Registry API检查它是否存在于您的私有 docker 注册表中
$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags
{"latest": "e54ca5efa2e962582a223ca9810f7f1b62ea9b5c3975d14a5da79d3bf6020f37"}
现在我可以使用该 API 删除标签了!!
$ curl -X DELETE localhost:5000/v1/repositories/ubuntu/tags/latest
true
再次检查,我的私人注册服务器中不存在该标签
$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
这真的很难看,但它有效,文本在 registry:2.5.1 上进行了测试。即使在更新配置以启用删除后,我也没有设法让删除工作顺利进行。ID真的很难找回,必须登录才能得到,可能是一些误会。无论如何,以下工作:
登录到容器
docker exec -it registry sh定义与您的容器和容器版本匹配的变量:
export NAME="google/cadvisor" export VERSION="v0.24.1"移动到注册表目录:
cd /var/lib/registry/docker/registry/v2删除与您的哈希相关的文件:
find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1删除清单:
rm -rf ./repositories/$NAME/_manifests/tags/$VERSION登出
exit运行 GC:
docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml如果一切都正确完成,则会显示有关已删除 blob 的一些信息。
有一些客户端(在 Python、Ruby 等中)正是这样做的。就我的口味而言,在我的注册表服务器上安装运行时(例如 Python)是不可持续的,只是为了管理我的注册表!
我deckschrubber的解决方案也是:
go get github.com/fraunhoferfokus/deckschrubber
$GOPATH/bin/deckschrubber
超过给定年龄的图像会被自动删除。-year可以使用、-month、-day或它们的组合来指定年龄:
$GOPATH/bin/deckschrubber -month 2 -day 13 -registry http://registry:5000
更新:这里是关于deckschrubber的简短介绍。
您可以使用的另一个工具是registry-cli。例如,这个命令:
registry.py -l "login:password" -r https://your-registry.example.com --delete
将删除除最后 10 张图像之外的所有图像。
删除所有标签的要求latest变得复杂,因为同一个图像清单可以被多个标签指向,所以当您删除一个标签的清单时,您可能会有效地删除多个标签。
有几个选项可以使它可行。一种是跟踪最新标签的摘要,只删除其他摘要的清单,或者您可以使用一些不同的 API 调用来删除标签本身。
无论您如何实现这一点,首先需要将您的注册表配置为允许删除 API。对于最小registry:2图像,这涉及使用环境变量REGISTRY_STORAGE_DELETE_ENABLED=true(或等效的 yaml 配置)启动它。
然后对于一个简单的脚本来遍历标签并删除,有:
#!/bin/sh
repo="localhost:5000/repo/to/prune"
for tag in $(regctl tag ls $repo); do
if [ "$tag" != "latest" ]; then
echo "Deleting: $(regctl image digest --list "${repo}:${tag}") [$tag]"
regctl tag rm "${repo}:${tag}"
fi
done
此处使用的regctl命令来自regclient,regctl tag rm逻辑首先尝试执行最近添加到distribution-spec的标签删除 API 。由于大多数注册中心尚未实现该规范,因此它回退到清单删除 API,但它首先创建一个虚拟清单以覆盖标记,然后删除该新摘要。这样做时,如果旧清单正被其他标签使用,它不会删除那些其他标签。
latest删除清单的脚本的替代版本(指向摘要的清单除外)如下所示:
#!/bin/sh
repo="localhost:5000/repo/to/prune"
save="$(regctl image digest --list "${repo}:latest")"
for tag in $(regctl tag ls $repo); do
digest="$(regctl image digest --list "${repo}:${tag}")"
if [ "$digest" != "$save" ]; then
echo "Deleting: $digest [$tag]"
regctl manifest rm "${repo}@${digest}"
fi
done
如果您发现自己需要创建删除策略来自动删除大量图像,我建议您查看regclient/regbot同一个 repo,它允许您定义该策略并使其运行以不断修剪您的注册表。
删除图像后,在大多数用例中,您需要对注册表进行垃圾收集。例如,registry:2图像看起来像:
docker exec registry /bin/registry garbage-collect \
/etc/docker/registry/config.yml --delete-untagged
简要地;
1)您必须为 docker repo 的 RepoDigests 键入以下命令;
## docker inspect <registry-host>:<registry-port>/<image-name>:<tag>
> docker inspect 174.24.100.50:8448/example-image:latest
[
{
"Id": "sha256:16c5af74ed970b1671fe095e063e255e0160900a0e12e1f8a93d75afe2fb860c",
"RepoTags": [
"174.24.100.50:8448/example-image:latest",
"example-image:latest"
],
"RepoDigests": [
"174.24.100.50:8448/example-image@sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6"
],
...
...
${摘要} = sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6
2) 使用注册表 REST API
##curl -u username:password -vk -X DELETE registry-host>:<registry-port>/v2/<image-name>/manifests/${digest}
>curl -u example-user:example-password -vk -X DELETE http://174.24.100.50:8448/v2/example-image/manifests/sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6
您应该获得 202 Accepted 成功调用。
3-) 运行垃圾收集器
docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml
注册表— 注册表容器名称。
有关更多详细说明,请在此处输入链接描述
还有一种方法可以仅根据创建日期从存储库中删除一些旧图像。
为此,请输入您的 docker 注册表容器并获取某个特定存储库的清单修订列表:
ls -latr /var/lib/registry/docker/registry/v2/repositories/YOUR_REPO/_manifests/revisions/sha256/
然后可以在请求中使用输出(带有 sha256 前缀):
curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://DOCKER_REGISTRY_HOST:5000/v2/YOUR_REPO/manifests/sha256:OUTPUT_LINE
当然不要忘记在那之后执行“garbage-collect”命令:
bin/registry garbage-collect /etc/docker/registry/config.yml
此 docker 映像包含一个 bash 脚本,可用于从远程 v2 注册表中删除映像: https ://hub.docker.com/r/vidarl/remove_image_from_registry/
我通常都喜欢用脚本来做事,但是如果你已经在运行一个从Joxit/docker-registry-ui构建的注册表 UI 容器,我发现选择单击 UI 中的删除按钮并删除一个页面会更容易一次图像,然后进行垃圾收集。
基于此答案的简单 ruby 脚本:registry_cleaner。
您可以在本地机器上运行它:
./registry_cleaner.rb --host=https://registry.exmpl.com --repository=name --tags_count=4
然后在注册表机器上删除带有/bin/registry garbage-collect /etc/docker/registry/config.yml.
下面的 Bash 脚本删除位于注册表中的所有标签,除了最新的。
for D in /registry-data/docker/registry/v2/repositories/*; do
if [ -d "${D}" ]; then
if [ -z "$(ls -A ${D}/_manifests/tags/)" ]; then
echo ''
else
for R in $(ls -t ${D}/_manifests/tags/ | tail -n +2); do
digest=$(curl -k -I -s -H -X GET http://xx.xx.xx.xx:5000/v2/$(basename ${D})/manifests/${R} -H 'accept: application/vnd.docker.distribution.manifest.v2+json' | grep Docker-Content-Digest | awk '{print $2}' )
url="http://xx.xx.xx.xx:5000/v2/$(basename ${D})/manifests/$digest"
url=${url%$'\r'}
curl -X DELETE -k -I -s $url -H 'accept: application/vnd.docker.distribution.manifest.v2+json'
done
fi
fi
done
在此运行之后
docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
这是基于 Yavuz Sert 回答的脚本。它会删除所有不是最新版本的标签,并且它们的标签大于 950。
#!/usr/bin/env bash
CheckTag(){
Name=$1
Tag=$2
Skip=0
if [[ "${Tag}" == "latest" ]]; then
Skip=1
fi
if [[ "${Tag}" -ge "950" ]]; then
Skip=1
fi
if [[ "${Skip}" == "1" ]]; then
echo "skip ${Name} ${Tag}"
else
echo "delete ${Name} ${Tag}"
Sha=$(curl -v -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://127.0.0.1:5000/v2/${Name}/manifests/${Tag} 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}')
Sha="${Sha/$'\r'/}"
curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE "http://127.0.0.1:5000/v2/${Name}/manifests/${Sha}"
fi
}
ScanRepository(){
Name=$1
echo "Repository ${Name}"
curl -s http://127.0.0.1:5000/v2/${Name}/tags/list | jq '.tags[]' |
while IFS=$"\n" read -r line; do
line="${line%\"}"
line="${line#\"}"
CheckTag $Name $line
done
}
JqPath=$(which jq)
if [[ "x${JqPath}" == "x" ]]; then
echo "Couldn't find jq executable."
exit 2
fi
curl -s http://127.0.0.1:5000/v2/_catalog | jq '.repositories[]' |
while IFS=$"\n" read -r line; do
line="${line%\"}"
line="${line#\"}"
ScanRepository $line
done