DockerLogo
Wenn man sich dazu entschließt, eine Docker-Registry zu installieren und zu konfigurieren, möchte man Images zentral abspeichern und verwalten können. Dies bietet viele Vorteile für den abteilungsübergreifenden Einsatz von Docker, aber auch ein paar Komplikationen im Umgang mit der Registry.
Wenn man mit dieser Registry arbeitet, entsteht irgendwann folgendes Problem. Das Löschen von Images funktioniert nicht wirklich.
Der Server ist fertig konfiguriert und man übermittelt Images an die Registry.
Der Speicherplatz wird zunehmend belegt, es liegen veraltete Images im Storage, die nicht mehr benötigt werden. Laut der Dokumentation von Docker, soll dieses Problem dadurch gelöst werden, dass man über die API Images “ganz einfach” löschen kann:
curl -k -X DELETE https://localhost:5000/v2/$imagename$/manifests/latest
Jedoch werden sich viele dann über diese (oder ähnliche) Fehlermeldung(en) ärgern:
{"errors":[{"code":"INVALID_DIGEST","message":"The digest is invalid"}]}
Laut Dokumentation, kann ein Image über die API nur dann gelöscht werden, wenn es durch den Namen und die Referenz(Tag, zum Beispiel “latest”) identifiziert werden kann, jedoch nur mittels “Digest”.
Ein Digest ist ein Wert um ein Image eindeutig identifizieren zu können. Er wird beim zum Beispiel bei einem Pull oder Push angezeigt. (Achtung! Der Digest ist nicht die Image ID, die beispielsweise bei einem “docker images” Command angezeigt wird.)

docker pull ubuntu
(...)
93f3596230c2: Pull complete
c06698a70f80: Pull complete
b6ea91fdc1f6: Pull complete
a53404c22cd2: Pull complete
Digest: sha256:c9f94277901034f3f40e73c861aee970da33dce9af2c02f13e1a4a72cdc0f8cb

Das Problem ist nun, dass der oben genannte DELETE nicht funktioniert. Eine kritische Alternative hierfür ist, auf der Commandline das Verzeichnis zu löschen
rm -rf ubuntu
Das Problem hierbei wären die Blobs, die als “Leiche” zurück bleiben würden. Wieso dies kritisch ist, möchte ich kurz versuchen zu erklären:
Jedes Image, besteht aus mehreren Layern, wobei die Layer aufeinander aufbauen. Der oberste Layer bekommt einen Tag (zum Beispiel “latest”). Blobs sind “BlobSums” (Digest) für die einzelnen Layer. Die einzelnen Layer sind wie der Digest des kompletten Images bei einem Pull, Push oder einem Api Request ersichtlich.
"schemaVersion": 1,
"name": "ubuntu",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a53404c22cd2ac9f68c352d17667ec1a2e36f22ca729983c40e1f24c106def6f"
},
{
"blobSum": "sha256:b6ea91fdc1f68eb0274174fd8c4eeea4baeee4986c942cbc7a21be82778e78b3"
},
{
(etc)

Beim Push in die Registry wird im Verzeichnis “blob” ein Eintrag für jeden Layer angelegt, mit der jeweiligen BlobSum. Löscht man nun ein “Image” aus der Registry, werden nur die Daten des Images gelöscht die im Verzeichnis “v2/repositories/$IMAGENAME” liegen. Auf den ersten Blick ist das Image gelöscht, denn in der Registry wird es nicht mehr als verfügbares Images zum Pull angezeigt. Die restlichen Layer jedoch und somit auch die Blobs, bleiben bestehen, womit sehr viel Speicherplatz mit nicht nutzbaren Daten belegt wird. Auch andere, von Docker erstellte Abhängigkeiten werden so nicht gelöscht. Das Problem ist dann, dass das Pushen des selben Images zurück in die Registry fehlerhaft sein wird, da versucht wird, Layer zu pushen, die schon hinterlegt sind und zu einem Image gehören aber nicht mehr zuordbar sind.
Komplizierter würde es werden, wenn man zum Beispiel nicht nur ein Image löschen möchte, sondern beispielsweise alle “untagged Images”. Dies sind veraltete Images, ohne einen Tag. Dies geschieht beispielsweise bei einem Push eines Images mit dem gleichem Namen.
Will oder kann man die Images nicht über die API löschen bietet “Burnettk” auf GitHub mit einem kleinen Skript eine gute Alternative.
Es handelt sich hier um ein Script, das ein Image anhand des Names identifiziert, und alles dazugehörige löscht. Was hierfür nötig ist, ist eine Environment Variable, die auf das Reporitory gesetzt wird. Als Beispiel:
export REGISTRY_DATA_DIR=/opt/registry_data/docker/registry/v2
Dannach kann das Script als normales Command genutzt werden. Um alle untagged Images zu löschen, kann man mit einer kleinen Schleife arbeiten, die innerhalb des Repoitory Verzeichnisses läuft:
for i in *; do delete_docker_registry_image --untagged --image $i; done;
Wenn man in diesem Verzeichnis, Images in einem weiteren Unterverzeichnis abspeichert, muss das Script kopiert und angepasst werden:
repositories_dir="repositories/$UNTERVERZEICHNIS"
An dieser Stelle bietet es sich auch an, diesen Löschvorgang in Form eines Crons zu automatisieren. Das Problem, dass die Registry zu wenig Speicherplatz frei hat, wäre so gelöst. Vielleicht kommt von Docker in den nächsten Docker-Engine Versionen eine brauchbare Lösung mit, jedoch ist das für mich die bisher beste Lösung.
dockerDocker, Docker, Docker!

Marius Gebert
Marius Gebert
Systems Engineer

Marius ist seit 2013 bei NETWAYS. Er hat 2016 seine Ausbildung zum Fachinformatiker für Systemintegration absolviert und ist nun im Web Services Team tätig. Hier kümmert er sich mit seinen Kollegen um die NWS Plattform und alles was hiermit zusammen hängt. 2017 hat Marius die Prüfung zum Ausbilder abgelegt und kümmert sich in seiner Abteilung um die Ausbildung unserer jungen Kollegen. Seine Freizeit verbringt Marius gerne an der frischen Luft und ist für jeden Spaß zu...