如何从私有docker注册表中删除图像?

我运行一个私有的docker注册表,我想从存储库中删除除latest之外的所有图像。我不想删除整个存储库,只删除其中的一些图像。API文档没有提到这样做的方法,但它肯定是可能的?

391058 次浏览

问题1

你提到它是你的私有docker注册表,所以你可能需要检查注册表的API而不是集线器注册中心API文档,这是你提供的链接。

问题2

docker注册API是一个客户端/服务器协议,它是由服务器的实现是否删除后端图像。(我猜)

DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*)

详细解释

下面我将根据您的描述来演示它是如何工作的,这是我对您的问题的理解。

我运行一个私有docker注册表。 我使用默认的端口,并在端口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}

之后,我可以使用注册表的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"}

目前,您不能为该任务使用注册表API。它只允许您删除存储库或特定的标记。

通常,删除存储库意味着删除与此repo关联的所有标记。

删除标记意味着删除图像和标记之间的关联。

以上任何一种方法都不会删除单个图像。它们留在磁盘上。


解决方案

为了解决这个问题,你需要将docker映像存储在本地。

解决方案的一个变通方法是删除除最新标记以外的所有标记,从而可能会删除对相关图像的引用。然后你可以运行this script . sh 这个问题,你可以有一个代表最新版本的标签:

 A <- B <- C <- D
|    |
|    <version2>
|    <latest>
<version1>

由于<latest>标记引用图像D,我问你:你真的想删除除图像D以外的所有图像吗?可能不是!

如果删除标记会发生什么?

如果你使用Docker REST API删除标签<version1>,你会得到:

 A <- B <- C <- D
|
<version2>
<latest>

记住: Docker将永远不会删除图像!即使它删除了,在这种情况下它也不能删除图像,因为图像C是被标记的图像D的祖先的一部分。

即使你使用this script . sh this script . sh

当前的v2注册表现在支持通过DELETE /v2/<name>/manifests/<reference>删除。

看:https://github.com/docker/distribution/blob/master/docs/spec/api.md#deleting-an-image

<reference>可以从GET /v2/<name>/manifests/<tag>请求的Docker-Content-Digest头中获取(请注意,这个请求需要Accept: application/vnd.docker.distribution.manifest.v2+json头)。

使用它的脚本:https://github.com/byrnedo/docker-reg-tool

要使其工作,必须启用删除(REGISTRY_STORAGE_DELETE_ENABLED=true)。

为了真正释放磁盘空间,你需要运行垃圾收集

这真的很丑,但它工作,文本是在注册表:2.5.1上测试的。 我没有设法得到删除工作顺利,即使更新配置,以启用删除。这个ID真的很难找回,必须登录才能得到它,可能有些误解。无论如何,下面的工作:

  1. 进入容器

     docker exec -it registry sh
    
  2. 定义与容器和容器版本匹配的变量:

     export NAME="google/cadvisor"
    export VERSION="v0.24.1"
    
  3. 进入注册表目录:

     cd /var/lib/registry/docker/registry/v2
    
  4. 删除与你的散列相关的文件:

     find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
    
  5. < p >删除清单:

     rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. < p >注销

     exit
    
  7. 运行GC:

     docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. 如果所有操作都正确,则会显示一些关于已删除blobs的信息。

我的注册表也遇到过同样的问题,然后我尝试了下面博客页面上列出的解决方案。它的工作原理。

请注意,必须启用删除才能正常工作。你可以通过提供自定义配置或设置REGISTRY_STORAGE_DELETE_ENABLED=true来实现。

步骤1:列出存储库

$ curl -sS <domain-on-ip>:5000/v2/_catalog

答复的格式如下:

{
"repositories": [
<repo>,
...
]
}

步骤2:列出存储库标记

$ curl -sS <domain-on-ip>:5000/v2/<repo>/tags/list

答复的格式如下:

{
"name": <repo>,
"tags": [
<tag>,
...
]
}

步骤3:确定目标标记的摘要

$ curl -sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-o /dev/null \
-w '%header{Docker-Content-Digest}' \
<domain-or-ip>:5000/v2/<repo>/manifests/<tag>

注意,这里需要Accept标头。没有它,您将得到一个不同的值,删除将失败。

答复的格式如下:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

步骤4:删除清单

$ curl -sS -X DELETE <domain-or-ip>:5000/v2/<repo>/manifests/<digest>

步骤5:垃圾回收图像

在docker注册表容器中运行这个命令:

$ registry garbage-collect /etc/docker/registry/config.yml

这是我的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

有一些客户端(Python, Ruby等)正是这样做的。就我的品味而言,在我的注册表服务器上安装运行时(例如Python)是不可持续的,只是为了维护我的注册表!


所以deckschrubber是我的解决方案:

go install github.com/fraunhoferfokus/deckschrubber@latest
$GOPATH/bin/deckschrubber

超过给定年龄的图像将被自动删除。年龄可以使用-year-month-day或它们的组合来指定:

$GOPATH/bin/deckschrubber -month 2 -day 13 -registry http://registry:5000

更新: 这里有一个简短的介绍 on deckschrubber。

这个docker镜像包含一个bash脚本,可以用来从远程v2注册表中删除镜像: https://hub.docker.com/r/vidarl/remove_image_from_registry/ < / p >

在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

基于的简单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删除blobs。

短暂地;

1)对于docker repo的repodigest,必须输入以下命令;

## 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"
],
...
...
< p > ${消化}= sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6 < / p >

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 accept。

3)运行垃圾收集器

docker exec registry bin/registry garbage-collect --dry-run /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

你可以使用的另一个工具是registry-cli。例如,以下命令:

registry.py -l "login:password" -r https://your-registry.example.com --delete

将删除除最后10张图片外的所有图片。

还有一种方法可以根据创建日期从存储库中删除一些旧映像。

要做到这一点,请输入您的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

我通常都是用脚本做事情,但如果你已经在运行 一个从Joxit / docker-registry-ui构建的注册表UI容器,我发现在UI中单击删除按钮,一次删除一页图像更容易,然后在垃圾收集

删除除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

如果你发现自己需要创建一个删除策略来自动删除大量图像,我建议从相同的repo中查看regclient/regbot,它允许你定义该策略并让它运行以持续修剪注册表。


一旦图像被删除,在大多数情况下你需要垃圾收集你的注册表。例如,对于registry:2图像,它看起来像:

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

从不安全注册表(私有,无认证)中删除除latest标记以外的所有标记的脚本:

#!/bin/sh -eu
repo=$1
registry=${2-localhost:5000}


tags=`curl -sS "$registry/v2/$repo/tags/list" | jq -r .tags[]`
tag2digest() {
local tag=$1
curl -sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-o /dev/null \
-w '%header{Docker-Content-Digest}' \
"$registry/v2/$repo/manifests/$tag"
}
latest_digest=`tag2digest latest`
digests=`echo "$tags" \
| while IFS= read -r tag; do
tag2digest "$tag"
echo
done \
| sort \
| uniq`
digests=`echo "$digests" \
| grep -Fvx "$latest_digest"`
echo "$digests" \
| while IFS= read -r digest; do
curl -sS -X DELETE "$registry/v2/$repo/manifests/$digest"
done

用法:

$ ./rm-tags.sh <image> [<registry>]

在移除标签(更准确地说是清单)之后,运行垃圾收集:

$ registry garbage-collect /etc/docker/registry/config.yml

要支持Docker Hub和/或认证,请参见这些 答案