Как удалить изображения из личного реестра 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. Мне не удалось заставить удалить работать гладко даже после обновления конфигурации, чтобы разрешить удаление. Удостоверение было действительно трудно получить, пришлось войти, чтобы получить его, возможно, какое-то недоразумение. Во всяком случае, следующие работы:
Вход в контейнер
docker exec -it registry sh
Определите переменные, соответствующие вашему контейнеру и версии контейнера:
export NAME="google/cadvisor" export VERSION="v0.24.1"
Перейдите в каталог реестра:
cd /var/lib/registry/docker/registry/v2
Удалите файлы, связанные с вашим хешем:
find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
Удалить манифесты:
rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
Выйти
exit
Запустите GC:
docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml
Если все было сделано правильно, отображается некоторая информация об удаленных 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}')