Як видалити зображення з приватного реєстру докера?


163

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


2
Прийнята відповідь більше не є правильною (хоча, безумовно, дуже хорошою). Ви можете видалити зображення за допомогою DELETE / v2 / <name> / manifests / <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>за допомогою API Docker REST, ви отримаєте це:

 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
Я відредагував свою відповідь із вирішенням, яке робить для мене роботу. Я сподіваюся, що це допомагає.
Konrad Kleine

Саме так - я розумію, що зображення будуються поступово, але мені потрібен спосіб обрізати ці мертві гілки. Дякую!
Лев

@KonradKleine як зробити графік зображень із списку зображень, отриманих з реєстру?
Рафаель Баррос

9
Збирання сміття нарешті надійшло до реєстру v2.4.0 docs.docker.com/registry/garbage-collection
Маркус Ліндберг

68

Я зіткнувся з такою ж проблемою з моїм реєстром, тоді я спробував рішення, перелічене нижче, зі сторінки блогу. Це працює.

Крок 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: Видаліть позначені маніфести

Запустіть цю команду в контейнері реєстру докера:

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/...", але це не змінило використаний простір на диску. Використання бін / реєстру github.com/docker/distribution v2.4.1 .
JamesThomasMoon1979

3
щоб видалити зображення, слід запустити контейнер реєстру з REGISTRY_STORAGE_DELETE_ENABLED = істинним параметром.
Аділ

3
Я встановлюю значення "видалити: увімкнено" в 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/


3
Деякі інші відповіді тут показують, як витягнути хеш маніфестації. Це необхідно, щоб перевести тег у те, що потрібно передати DELETE.
JamesThomasMoon1979

Отримання помилки, що не підтримується Я вказав належне значення дайджесту як еталон.
Вінстер

1
@ JamesThomasMoon1979 оновлено інструкціями щодо отримання контрольного хеша.
byrnedo

11
на V2 теж та отримую помилку{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem

1
Перевір редагування @Gadelkareem
byrnedo

17

Завдання 1

Ви згадали, що це ваш приватний реєстр докерів, тому вам, ймовірно, потрібно перевірити API реєстру замість документа API реєстру Hub , що є посиланням, яке ви надали.

Завдання 2

API реєстру докера - це протокол клієнт / сервер, залежно від того, чи потрібно видаляти зображення в бек-енді. (Я вважаю)

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

Детальне пояснення

Нижче я демонструю, як це працює зараз з вашого опису, як я розумію ваші запитання.

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

$ 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 реєстру, щоб видалити тег.
Konrad Kleine

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. Якщо все зроблено належним чином, відображається деяка інформація про видалені краплі.


я дотримувався тих же кроків, про які йшлося вище, але коли я перевіряв використання диска реєстру докерів {контейнер}, він показував те саме, що і раніше, ніж робити ці кроки ... будь-яка ідея чому?
Савіо Матвій

не працює. Легко перевірити. Виконайте наступні дії та знову натисніть на репо, він говорить "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

ОНОВЛЕННЯ : ось короткий вступ про колоду.


1
deckschrubberдосить непоганий - дуже простий в установці (одинарний бінарний), і дозволяє видаляти зображення за назвою (із збігом регулярних виразів), а також за віком.
RichVel

ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon

@scythargon сказати неможливо, якщо ваша специфікація правильна.
тремтіння

Попередження: в моєму сховищі буквально нічого не робилося.
Річард Уорбуртон

На жаль, не буде видалено зображення, на яких немає тегів. Тож якщо ви продовжуєте натискати зображення на один тег, він перевірятиме лише те, що позначено тегами, а не інші.
Krystian

1

Коротко;

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

register - назва контейнера реєстру.

Для більш детального пояснення введіть опис посилання тут



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

Простий сценарій рубіну, заснований на цій відповіді: register_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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.