Warum Ihre Container-Tags Sie belügen
Sie führen docker pull myapp:latest aus und denken, Sie wissen genau, was Sie bekommen. Aber das tun Sie nicht. Dieser Tag könnte morgen auf ein anderes Image verweisen – oder er verweist bereits jetzt auf ein anderes Image als noch vor einer Stunde. Der gleiche Tag, der gleiche Befehl, aber völlig andere Software in der Produktion.
Das ist kein theoretisches Problem. Teams haben Stunden damit verbracht, Produktionsprobleme zu debuggen, nur um festzustellen, dass das Image, das ihrer Meinung nach lief, gar nicht das Image war, das tatsächlich lief. Der Tag sagte das eine, aber der Inhalt war etwas völlig anderes.
Wo Images leben
Bevor wir über Tags und ihre Probleme sprechen, müssen wir klären, wo Container-Images tatsächlich gespeichert werden.
Wenn Sie ein Docker-Image auf Ihrem Laptop bauen, existiert dieses Image nur auf Ihrem Rechner. Um es auf einem Server, in einer Staging-Umgebung oder in der Produktion auszuführen, brauchen Sie einen Ort, um dieses Image zu speichern und zu teilen. Dieser Ort heißt Registry.
Stellen Sie sich eine Registry als Dateiserver für Container-Images vor, aber mit zusätzlichen Fähigkeiten. Sie verwaltet Versionen, prüft die Integrität und kontrolliert, wer auf was zugreifen kann. Wenn Sie docker pull nginx:latest ausführen, ziehen Sie von Docker Hub, einer öffentlichen Registry. Jeder kann von dort pullen, aber auch jeder kann dorthin pushen – ein Grund, warum Sie sich für die Produktion nicht auf öffentliche Images verlassen sollten.
Die meisten Unternehmen betreiben ihre eigene interne Registry. Gängige Optionen sind Harbor, Nexus, GitLab Container Registry oder Amazon ECR. Eine interne Registry bietet drei Vorteile:
- Herkunftsnachweis: Sie wissen, woher jedes Image kommt. Keine zufälligen Images aus dem Internet.
- Geschwindigkeit: Die Übertragung von Images über Ihr internes Netzwerk ist viel schneller als das Ziehen aus dem öffentlichen Internet.
- Kontrolle: Sie entscheiden, wer Images pushen und wer sie pullen darf.
Ohne eine Registry hat Ihre Deployment-Pipeline keinen Ort, um die von ihr gebauten Images abzulegen. Mit einer Registry haben Sie eine einzige Quelle der Wahrheit für jedes Image, das Ihr Team produziert.
Tags sind Labels, keine Identifikatoren
Jedes Image in einer Registry hat einen oder mehrere Tags. Ein Tag ist ein Label, das Sie an ein Image anhängen, um eine bestimmte Version oder Variante zu kennzeichnen. Sie sehen Tags überall: myapp:1.0.0, myapp:staging, myapp:latest.
Tags sind praktisch. Sie bieten eine menschenlesbare Möglichkeit, auf Images zu verweisen. Anstatt sich einen langen Hash zu merken, tippen Sie myapp:1.0.0 und erhalten das gewünschte Image.
Aber Tags haben einen grundlegenden Fehler: Sie sind veränderbar. Sie können jederzeit ändern, auf welches Image ein Tag verweist. Heute zeigt myapp:latest auf den Image-Hash abc123. Morgen, nach einem neuen Build, zeigt er auf def456. Der Tag bleibt gleich, aber das Image ändert sich.
Diese Veränderbarkeit verursacht echte Probleme. Stellen Sie sich ein Szenario vor, in dem Ihre Staging-Umgebung myapp:staging verwendet. Ihre Pipeline baut ein neues Image, taggt es als staging und pusht es. Jetzt läuft in Staging der neue Code. Aber was, wenn jemand manuell den staging-Tag mit einem anderen Image überschreibt? Oder wenn Ihre Pipeline versehentlich den falschen Build taggt? Sie haben keine Möglichkeit zu wissen, welches Image tatsächlich in Staging läuft, es sei denn, Sie überprüfen den Digest.
Das Muster der unveränderlichen Tags
Die Lösung ist die Verwendung von unveränderlichen Tags – Tags, die sich nach ihrer Erstellung nie ändern. Sobald Sie einem Image einen Tag zuweisen, bleibt dieser Tag für immer bei diesem Image.
Unveränderliche Tags enthalten eindeutige Informationen, die einen bestimmten Build identifizieren. Übliche Muster sind:
- Semantische Version:
myapp:1.2.3 - Git-Commit-Hash:
myapp:a1b2c3d - Build-Zeitstempel:
myapp:20240515-1430 - Pipeline-Run-ID:
myapp:build-456
Mit unveränderlichen Tags können Sie genau nachvollziehen, welcher Code in welcher Umgebung läuft. Wenn jemand einen Fehler in der Produktion meldet, schauen Sie auf den Tag, finden den Commit-Hash und wissen genau, welcher Code dieses Image erzeugt hat. Kein Rätselraten, kein „Welche Version von latest ist das?“
Einige Teams kombinieren Ansätze. Sie verwenden semantische Versionen für Releases, Commit-Hashes für Entwicklungs-Builds und Zeitstempel für automatisierte Deployments. Der Schlüssel ist Konsistenz: Jeder Build erzeugt einen eindeutigen Tag, der nie wiederverwendet wird.
Digests: Die Wahrheit
Tags sind praktisch, aber unzuverlässig. Wenn Sie absolute Sicherheit darüber haben wollen, welches Image Sie ausführen, müssen Sie Digests verwenden.
Jedes Container-Image hat einen Digest. Ein Digest ist ein kryptografischer Hash des Image-Inhalts. Er sieht ungefähr so aus: sha256:abc123def456.... Der Digest ist eindeutig für dieses spezifische Image. Wenn sich der Image-Inhalt auch nur um ein Byte ändert, ändert sich der Digest vollständig.
Im Gegensatz zu Tags können Digests nicht verschoben oder neu zugewiesen werden. Der Digest sha256:abc123 wird für immer auf genau dasselbe Image verweisen. Sie können diesen Digest unmöglich auf ein anderes Image umleiten.
Wenn Sie ein Image per Digest pullen, erhalten Sie garantiert genau das Image, das Sie erwarten. Es gibt keine Unklarheiten, keine Chance, eine andere Version zu bekommen.
# Das ist sicher – Sie bekommen genau das, was Sie angefordert haben
docker pull myapp@sha256:abc123def456
Tags und Digests gemeinsam in Pipelines nutzen
In der Praxis verwenden Sie Tags und Digests gemeinsam. Tags machen Images menschenlesbar und leicht referenzierbar. Digests bieten die Garantie, dass Sie das richtige Image ausführen.
So geht eine typische Pipeline damit um:
- Das Image bauen.
- Mit einem unveränderlichen Tag versehen (Commit-Hash oder Version).
- Das Image in die Registry pushen.
- Den Digest in Ihren Deployment-Metadaten festhalten.
- Bei der Promotion von Staging in die Produktion prüfen, ob der Digest exakt übereinstimmt.
Der Prüfschritt ist entscheidend. Wenn Sie ein Image von Staging in die Produktion überführen, sollten Sie nicht nur den Tag prüfen. Sie sollten prüfen, ob der Digest identisch ist. Das verhindert, dass jemand den Staging-Tag mit einem anderen Image überschreibt und dieses falsche Image in die Produktion übernommen wird.
Hier ist ein praktisches Beispiel, wie Sie den Digest nach dem Pushen eines Images abrufen und damit ein Deployment festlegen können:
# Image mit einem unveränderlichen Tag bauen und pushen
docker build -t myapp:build-456 .
docker push myapp:build-456
# Den Digest des gepushten Images abrufen
digest=$(docker inspect --format='{{index .RepoDigests 0}}' myapp:build-456 | cut -d'@' -f2)
echo "Digest: $digest"
# Den Digest in einem Kubernetes-Deployment-Manifest verwenden
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp@$digest
EOF
# Das Deployment anwenden
kubectl apply -f deployment.yaml
Dadurch wird sichergestellt, dass jeder Pod genau dasselbe Image ausführt, unabhängig davon, was mit den Tags in der Registry passiert.
Viele Deployment-Tools unterstützen Digest-basierte Deployments. Kubernetes zum Beispiel erlaubt es, ein Image per Digest statt per Tag anzugeben:
spec:
containers:
- name: myapp
image: myregistry.com/myapp@sha256:abc123def456
Dadurch wird sichergestellt, dass jeder Pod genau dasselbe Image ausführt, unabhängig davon, was mit den Tags in der Registry passiert.
Praktische Checkliste
Führen Sie vor Ihrem nächsten Deployment diese Prüfungen durch:
- Jeder Build erzeugt einen eindeutigen, unveränderlichen Tag (Commit-Hash, Version oder Zeitstempel)
- Der
latest-Tag wird niemals in Produktions-Deployments verwendet - Ihre Pipeline zeichnet den Digest jedes von ihr gebauten Images auf
- Die Image-Promotion zwischen Umgebungen prüft den Digest, nicht nur den Tag
- Ihre Registry hat Zugriffskontrollen, um unbefugte Pushs zu verhindern
- Alte Images werden regelmäßig bereinigt, um Speicherüberlauf zu vermeiden
Das Fazit
Tags sind für Menschen. Digests sind für Maschinen. Nutzen Sie Tags, um sich das Leben leichter zu machen, aber nutzen Sie Digests, um Ihre Deployments zuverlässig zu machen. Wenn in der Produktion etwas schiefgeht, wollen Sie genau wissen, was läuft. Ein veränderbarer Tag wird es Ihnen nicht sagen. Ein Digest wird es tun – jedes einzelne Mal.