Как удалить изображения из личного реестра Docker?

Я запустил личный реестр докеров и хочу удалить все изображения, кроме latest из хранилища. Я не хочу удалять весь репозиторий, только некоторые изображения внутри него. Документы API не упоминают способ сделать это, но, конечно, это возможно?

17 ответов

Решение

В настоящее время вы не можете использовать API реестра для этой задачи. Это позволяет только удалить репозиторий или определенный тег.

Как правило, удаление репозитория означает, что все теги, связанные с этим репо, будут удалены.

Удаление тега означает, что связь между изображением и тегом удалена.

Ни один из вышеперечисленных не удалит одно изображение. Они остались на вашем диске.


Временное решение

Для этого обходного пути вам нужно, чтобы ваши изображения докера хранились локально.

Обходным решением для вашего решения может быть удаление всех тегов, кроме самых последних, и тем самым потенциально удаление ссылки на связанные изображения. Затем вы можете запустить этот скрипт, чтобы удалить все изображения, на которые не ссылаются ни теги, ни происхождение какого-либо использованного изображения.

Терминология (изображения и теги)

Рассмотрим графическое изображение, например, где заглавные буквы (A, B,...) представляют короткие идентификаторы изображений и <- означает, что изображение основано на другом изображении:

 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? Возможно нет!

Что произойдет, если вы удалите тег?

Если вы удалите тег <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: Список каталогов

Вы можете перечислить свои каталоги, позвонив по этому адресу:

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.5.1. Мне не удалось заставить удалить работать гладко даже после обновления конфигурации, чтобы разрешить удаление. Удостоверение было действительно трудно получить, пришлось войти, чтобы получить его, возможно, какое-то недоразумение. Во всяком случае, следующие работы:

  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-объектах.

Проблема 1

Вы упомянули, что это был ваш личный реестр докеров, поэтому вам, вероятно, нужно проверить API реестра, а не документ Doc Registry Hub, который вы указали.

Проблема 2

API реестра Docker - это протокол клиент-сервер, который зависит от того, будет ли сервер реализовывать удаление изображений в серверной части. (Похоже)

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"}

Есть некоторые клиенты (в 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.

Требование удалить все теги, кроме тега, усложняется, потому что на один и тот же манифест изображения могут указывать несколько тегов, поэтому, когда вы удаляете манифест для одного тега, вы можете эффективно удалить несколько тегов.

Есть несколько вариантов сделать это работоспособным. Один из них — отслеживать дайджест для последнего тега и удалять только манифесты для других дайджестов, или вы можете использовать другие вызовы API для удаления самих тегов.


Независимо от того, как вы это реализуете, сначала необходимо настроить реестр, чтобы разрешить удаление API. С минимальным изображением это включает его запуск с переменной окружения 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

The regctlиспользуемая здесь команда исходит от regclient и regctl tag rmлогика сначала пытается выполнить 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из того же репозитория, который позволяет вам определить эту политику и оставить ее включенной, чтобы постоянно сокращать ваш реестр.


После того, как образы будут удалены, в большинстве случаев вам потребуется выполнить сборку мусора в реестре. Например, с registry:2изображение, которое выглядит так:

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

Я обычно за то, чтобы делать что-то со сценариями, но если вы уже используете контейнер пользовательского интерфейса реестра, созданный из Joxit/docker-registry-ui, мне было проще просто нажать кнопку удаления в пользовательском интерфейсе и удалить страницу изображения за раз, а затем сборщик мусора.

Другой инструмент, который вы можете использовать, - это registry-cli. Например, эта команда:

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

удалит все изображения, кроме последних 10.

Вкратце;

1) Вы должны ввести следующую команду для 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

реестр - имя контейнера реестра.

Для более подробного объяснения введите описание ссылки здесь

Также есть способ удалить некоторые старые изображения из репозитория только на основании даты, когда они были созданы.

Для этого войдите в контейнер реестра 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

И, конечно же, не забудьте после этого выполнить команду "сбор мусора":

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

Вот сценарий, основанный на ответе Явуза Серта. Он удаляет все теги, которые не являются последней версией, и их тег превышает 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

Этот образ докера включает bash-скрипт, который можно использовать для удаления изображений из удаленного реестра v2: https://hub.docker.com/r/vidarl/remove_image_from_registry/

Простой рубиновый скрипт, основанный на этом ответе: 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 Script Удаляет все теги, расположенные в реестре, кроме самых последних.

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

Скрипт для удаления всего, кроме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 и/или аутентификации см. эти ответы .

Это самое простое решение, которое сработало для моей установки: использование частного реестра в роевом кластере.

Экспериментируйте с любым фильтром, который вам нужен.

      docker images | grep 'your_own_filter'

tail -n +4: сохранить последние 3 созданных изображения

awk '{print $3}': извлечет третий столбец, который представляет собой «ИДЕНТИФИКАТОР ИЗОБРАЖЕНИЯ».

Пример:

      docker rmi $(docker images | grep 'your_own_filter' | tail -n +4 | awk '{print $3}')
Другие вопросы по тегам