繁体   English   中英

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

[英]How to delete images from a private docker registry?

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

目前,您不能将 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>标签引用图像D我问你:你真的想删除除图像D所有内容吗? 可能不是!

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

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

 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 步:列出目录

您可以通过调用以下网址列出您的目录:

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 注册表容器中运行此命令:

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/

问题一

您提到它是您的私有 docker 注册表,因此您可能需要检查Registry API而不是Hub registry API doc ,这是您提供的链接。

问题二

docker registry 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}

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

这真的很难看,但它有效,文本在注册表: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. 删除清单:

     rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
  6. 登出

    exit
  7. 运行 GC:

     docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml
  8. 如果一切顺利,将显示有关已删除 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 的简短介绍

简要地;

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"
        ],
...
...

${digest} = 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

registry — 注册表容器名称。

有关更多详细说明,请在此处输入链接说明

您可以使用的另一个工具是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

此 docker 映像包含一个 bash 脚本,可用于从远程 v2 注册表中删除映像: https : //hub.docker.com/r/vidarl/remove_image_from_registry/

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

这是基于 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

我通常都喜欢用脚本做事,但如果你已经在运行一个由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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM