Comment supprimer des images d'un registre docker privé?

163

J'exécute un registre de docker privé et je souhaite supprimer toutes les images sauf celles latestd'un référentiel. Je ne veux pas supprimer tout le référentiel, juste certaines des images qu'il contient. Les documents de l'API ne mentionnent pas un moyen de le faire, mais c'est sûrement possible?

Leo
la source
2
La réponse acceptée n'est plus correcte (bien que certainement très bonne). Vous pouvez supprimer des images en utilisant DELETE / v2 / <nom> / manifestes / <reference>: docs.docker.com/registry/spec/api/#deleting-an-image
Michael Zelensky

Réponses:

116

Actuellement, vous ne pouvez pas utiliser l'API de registre pour cette tâche. Il vous permet uniquement de supprimer un référentiel ou une balise spécifique.

En général, la suppression d'un référentiel signifie que toutes les balises associées à ce référentiel sont supprimées.

La suppression d'une étiquette signifie que l'association entre une image et une étiquette est supprimée.

Aucun des éléments ci-dessus ne supprimera une seule image. Ils sont laissés sur votre disque.


solution de contournement

Pour cette solution de contournement, vous devez avoir vos images Docker stockées localement.

Une solution de contournement pour votre solution serait de supprimer toutes les balises sauf les dernières et donc de supprimer potentiellement la référence aux images associées. Ensuite, vous pouvez exécuter ce script pour supprimer toutes les images, qui ne sont référencées par aucune balise ou l'ascendance d'une image utilisée.

Terminologie (images et tags)

Considérons un graphique d'image comme celui - ci où les lettres majuscules ( A, B, ...) représentent court ID d'image et des <-moyens que l'image est basée sur une autre image:

 A <- B <- C <- D

Maintenant, nous ajoutons des balises à l'image:

 A <- B <- C <- D
           |    |
           |    <version2>
           <version1>

Ici, la balise fait <version1>référence à l'image Cet la balise fait <version2>référence à l'image D.

Affiner votre question

Dans votre question, vous avez dit que vous vouliez supprimer

toutes les images sauf le latest

. Or, cette terminologie n'est pas tout à fait correcte. Vous avez mélangé des images et des tags. En regardant le graphique, je pense que vous conviendrez que la balise <version2>représente la dernière version. En fait, selon cette question, vous pouvez avoir une balise qui représente la dernière version:

 A <- B <- C <- D
           |    |
           |    <version2>
           |    <latest>
           <version1>

Puisque la <latest>balise fait référence à l'image, Dje vous demande: voulez-vous vraiment tout supprimer sauf l'image D? Probablement pas!

Que se passe-t-il si vous supprimez une balise?

Si vous supprimez la balise <version1>à l'aide de l'API Docker REST, vous obtiendrez ceci:

 A <- B <- C <- D
                |
                <version2>
                <latest>

N'oubliez pas: Docker ne supprimera jamais une image! Même si c'est le cas, dans ce cas, il ne peut pas supprimer une image, puisque l'image Cfait partie de l'ascendance de l'image Dqui est étiquetée.

Même si vous utilisez ce script , aucune image ne sera supprimée.

Quand une image peut être supprimée

Sous la condition que vous puissiez contrôler quand quelqu'un peut tirer ou pousser dans votre registre (par exemple en désactivant l'interface REST). Vous pouvez supprimer une image d'un graphique d'image si aucune autre image n'est basée dessus et qu'aucune balise n'y fait référence.

Notez que dans le graphique suivant, l'image Dn'est pas basée sur Cmais sur B. Par conséquent, Dne dépend pas de C. Si vous supprimez la balise <version1>dans ce graphique, l'image Cne sera utilisée par aucune image et ce script peut la supprimer.

 A <- B <--------- D
      \            |
       \           <version2>
        \          <latest>
         \ <- C
              |
              <version1>

Après le nettoyage, votre graphique d'image ressemble à ceci:

 A <- B <- D
           |
           <version2>
           <latest>

c'est ce que tu veux?

Konrad Kleine
la source
Vous avez raison: la suppression des balises n'a pas vraiment supprimé les images associées. Existe-t-il alors un moyen de supprimer les images avec un accès ssh à la boîte?
Leo
1
J'ai modifié ma réponse avec une solution de contournement qui fait le travail pour moi. J'espère que cela aide.
Konrad Kleine
C'est exactement cela - je comprends que les images sont construites progressivement, mais j'ai besoin d'un moyen d'élaguer ces branches mortes. Merci!
Leo
@KonradKleine Comment créer un graphique d'images à partir de la liste d'images que vous obtenez du registre?
Rafael Barros
9
Le ramassage des ordures est enfin arrivé au registre v2.4.0 docs.docker.com/registry/garbage-collection
Markus Lindberg
68

J'ai rencontré le même problème avec mon registre, puis j'ai essayé la solution ci-dessous à partir d'une page de blog. Ça marche.

Étape 1: Liste des catalogues

Vous pouvez lister vos catalogues en appelant cette URL:

http://YourPrivateRegistyIP:5000/v2/_catalog

La réponse sera dans le format suivant:

{
  "repositories": [
    <name>,
    ...
  ]
}

Étape 2: Liste des balises pour le catalogue associé

Vous pouvez lister les balises de votre catalogue en appelant cette URL:

http://YourPrivateRegistyIP:5000/v2/<name>/tags/list

La réponse sera dans le format suivant:

{
"name": <name>,
"tags": [
    <tag>,
    ...
]

}

Étape 3: Répertoriez la valeur du manifeste pour la balise associée

Vous pouvez exécuter cette commande dans le conteneur de registre 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)}'

La réponse sera dans le format suivant:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Exécutez la commande ci-dessous avec la valeur manifeste:

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

Étape 4: supprimer les manifestes marqués

Exécutez cette commande dans votre conteneur de registre docker:

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

Voici mon 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
Yavuz Sert
la source
Les commandes ont réussi, y compris les messages concernant " Deleteing blob: /docker/...", mais cela n'a pas modifié l'espace disque utilisé. Utilisation de bin / registry github.com/docker/distribution v2.4.1 .
JamesThomasMoon1979
3
pour supprimer une image, vous devez exécuter le conteneur de registre avec le paramètre REGISTRY_STORAGE_DELETE_ENABLED = true.
Adil
3
J'ai défini la valeur "delete: enabled" sur true dans le fichier /etc/docker/registry/config.yml. Pour cette configuration, pas besoin de définir la variable REGISTRY_STORAGE_DELETE_ENABLED @AdilIlhan
Yavuz Sert
re: étape 3, le "Docker-Content-Digest" doit être dans l'en-tête? (ne pas le voir dans la sortie curl). Dois-je être en mesure de voir les résultats du marquage d'un manifeste avant de supprimer le (s) manifeste (s) marqué (s) pour savoir que je l'ai marqué avec succès? Il semble me donner une réponse 202, peu importe ce que je lui envoie.
SteveW
la Docker-Content-Digestpièce doit être en minuscules (testé sur moteur docker v18.09.2) iecurl -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)}'
Muhammad Abdurrahman
63

Le courant v2registre prend désormais en charge la suppression par l' intermédiaireDELETE /v2/<name>/manifests/<reference>

Voir: https://github.com/docker/distribution/blob/master/docs/spec/api.md#deleting-an-image

Utilisation de travail: https://github.com/byrnedo/docker-reg-tool

Edit: Le manifeste <reference>ci-dessus peut être récupéré de la demande à

GET /v2/<name>/manifests/<tag>

et vérifier l'en- Docker-Content-Digesttête de la réponse.

Edit 2: Vous devrez peut-être exécuter votre registre avec l'ensemble d'environnement suivant:

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3: Vous devrez peut-être exécuter un garbage collection pour libérer cet espace disque: https://docs.docker.com/registry/garbage-collection/

Byrnedo
la source
3
Quelques autres réponses ici montrent comment extraire le hachage de référence manifeste. Ceci est nécessaire pour traduire une balise en quelque chose à laquelle passer DELETE.
JamesThomasMoon1979
Obtention d'une erreur non prise en charge. J'ai donné la valeur de digestion appropriée comme référence.
Winster
1
@ JamesThomasMoon1979 mis à jour avec des instructions sur l'obtention du hachage de référence.
byrnedo
11
sur V2 aussi et obtenant une erreur{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem
1
Vérifiez la modification @Gadelkareem
byrnedo le
17

Problème 1

Vous avez mentionné qu'il s'agissait de votre registre docker privé, vous devez donc probablement vérifier l' API de registre au lieu de la documentation de l'API de registre Hub , qui est le lien que vous avez fourni.

Problème 2

L'API de registre docker est un protocole client / serveur, c'est à la mise en œuvre du serveur de décider s'il faut supprimer les images dans le back-end. (J'imagine)

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

Explication détaillée

Ci-dessous, je montre comment cela fonctionne maintenant à partir de votre description comme ma compréhension de vos questions.

Vous exécutez, vous exécutez le registre docker privé, j'utilise celui par défaut et j'écoute dans le 5000port

docker run -d -p 5000:5000 registry

Ensuite, je marque l'image locale et j'y pousse.

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

Après cela, vous pouvez utiliser l' API de registre pour vérifier qu'elle existe dans votre registre docker privé

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags
{"latest": "e54ca5efa2e962582a223ca9810f7f1b62ea9b5c3975d14a5da79d3bf6020f37"}

Maintenant, je peux supprimer la balise en utilisant cette API !!

$ curl -X DELETE localhost:5000/v1/repositories/ubuntu/tags/latest
true

Vérifiez à nouveau, la balise n'existe pas dans votre serveur de registre privé

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
Larry Cai
la source
2
S'il vous plaît, voir ma réponse ci-dessous pour une explication pourquoi cette commande n'aide pas.
Konrad Kleine
2
Vous supprimez les balises mais pas les données d'image. Ce dernier est fait par le script que je référence après avoir supprimé une balise. J'utilise également l'API de registre pour supprimer une balise.
Konrad Kleine
3
c'est jusqu'à l'implémentation de chaque serveur d'API de registre, du point de vue du protocole, il n'existe pas.
Larry Cai
Vous avez raison de dire que cela dépend de la mise en œuvre - mais je cherchais / je cherche un moyen de supprimer les données d'image de l' registryimage canonique . SSH et exécuter un script fonctionnent, même si ce n'est pas idéal. En remarque, je pense toujours que c'est une API incomplète - vous pouvez supprimer des balises, mais si vous OBTENEZ, /imagesvous voyez toujours toutes les données d'image restantes.
Leo
Merci d'avoir mentionné Registry API, recherchait un moyen de SUPPRIMER une image dans un registre privé. Cette méthode est assez bonne pour moi, même si c'est juste «untagging» sans réellement libérer de l'espace disque.
Howard Lee
16

C'est vraiment moche mais ça marche, le texte est testé sur le registre: 2.5.1. Je n'ai pas réussi à faire fonctionner la suppression correctement, même après la mise à jour de la configuration pour activer la suppression. L'identifiant était vraiment difficile à récupérer, il a fallu se connecter pour l'obtenir, peut-être un malentendu. Quoi qu'il en soit, les travaux suivants:

  1. Connectez-vous au conteneur

    docker exec -it registry sh
    
  2. Définissez des variables correspondant à votre conteneur et à votre version de conteneur:

    export NAME="google/cadvisor"
    export VERSION="v0.24.1"
    
  3. Accédez au répertoire du registre:

    cd /var/lib/registry/docker/registry/v2
    
  4. Supprimez les fichiers liés à votre hachage:

    find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
    
  5. Supprimer les manifestes:

    rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. Se déconnecter

    exit
    
  7. Exécutez le GC:

    docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. Si tout a été fait correctement, des informations sur les objets blob supprimés s'affichent.

Hirro
la source
J'ai suivi les mêmes étapes mentionnées ci-dessus, mais quand j'ai vérifié l'utilisation du disque du registre docker {container}, il montrait la même chose qu'avant de faire les étapes ... une idée pourquoi?
Savio Mathew
ne fonctionne pas. Facile à vérifier. Lorsque vous effectuez les étapes suivantes et poussez à nouveau le dépôt, il indique "064794e955a6: le calque existe déjà"
Oduvan
8

Il existe des clients (en Python, Ruby, etc.) qui font exactement cela. À mon goût, il n'est pas viable d'installer un runtime (par exemple Python) sur mon serveur de registre, juste pour gérer mon registre!


Alors deckschrubberma solution:

go get github.com/fraunhoferfokus/deckschrubber
$GOPATH/bin/deckschrubber

les images plus anciennes qu'un âge donné sont automatiquement supprimées. L' âge peut être spécifié en utilisant -year, -month, -dayou une combinaison d'entre eux:

$GOPATH/bin/deckschrubber -month 2 -day 13 -registry http://registry:5000

MISE À JOUR : voici une brève introduction sur deckschrubber.

Yan Foto
la source
1
deckschrubberest assez bon - très facile à installer (binaire unique), et vous permet de supprimer les images par nom (avec correspondance regex) ainsi que par âge.
RichVel
ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon
@scythargon impossible de dire si votre spécification est correcte.
jitter
Attention: n'a littéralement rien fait sur mon référentiel.
Richard Warburton
Malheureusement, il ne supprimera pas les images qui n'ont pas de balises. Donc, si vous continuez à pousser des images sur un seul tag, il ne vérifiera que celui qui est tagué, pas les autres.
Krystian
1

Brièvement;

1) Vous devez taper la commande suivante pour RepoDigests d'un repo 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) Utiliser l'API REST du registre

  ##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

Vous devriez obtenir un 202 Accepté pour un appel réussi.

3-) Exécutez Garbage Collector

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

registre - nom du conteneur de registre.

Pour plus d'explications détaillées, entrez la description du lien ici

fgul
la source
0

Sous le script Bash Supprime toutes les balises situées dans le registre à l'exception de la dernière.

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

Après cette course

docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
Shrijan Tiwari
la source
Pourriez-vous expliquer un peu plus comment utiliser ce bon script?
Khalil Gharbaoui
0

Script ruby ​​simple basé sur cette réponse: registry_cleaner .

Vous pouvez l'exécuter sur une machine locale:

./registry_cleaner.rb --host=https://registry.exmpl.com --repository=name --tags_count=4

Et puis sur la machine de registre, supprimez les objets blob avec /bin/registry garbage-collect /etc/docker/registry/config.yml.

Ilya Krigouzov
la source
0

Voici un script basé sur la réponse de Yavuz Sert. Il supprime toutes les balises qui ne sont pas la dernière version et leur balise est supérieure à 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
la source