Container-Registries sind die Verteilstelle für Container-Images. Wer regelmäßig Container ausliefert, verbringt einen Teil der Arbeit dort: Images hochladen, Aufbewahrungsregeln setzen, Schwachstellen prüfen, Signaturen verwalten. Dieser Artikel beschreibt, was eine Registry tut und was im Betrieb zu beachten ist.
Was eine Registry ist
Eine Registry ist ein HTTP-Dienst mit einem standardisierten Protokoll. Er nimmt Images entgegen, verwaltet sie in Repositories und gibt sie auf Abruf heraus.
# Aufbau einer vollstaendigen Image-Referenz
registry.example.org / team/webapp : 1.4.2
| | |
Registry-Host Repository Tag (veraenderlich!)
registry.example.org/team/webapp@sha256:abc123...
|
Digest (unveraenderlich - die
eindeutige Kennung des Inhalts)
# Merksatz:
# Tag = Versionsetikett, das verschoben werden kann
# Digest = Fingerabdruck des Inhalts, unveraenderlich
Der wichtigste Grundsatz: Tags lügen
Ein Tag wie :latest oder :1.4 ist ein Zeiger, den jeder mit Schreibrecht verschieben kann. Zwei Systeme, die heute :1.4 ziehen, können morgen unterschiedliche Images haben. Das ist die Quelle des Problems, das in der Praxis am meisten Ärger macht („es läuft auf dem Testsystem, aber nicht in Produktion“).
# Schlecht: veraenderliches Tag in der Produktion
image: registry.example.org/team/webapp:latest
# Besser: unveraenderlicher Digest
image: registry.example.org/team/webapp@sha256:9f2a7c...
# Damit laeuft ueberall exakt derselbe Inhalt - dauerhaft.
# Nachteil: Man sieht nicht auf einen Blick, welche Version es ist.
# Deshalb: Versionsnummer als Tag UND Digest in der Beschreibung,
# so stehen beide Informationen zur Verfuegung:
# image: repo/webapp:1.4.2@sha256:9f2a7c...
# Digest eines lokalen Images ermitteln
docker inspect --format='{{index .RepoDigests 0}}' webapp:1.4.2
docker buildx imagetools inspect registry.example.org/team/webapp:1.4.2
Betriebsarten
- Öffentliche Registries: Docker Hub, GitHub Container Registry, Quay. Einfach für öffentliche Images; für private Images gibt es in der Regel Ratenbegrenzungen und Kosten.
- Selbst betrieben: Harbor (mit Rollen, Aufbewahrungsregeln, Scans, Signaturen), Distribution (der einfache Referenzserver), GitLab oder Gitea mit eingebauter Registry.
- Cloud-Dienste: ECR (AWS), ACR (Azure), Artifact Registry (Google) – mit Anbindung an die Rollen des jeweiligen Anbieters.
# Selbst betriebene Registry starten (einfache Variante)
docker run -d --name registry \
-p 5000:5000 \
-v /srv/registry:/var/lib/registry \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry:2
# Anmelden und hochladen
docker login registry.example.org
docker tag webapp:1.4.2 registry.example.org/team/webapp:1.4.2
docker push registry.example.org/team/webapp:1.4.2
# Inhalt auflisten (Registry-API)
curl -s https://registry.example.org/v2/_catalog | jq
curl -s https://registry.example.org/v2/team/webapp/tags/list | jq
# In Kubernetes anmelden
kubectl create secret docker-registry regcred \
--docker-server=registry.example.org \
--docker-username=ci \
--docker-password='...' \
--namespace=default
# oder sauberer: ein ServiceAccount mit imagePullSecrets
Aufbewahrung (Retention): das unterschätzte Thema
Ohne Regeln wächst eine Registry unbegrenzt. Jeder Build erzeugt Schichten; alte Versionen bleiben liegen und belegen Speicher – in einem Fall schnell mehrere hundert Gigabyte.
# Regelbeispiel
# - Die letzten 10 Versionen je Repository behalten
# - Alles aelter als 90 Tage entfernen, sofern nicht als Release markiert
# - Von Tags, die produktiv verwendet werden, immer behalten
# - Unbenutzte Schichten nach der Bereinigung einsammeln (garbage collection)
# Harbor: Retention Policies in der Oberflaeche, je Projekt konfigurierbar
# Distribution (einfach): Loeschen per API, danach GC
curl -X DELETE https://registry.example.org/v2/team/webapp/manifests/sha256:...
docker exec registry registry garbage-collect /etc/docker/registry/config.yml
Die Reihenfolge ist entscheidend: Erst das Manifest löschen, dann die Bereinigung laufen lassen. Andersherum bleiben die Schichten liegen, weil noch Manifeste darauf verweisen.
Sicherheit
- Schwachstellenprüfung: Beim Hochladen und regelmäßig danach. Werkzeuge wie Trivy, Grype oder Clair prüfen die Schichten gegen bekannte Schwachstellen-Datenbanken. Wichtig: Nicht nur melden, sondern Regeln definieren, was blockiert wird.
- Signaturen: cosign (aus dem Sigstore-Projekt) signiert Images mit einem Schlüssel oder einer kurzlebigen Identität. In Kubernetes lässt sich über einen Admission Controller erzwingen, dass nur signierte Images gestartet werden.
- Zugriffsrechte: Lesen und Schreiben trennen. Der CI-Job darf schreiben, die Laufzeitumgebung eines Clusters nur lesen. Ein kompromittierter CI-Job mit Schreibrecht kann sonst beliebige Images unterschieben.
- Kein anonymer Schreibzugriff. Eine offene Registry im Internet ist binnen Stunden voll mit fremden Images und wird als Verteilstelle für Schadsoftware missbraucht.
- Zugangsdaten nicht im Image. Keine Schlüssel, keine Kennwörter, keine Zertifikate im Abbild. Sie gehören in den Secrets-Speicher der Laufzeitumgebung.
- Basisimages aktuell halten. Ein Image erbt die Schwachstellen seines Basissystems. Regelmäßiges Neubauen ist deshalb kein Luxus, sondern Teil des Patch-Prozesses.
# Schwachstellen pruefen
trivy image registry.example.org/team/webapp:1.4.2
trivy image --severity HIGH,CRITICAL --exit-code 1 registry.example.org/team/webapp:1.4.2
# Vor dem Push pruefen
trivy image --exit-code 1 --severity CRITICAL webapp:1.4.2 || echo "Push blockiert"
# Signieren und pruefen
cosign sign registry.example.org/team/webapp:1.4.2
cosign verify registry.example.org/team/webapp:1.4.2
Fehlerbilder im Betrieb
- „unauthorized: authentication required“ – Zugangsdaten fehlen oder sind abgelaufen. In Kubernetes das
imagePullSecretam Pod oder ServiceAccount prüfen. - „manifest unknown“ – Tag oder Digest existiert nicht. Häufig ein Tippfehler im Repository-Namen oder ein Push, der nie durchgelaufen ist.
- „toomanyrequests“ – Ratenbegrenzung der öffentlichen Registry. Abhilfe: eigene Registry als Zwischenspeicher betreiben oder angemeldet ziehen.
- Image zieht nicht in Kubernetes – Die Reihenfolge prüfen: Stimmt der Name, existiert das Secret im richtigen Namespace, erlaubt die Netzwerkrichtlinie den Zugang?
- Platte voll – Aufbewahrungsregeln fehlen. Siehe oben: erst Manifeste löschen, dann Bereinigung.
Empfehlung
Für jeden ernsthaften Betrieb gehört eine eigene Registry dazu – schon wegen der Ratenbegrenzungen öffentlicher Dienste und der Frage, was passiert, wenn ein fremder Dienst ausfällt. Harbor ist für die meisten Fälle die richtige Wahl, weil Rollen, Aufbewahrungsregeln, Scans und Signaturen bereits eingebaut sind. Die drei Regeln, die dabei am meisten bewirken: Digests statt Tags in Produktion, Aufbewahrungsregeln von Anfang an und Schreibrechte nur für die Auslieferungskette.
Verwandte Grundlagen: Docker-Grundlagen, Registry, OCI-Standard, Container-Image.