Wie lösche ich Bilder aus einer privaten Docker-Registrierung?

162

Ich führe eine private Docker-Registrierung aus und möchte alle Bilder außer den latestaus einem Repository löschen . Ich möchte nicht das gesamte Repository löschen, sondern nur einige der darin enthaltenen Bilder. Die API-Dokumente erwähnen keine Möglichkeit, dies zu tun, aber es ist sicherlich möglich?

Löwe
quelle
1
Akzeptierte Antwort ist nicht mehr korrekt (obwohl definitiv sehr gut). Sie können Bilder mit DELETE / v2 / <Name> / Manifeste / <Referenz> entfernen
Michael Zelensky

Antworten:

116

Derzeit können Sie die Registrierungs-API für diese Aufgabe nicht verwenden. Sie können nur ein Repository oder ein bestimmtes Tag löschen.

Im Allgemeinen bedeutet das Löschen eines Repositorys, dass alle diesem Repo zugeordneten Tags gelöscht werden.

Das Löschen eines Tags bedeutet, dass die Zuordnung zwischen einem Bild und einem Tag gelöscht wird.

Keines der oben genannten Elemente löscht ein einzelnes Bild. Sie verbleiben auf Ihrer Festplatte.


Problemumgehung

Für diese Problemumgehung müssen Ihre Docker-Images lokal gespeichert sein.

Eine Problemumgehung für Ihre Lösung besteht darin, alle bis auf die neuesten Tags zu löschen und dadurch möglicherweise den Verweis auf die zugehörigen Bilder zu entfernen. Anschließend können Sie dieses Skript ausführen , um alle Bilder zu entfernen, auf die von keinem Tag oder der Abstammung eines verwendeten Bildes verwiesen wird.

Terminologie (Bilder und Tags)

Betrachten wir ein Bild Graph wie dieser , wo die Großbuchstaben ( A, B, ...) Kurz Bild - IDs und stellen <-bedeutet , dass ein Bild auf ein anderes Bild basiert:

 A <- B <- C <- D

Jetzt fügen wir dem Bild Tags hinzu:

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

Hier wird der Tag <version1>verweist auf das Bild Cund den Tag <version2>verweist auf das Bild D.

Verfeinern Sie Ihre Frage

In Ihrer Frage sagten Sie, dass Sie entfernen wollten

alle Bilder außer dem latest

. Diese Terminologie ist nicht ganz richtig. Sie haben Bilder und Tags gemischt. Wenn Sie sich die Grafik ansehen, stimmen Sie zu, dass das Tag <version2>die neueste Version darstellt. In der Tat können Sie gemäß dieser Frage ein Tag haben, das die neueste Version darstellt:

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

Da das <latest>Tag auf das Bild verweist, Dfrage ich Sie: Wollen Sie wirklich alle außer dem Bild löschen D? Wahrscheinlich nicht!

Was passiert, wenn Sie ein Tag löschen?

Wenn Sie das Tag <version1>mit der Docker REST-API löschen, erhalten Sie Folgendes :

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

Denken Sie daran: Docker löscht niemals ein Bild! Selbst wenn dies der Fall ist, kann in diesem Fall kein Bild gelöscht werden, da das Bild CTeil der Abstammung für Ddas mit Tags versehene Bild ist.

Selbst wenn Sie dieses Skript verwenden , wird kein Bild gelöscht.

Wenn ein Bild gelöscht werden kann

Unter der Bedingung, dass Sie steuern können, wann jemand Ihre Registrierung abrufen oder verschieben kann (z. B. durch Deaktivieren der REST-Schnittstelle). Sie können ein Bild aus einem Bilddiagramm löschen, wenn kein anderes Bild darauf basiert und kein Tag darauf verweist.

Beachten Sie, dass in der folgenden Grafik, das Bild Dwird nicht auf der Grundlage , Csondern auf B. Daher Dhängt nicht davon ab C. Wenn Sie ein Tag <version1>in diesem Diagramm löschen , wird das Bild Cvon keinem Bild verwendet, und dieses Skript kann es entfernen.

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

Nach der Bereinigung sieht Ihr Bilddiagramm folgendermaßen aus:

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

Ist das was du willst?

Konrad Kleine
quelle
Sie haben Recht - durch das Löschen der Tags wurden die zugehörigen Bilder nicht gelöscht. Gibt es dann eine Möglichkeit, die Bilder zu löschen, wenn ssh Zugriff auf die Box hat?
Leo
1
Ich habe meine Antwort mit einer Problemumgehung bearbeitet, die den Job für mich erledigt. Ich hoffe, es hilft.
Konrad Kleine
Genau das ist es - ich verstehe, dass Bilder inkrementell erstellt werden, aber ich brauche einen Weg, um diese toten Zweige zu beschneiden. Vielen Dank!
Leo
@KonradKleine Wie erstellen Sie das Bilddiagramm aus der Liste der Bilder, die Sie aus der Registrierung erhalten?
Rafael Barros
9
Das Sammeln von Müll ist endlich in der Registrierung v2.4.0 angekommen. Docs.docker.com/registry/garbage-collection
Markus Lindberg
68

Ich hatte das gleiche Problem mit meiner Registrierung, als ich die unten aufgeführte Lösung von einer Blog-Seite aus ausprobierte. Es klappt.

Schritt 1: Kataloge auflisten

Sie können Ihre Kataloge auflisten, indem Sie diese URL aufrufen:

http://YourPrivateRegistyIP:5000/v2/_catalog

Die Antwort hat das folgende Format:

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

Schritt 2: Auflisten von Tags für den zugehörigen Katalog

Sie können Tags Ihres Katalogs auflisten, indem Sie diese URL aufrufen:

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

Die Antwort hat das folgende Format:

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

}}

Schritt 3: Listen Sie den Manifestwert für das zugehörige Tag auf

Sie können diesen Befehl im Docker-Registrierungscontainer ausführen:

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)}'

Die Antwort hat das folgende Format:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Führen Sie den folgenden Befehl mit dem Manifestwert aus:

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

Schritt 4: Löschen Sie markierte Manifeste

Führen Sie diesen Befehl in Ihrem Docker-Registrierungscontainer aus:

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

Hier ist meine 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
quelle
Die Befehle waren erfolgreich, einschließlich Meldungen zu " Deleteing blob: /docker/...", aber der verwendete Speicherplatz wurde nicht geändert. Verwenden von bin / registry github.com/docker/distribution v2.4.1 .
JamesThomasMoon1979
3
Um ein Bild zu löschen, sollten Sie den Registrierungscontainer mit dem Parameter REGISTRY_STORAGE_DELETE_ENABLED = true ausführen.
Adil
3
Ich habe den Wert "delete: enabled" in der Datei /etc/docker/registry/config.yml auf true gesetzt. Für diese Konfiguration muss die Variable REGISTRY_STORAGE_DELETE_ENABLED @AdilIlhan
Yavuz Sert
Betreff: Schritt 3, muss der "Docker-Content-Digest" im Header sein? (nicht in der Curl-Ausgabe zu sehen). Sollte ich in der Lage sein, die Ergebnisse des Markierens eines Manifests anzuzeigen, bevor ich die markierten Manifeste lösche, um zu wissen, dass ich es erfolgreich markiert habe? Es scheint mir eine Antwort zu geben, egal was ich sende.
SteveW
Das Docker-Content-DigestTeil sollte in Kleinbuchstaben geschrieben sein (getestet mit Docker Engine v18.09.2), dhcurl -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

Die aktuelle v2Registrierung unterstützt jetzt das Löschen überDELETE /v2/<name>/manifests/<reference>

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

Arbeitsnutzung: https://github.com/byrnedo/docker-reg-tool

Bearbeiten: Das <reference>obige Manifest kann von der Anforderung an abgerufen werden

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

und Überprüfen des Docker-Content-DigestHeaders in der Antwort.

Bearbeiten 2: Möglicherweise müssen Sie Ihre Registrierung mit dem folgenden env-Satz ausführen:

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3: Möglicherweise müssen Sie die Garbage Collection ausführen, um diesen Speicherplatz freizugeben: https://docs.docker.com/registry/garbage-collection/

byrnedo
quelle
3
Einige andere Antworten hier zeigen, wie man einen offensichtlichen Referenz-Hash extrahiert. Dies ist erforderlich, um ein Tag in etwas zu übersetzen, an das übergeben werden soll DELETE.
JamesThomasMoon1979
Nicht unterstützter Fehler. Ich habe den richtigen Digest-Wert als Referenz angegeben.
Winster
@ Winster: Mit welcher Version der Registrierung verbinden Sie sich?
Byrnedo
1
@ JamesThomasMoon1979 aktualisiert mit Anweisungen zum Abrufen des Referenz-Hash.
Byrno
11
auf V2 auch und Fehler bekommen{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem
17

Problem 1

Sie haben erwähnt, dass es sich um Ihre private Docker-Registrierung handelt. Daher müssen Sie wahrscheinlich die Registrierungs-API anstelle des Hub-Registrierungs-API-Dokuments überprüfen , das der von Ihnen angegebene Link ist.

Problem 2

Die Docker-Registrierungs-API ist ein Client / Server-Protokoll. Es liegt an der Implementierung des Servers, ob die Images im Back-End entfernt werden sollen. (Ich vermute)

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

Detaillierte Erklärung

Unten zeige ich anhand Ihrer Beschreibung, wie es für Ihre Fragen funktioniert, wie es jetzt funktioniert.

Wenn Sie eine private Docker-Registrierung ausführen, verwende ich die Standardregistrierung und lausche im 5000Port

docker run -d -p 5000:5000 registry

Dann tagge ich das lokale Bild und drücke es hinein.

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

Danach können Sie mithilfe der Registrierungs-API überprüfen, ob sie in Ihrer privaten Docker-Registrierung vorhanden ist

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

Jetzt kann ich das Tag mit dieser API löschen !!

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

Überprüfen Sie erneut, ob das Tag auf Ihrem privaten Registrierungsserver nicht vorhanden ist

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
Larry Cai
quelle
2
In meiner Antwort unten finden Sie eine Erklärung, warum dieser Befehl nicht hilft.
Konrad Kleine
2
Sie löschen Tags, aber nicht die Bilddaten. Letzteres erfolgt durch das Skript, auf das ich verweise, nachdem Sie ein Tag gelöscht haben. Ich verwende auch die Registrierungs-API, um ein Tag zu löschen.
Konrad Kleine
3
Es ist bis zur Implementierung jedes Registrierungs-API-Servers aus Sicht des Protokolls nicht vorhanden.
Larry Cai
Sie haben Recht, dass es an der Implementierung liegt - aber ich habe nach einer Möglichkeit gesucht, Bilddaten aus dem kanonischen registryBild zu löschen . SSHing in und Ausführen eines Skripts funktioniert, auch wenn es nicht ideal ist. Als Randnotiz denke ich immer noch, dass es eine unvollständige API ist - Sie können Tags löschen, aber wenn Sie GET bekommen, sehen /imagesSie immer noch alle übrig gebliebenen Bilddaten.
Leo
Vielen Dank, dass Sie die Registrierungs-API erwähnt haben. Sie haben nach einer Möglichkeit gesucht, ein Bild in der privaten Registrierung zu löschen. Diese Methode ist gut genug für mich, auch wenn sie nur "nicht markiert" ist, ohne tatsächlich Speicherplatz freizugeben.
Howard Lee
16

Das ist wirklich hässlich, aber es funktioniert, Text wird in der Registrierung getestet: 2.5.1. Ich habe es nicht geschafft, dass das Löschen reibungslos funktioniert, selbst nachdem die Konfiguration aktualisiert wurde, um das Löschen zu aktivieren. Die ID war wirklich schwer abzurufen, musste sich anmelden, um sie zu erhalten, vielleicht ein Missverständnis. Auf jeden Fall funktioniert folgendes:

  1. Melden Sie sich beim Container an

    docker exec -it registry sh
    
  2. Definieren Sie Variablen, die Ihrem Container und Ihrer Containerversion entsprechen:

    export NAME="google/cadvisor"
    export VERSION="v0.24.1"
    
  3. Wechseln Sie in das Registrierungsverzeichnis:

    cd /var/lib/registry/docker/registry/v2
    
  4. Löschen Sie Dateien, die sich auf Ihren Hash beziehen:

    find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
    
  5. Manifeste löschen:

    rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. Ausloggen

    exit
    
  7. Führen Sie den GC aus:

    docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. Wenn alles richtig gemacht wurde, werden einige Informationen zu gelöschten Blobs angezeigt.

Hirro
quelle
Ich habe die gleichen Schritte ausgeführt, die oben erwähnt wurden, aber als ich die Datenträgerverwendung der Docker-Registrierung {Container} überprüft habe, wurde dieselbe angezeigt wie vor den Schritten ... Irgendeine Idee, warum?
Savio Mathew
funktioniert nicht Einfach zu überprüfen. Wenn Sie die folgenden Schritte ausführen und das Repo erneut drücken, wird "064794e955a6: Layer existiert bereits"
angezeigt
8

Es gibt einige Clients (in Python, Ruby usw.), die genau das tun. Für meinen Geschmack ist es nicht nachhaltig, eine Laufzeit (z. B. Python) auf meinem Registrierungsserver zu installieren, nur um meine Registrierung zu verwalten!


So deckschrubberist meine Lösung:

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

Bilder, die älter als ein bestimmtes Alter sind, werden automatisch gelöscht. Das Alter kann angegeben werden -year, -month, -dayoder eine Kombination von ihnen:

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

UPDATE : Hier ist eine kurze Einführung zu Deckschrubber.

Yan Foto
quelle
1
deckschrubberist ziemlich gut - sehr einfach zu installieren (einzelne Binärdatei) und ermöglicht das Löschen von Bildern nach Namen (mit Regex-Übereinstimmung) sowie nach Alter.
RichVel
ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon
@scythargon unmöglich zu sagen, ob Ihre Spezifikation korrekt ist.
Jitter
Warnung: Ich habe buchstäblich nichts in meinem Repository getan.
Richard Warburton
Leider werden keine Bilder entfernt, die keine Tags haben. Wenn Sie also weiterhin Bilder auf ein einzelnes Tag übertragen, wird nur das mit Tags versehene und nicht das andere Tag überprüft.
Krystian
1

Kurz;

1) Sie müssen den folgenden Befehl für RepoDigests eines Docker-Repos eingeben.

## 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) Verwenden Sie die Registrierungs-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

Sie sollten eine 202 Accepted für einen erfolgreichen Aufruf erhalten.

3-) Führen Sie Garbage Collector aus

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

Registrierung - Name des Registrierungscontainers.

Für eine ausführlichere Erklärung geben Sie hier die Linkbeschreibung ein

fgul
quelle
0

Unterhalb des Bash-Skripts Löscht alle in der Registrierung befindlichen Tags mit Ausnahme der neuesten.

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

Nach diesem Lauf

docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
Shree Tiwari
quelle
Könnten Sie etwas näher auf die Verwendung dieses feinen Skripts eingehen?
Khalil Gharbaoui
0

Einfaches Ruby-Skript basierend auf dieser Antwort: registry_cleaner .

Sie können es auf einem lokalen Computer ausführen:

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

Und dann auf dem Registrierungscomputer Blobs mit entfernen /bin/registry garbage-collect /etc/docker/registry/config.yml.

Ilya Krigouzov
quelle
0

Hier ist ein Skript, das auf der Antwort von Yavuz Sert basiert. Es werden alle Tags gelöscht, die nicht die neueste Version sind, und ihr Tag ist größer als 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
quelle