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

163

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

Лео
источник
2
Принятый ответ больше не является правильным (хотя определенно очень хорошим). Вы можете удалить изображения, используя DELETE / v2 / <name> / manifest / <reference>: docs.docker.com/registry/spec/api/#deleting-an-image
Михаил Зеленский

Ответы:

116

В настоящее время вы не можете использовать 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>

Это то, что вы хотите?

Конрад Кляйн
источник
Вы правы - удаление тегов на самом деле не привело к удалению связанных изображений. Есть ли способ удалить изображения, предоставившие ssh доступ к коробке, тогда?
Лев
1
Я отредактировал свой ответ с помощью обходного пути, который делает эту работу за меня. Я надеюсь, что это помогает.
Конрад Кляйн
Это именно так - я понимаю, что изображения создаются постепенно, но мне нужен способ обрезать эти мертвые ветви. Спасибо!
Лев
@KonradKleine, как вы делаете график изображений из списка изображений, которые вы получаете из реестра?
Рафаэль Баррос
9
Сборщик
Маркус Линдберг
68

Я столкнулся с той же проблемой с моим реестром, а затем попробовал решение, перечисленное ниже, со страницы блога. Оно работает.

Шаг 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

Запустите приведенную ниже команду со значением manifest:

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
Явуз Серт
источник
Команды были выполнены успешно, включая сообщения о " Deleteing blob: /docker/...", но это не изменило используемое дисковое пространство. Использование bin / registry github.com/docker/distribution v2.4.1 .
JamesThomasMoon1979
3
чтобы удалить изображение, вы должны запустить контейнер реестра с параметром REGISTRY_STORAGE_DELETE_ENABLED = true.
Адил
3
Я установил значение «delete: enabled» в true /etc/docker/registry/config.yml. Для этой конфигурации нет необходимости устанавливать переменную REGISTRY_STORAGE_DELETE_ENABLED @AdilIlhan
Yavuz Sert
re: шаг 3, «Docker-Content-Digest» должен быть в шапке? (не видеть его в выводе curl). Должен ли я увидеть результаты пометки манифеста перед удалением помеченного (ых) манифеста (ов), чтобы узнать, что я успешно пометил его? Кажется, я получаю ответ 202 независимо от того, что я отправлю.
SteveW
Docker-Content-Digestчасть должна быть в нижнем регистре (проверено на Docker двигателя v18.09.2) , т.е.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)}'
Мухаммад Абдуррахман
63

Текущий 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: Возможно, вам придется запустить ваш реестр со следующим набором env:

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3: вам может потребоваться запустить сборку мусора, чтобы освободить это дисковое пространство: https://docs.docker.com/registry/garbage-collection/

byrnedo
источник
3
Некоторые другие ответы здесь показывают, как извлечь манифест ссылочного хэша. Это необходимо для перевода тега во что-то, что нужно передать DELETE.
JamesThomasMoon1979
Получение неподдерживаемой ошибки. Я дал правильное значение дайджеста в качестве ссылки.
Уинстер
1
@ JamesThomasMoon1979 обновлен инструкциями по получению ссылочного хэша.
byrnedo
11
на V2 тоже и получаю ошибку{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem
1
Проверьте редактирование @Gadelkareem
byrnedo
17

Проблема 1

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

Проблема 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"}
Ларри Кай
источник
2
Пожалуйста, посмотрите мой ответ ниже для объяснения, почему эта команда не помогает.
Конрад Кляйн
2
Вы удаляете теги, но не данные изображения. Последнее делается с помощью сценария, на который я ссылаюсь после того, как вы удалили тег. Я также использую API реестра для удаления тега.
Конрад Кляйн
3
это зависит от реализации каждого сервера API реестра, с точки зрения протокола, его не существует.
Ларри Кай
Вы правы, что дело за реализацией - но я искал / ищу способ удалить данные изображения из канонического registryизображения. SSHing и запуск скрипта работает, даже если он не идеален. В качестве примечания я все еще думаю, что это неполный API - вы можете удалять теги, но если вы ПОЛУЧИТЕ, /imagesвы все равно увидите все оставшиеся данные изображения.
Лев
Спасибо за упоминание реестра API, искал способ УДАЛИТЬ изображение в личном реестре. Этот метод достаточно хорош для меня, даже если он просто «размечен» без фактического освобождения дискового пространства.
Говард Ли
16

Это действительно некрасиво, но работает, текст проверен в реестре: 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-объектах.

hirro
источник
я выполнил те же действия, что и выше, но когда я проверил использование диска реестра {контейнера} на диске, он показывал то же, что и до выполнения шагов ... есть идеи, почему?
Савио Мэтью
не работает Легко проверить. Когда вы делаете следующие шаги и снова нажимаете на репо, он говорит: «064794e955a6: Слой уже существует»
Oduvan
8

Есть некоторые клиенты (в 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
deckschrubberэто очень хорошо - очень прост в установке (один двоичный файл), и позволяет удалять изображения как по имени (с регулярным выражением), так и по возрасту.
RichVel
ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon
@scythargon невозможно сказать, правильна ли ваша спецификация.
джиттер
Предупреждение: буквально ничего не сделал в моем хранилище.
Ричард Уорбертон
К сожалению, он не удалит изображения, которые не имеют тегов. Поэтому, если вы продолжаете помещать изображения в один тег, он будет проверять только один тег, а не остальные.
Кристиан
1

Кратко;

1) Вы должны ввести следующую команду для RepoDigests репозитория Docker;

## 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 Принято для успешного вызова.

3-) Запустить сборщик мусора

docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml

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

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

fgul
источник
0

Ниже 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
Шриджан Тивари
источник
Не могли бы вы подробнее рассказать о том, как использовать этот прекрасный сценарий?
Халил Гарбауи
0

Простой скрипт 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.

Илья Кригузов
источник
0

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