Kubernetes Deployments verwalten keine Pods
07.06.2026
Thema: alle Beiträge zu Kubernetes
Zwischen Deployment und Pod liegt das ReplicaSet. Wer diese Schicht kennt, versteht Skalierung, Rolling Update und Rollback als eine einzige Mechanik.
Inhaltsverzeichnis
- Was du brauchst
- Das kleinste Deployment
- Skalieren
- Das Rolling Update
- Die zwei Regler
- Warum der Rollback funktioniert
- Wo der Rollback aufhört
- Fazit
Zwischen deinem Deployment und deinen Pods liegt eine Schicht, die du im Alltag nie zu Gesicht bekommst: das ReplicaSet.
Ein Deployment startet keine Pods. Es legt ReplicaSets an, und die starten Pods. Solange alles läuft, ist das eine Formalie. In dem Moment, in dem du ein Update ausrollst, ist es die Erklärung für alles: warum ein Rolling Update schrittweise geht, was maxSurge und maxUnavailable wirklich justieren, warum ein Rollback überhaupt funktioniert und ab wann er es nicht mehr tut.
Dieses Tutorial baut die Schicht sichtbar auf. Am Ende hast du ein Deployment skaliert, aktualisiert und zurückgerollt und dabei jedes Mal gesehen, welches ReplicaSet gerade was tut.
Was du brauchst
Einen laufenden Kubernetes-Cluster und ein verbundenes kubectl. Falls du keinen hast, reicht ein lokaler: Kubernetes lokal testen mit k3d braucht zwei Minuten.
Das kleinste Deployment
Lege deployment.yaml an:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: web
image: nginx:1.31.0-alpine3.23
resources:
requests:
cpu: "50m"
memory: "16Mi"
limits:
cpu: "50m"
memory: "16Mi"
Fünf Felder tragen die Sache:
.metadata.nameist der Name des Deployments.spec.replicasist die gewünschte Anzahl Pods.spec.selectorsagt, an welchen Labels das Deployment seine Pods erkennt.spec.templateist die Vorlage, aus der Pods entstehen.spec.template.metadata.labelsmuss zu.spec.selector.matchLabelspassen, sonst findet das Deployment die Pods nicht, die es selbst erzeugt hat
Anlegen und nachsehen:
kubectl apply -f deployment.yaml
kubectl get deploy/nginx
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 113m
1/1 heißt: ein Pod läuft tatsächlich, einer war gewünscht.
Und jetzt der Blick auf die Schicht, um die es geht:
kubectl get replicasets
NAME DESIRED CURRENT READY AGE
nginx-5589648df4 1 1 1 113m
Das hast du nie angelegt. Das Deployment hat es angelegt. Der Namenszusatz 5589648df4 ist ein Hash über die Pod-Vorlage, und genau daran hängt gleich alles Weitere.
Skalieren
kubectl scale deploy/nginx --replicas 5
kubectl get pods
Fünf Pods, alle Running. Was dabei passiert ist, steht eine Ebene tiefer: Das Deployment hat keine Pods gestartet, es hat die Wunschzahl im ReplicaSet hochgesetzt. Gestartet hat das ReplicaSet.
kubectl get replicasets
NAME DESIRED CURRENT READY AGE
nginx-5589648df4 5 5 5 114m
Derselbe Hash wie vorher. Die Pod-Vorlage hat sich nicht geändert, also bleibt es dasselbe ReplicaSet.
Der Zustand wird gehalten
Lösch einen Pod:
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-5589648df4-7qck7 1/1 Running 0 80m
nginx-5589648df4-9g952 1/1 Running 0 20s
nginx-5589648df4-jvjwc 1/1 Running 0 20s
nginx-5589648df4-pvlv7 1/1 Running 0 20s
nginx-5589648df4-sjq2s 1/1 Running 0 20s
kubectl delete pod/nginx-5589648df4-7qck7
Der Pod verschwindet und ist sofort wieder da, unter neuem Namen. Auch das macht nicht das Deployment, sondern das ReplicaSet: Es vergleicht laufend Ist gegen Soll und gleicht die Differenz aus. Mehr ist Self-Healing an dieser Stelle nicht.
Runterskalieren geht genauso:
kubectl scale deploy/nginx --replicas 2
Das Rolling Update
Hier wird die zweite Schicht sichtbar. Öffne ein zweites Terminal und lass dir die Events anzeigen:
kubectl events --watch | grep ScalingReplicaSet
Jetzt eine neue Image-Version ausrollen, der Alltagsfall eines Patch-Updates:
kubectl set image deploy/nginx web=nginx:1.31.1-alpine3.23
Im Event-Fenster läuft das hier durch:
Scaled up replica set nginx-5644799d59 from 0 to 1
Scaled down replica set nginx-5589648df4 from 3 to 2
Scaled up replica set nginx-5644799d59 from 1 to 2
Scaled down replica set nginx-5589648df4 from 2 to 1
Scaled up replica set nginx-5644799d59 from 2 to 3
Scaled down replica set nginx-5589648df4 from 1 to 0
Lies die Namen. Es sind zwei verschiedene ReplicaSets, und sie wechseln sich ab. Weil sich die Pod-Vorlage geändert hat, ändert sich ihr Hash, und das Deployment legt ein zweites ReplicaSet an. Danach dreht es an zwei Zahlen: die neue hoch, die alte runter, abwechselnd, bis die alte bei null steht.
Das ist das ganze Rolling Update. Kein eigener Mechanismus, sondern zwei ReplicaSets und eine Reihenfolge.
Geht beim Hochfahren der neuen Version etwas schief, stoppt das Deployment an dieser Stelle. Die alte Version läuft dann einfach weiter, und deine Nutzer merken von dem gescheiterten Rollout nichts.
Den Fortschritt siehst du mit:
kubectl rollout status deploy/nginx
Und dass die neuen Pods wirklich das neue Image tragen, mit kubectl describe pod/nginx-...:
Containers:
web:
Container ID: containerd://1322934d3f030465bc27c480e9ce0a500e9ed8e1aa4d58295a8827cbeda8f594
Image: nginx:1.31.1-alpine3.23
...
Die zwei Regler
Die Reihenfolge oben ist nicht in Stein gemeißelt. Sie ergibt sich aus zwei Werten, und die stehen in .spec.strategy.
Zuerst die Grundsatzentscheidung, .spec.strategy.type:
RollingUpdateist der Standard und macht genau das, was du gerade gesehen hastRecreatefährt erst alle alten Pods herunter und dann die neuen hoch. Also mit Ausfall, dafür ohne zwei parallel laufende Versionen
Beim Rolling Update kommen zwei Stellschrauben dazu, beide als Anzahl oder Prozentwert:
.spec.strategy.rollingUpdate.maxUnavailablesagt, wie viele Pods während des Updates fehlen dürfen.spec.strategy.rollingUpdate.maxSurgesagt, wie viele Pods zusätzlich über die Wunschzahl hinaus laufen dürfen
Anders gesagt: Der eine Regler erlaubt eine Unterdeckung, der andere eine Überdeckung. Zusammen bestimmen sie, wie weit sich altes und neues ReplicaSet überlappen dürfen.
Sicher und langsam
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
Damit läuft zu jedem Zeitpunkt die volle Anzahl Pods. Es darf genau ein zusätzlicher Pod entstehen, und erst wenn der bereit ist, verschwindet ein alter. Sicher, aber Pod für Pod und entsprechend langsam.
Beide Werte gleichzeitig auf 0 geht nicht: Dann dürfte weder ein Pod fehlen noch einer dazukommen, und das Update käme nie in Gang.
Aufgabe: maxUnavailable auf 100 Prozent
Leg ein Deployment mit 5 Replicas an und setz nur maxUnavailable: 100%. Roll ein Update aus und beobachte dabei kubectl events --watch | grep ScalingReplicaSet.
Lösung:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 5
selector:
matchLabels:
app: nginx
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 100%
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: web
image: nginx:1.31.0-alpine3.23
resources:
requests:
cpu: "50m"
memory: "16Mi"
limits:
cpu: "50m"
memory: "16Mi"
Die Events:
Scaled up replica set nginx-5644799d59 from 0 to 2
Scaled down replica set nginx-5589648df4 from 5 to 0
Scaled up replica set nginx-5644799d59 from 2 to 5
Erwartet hättest du wahrscheinlich, dass sofort alle fünf alten Pods verschwinden. Stattdessen kommen erst zwei neue.
Die Erklärung steht in kubectl describe deploy/nginx: maxSurge hast du nicht gesetzt, also gilt der Standardwert 25%. Bei 5 Replicas sind das aufgerundet 2 Pods, die vorab entstehen dürfen. Erst danach greift dein maxUnavailable.
Das ist die eigentliche Lehre dieser Aufgabe: Du hast nie nur einen Regler gesetzt. Der andere steht weiter auf seinem Standardwert und redet mit. Ohne Angabe stehen beide auf 25%.
Warum der Rollback funktioniert
kubectl rollout undo deploy/nginx
Kubernetes warnt dabei, dass ein späteres kubectl apply den Undo wieder aufheben könnte. Der Undo selbst greift trotzdem, die Pods tragen danach wieder 1.31.0.
Und jetzt die Frage, die das Tutorial zusammenhält: Woher nimmt Kubernetes die alte Version?
kubectl get replicasets
Da steht sie. Das alte ReplicaSet ist nicht gelöscht worden, es steht nur auf 0. Ein Rollback ist deshalb kein Rückwärts-Deployment, sondern derselbe Vorgang wie vorhin, nur mit vertauschten Rollen: Die Zahl im alten ReplicaSet geht hoch, die im neuen runter.
Deshalb kommst du auch nur so weit zurück, wie ReplicaSets aufbewahrt werden. Standard sind 10, einstellbar über .spec.revisionHistoryLimit.
Aufgabe: revisionHistoryLimit auf 1
Leg ein Deployment mit einem Pod an und setz revisionHistoryLimit: 1. Roll auf nginx:1.31.1-alpine3.23 und danach zurück auf nginx:1.31.0-alpine3.23, und sieh dir dazwischen jeweils die ReplicaSets an.
Lösung:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: web
image: nginx:1.31.0-alpine3.23
resources:
requests:
cpu: "50m"
memory: "16Mi"
limits:
cpu: "50m"
memory: "16Mi"
kubectl set image deploy/nginx web=nginx:1.31.1-alpine3.23
kubectl get replicasets
kubectl set image deploy/nginx web=nginx:1.31.0-alpine3.23
kubectl get replicasets
Es bleiben zwei ReplicaSets: das aktive und genau ein altes. Praktisch heißt das, du kommst genau einen Schritt zurück.
Wer hier aufräumt, spart ein paar Objekte in etcd und verkürzt gleichzeitig seinen Fluchtweg. Das ist eine Abwägung, keine Optimierung.
Wo der Rollback aufhört
Ein letzter Punkt, weil er im Ernstfall der teuerste ist.
Ein Rollback holt das ReplicaSet zurück und damit das Image. Deine Daten holt er nicht zurück. Bei einer zustandslosen Anwendung wie dem nginx hier fällt das nicht auf. Bei allem, was auf ein PersistentVolume schreibt, ist es der entscheidende Unterschied, und es kann bedeuten, dass der Rollback zwar durchläuft, der Pod danach aber gar nicht mehr startet.
Wie das konkret aussieht, steht in Von Redis 7.2 auf Valkey migrieren: Dort macht ein Versionssprung das Datenformat auf dem Volume für die alte Version unlesbar, und kubectl rollout undo hilft nicht mehr.
Die Regel dahinter ist einfach und gilt über Kubernetes hinaus: Das Image ist austauschbar, das Volume nicht.
Fazit
- Ein Deployment verwaltet ReplicaSets, ein ReplicaSet verwaltet Pods. Der Hash im ReplicaSet-Namen ist der Fingerabdruck der Pod-Vorlage
- Skalieren ändert eine Zahl im bestehenden ReplicaSet. Self-Healing ist derselbe Abgleich, nur ungewollt ausgelöst
- Ein Rolling Update legt ein zweites ReplicaSet an und dreht abwechselnd an beiden Zahlen. Mehr ist es nicht
maxUnavailableundmaxSurgebestimmen die erlaubte Überlappung. Setzt du nur einen davon, gilt für den anderen weiter der Standard von 25 Prozentkubectl rollout undofunktioniert, weil die alten ReplicaSets liegen bleiben. Wie weit du zurückkommst, sagtrevisionHistoryLimit- Zurück kommt das Image, nicht die Daten
© 2026 Trutz Software Consulting GmbH. CC BY 4.0: Dieser Text darf mit Namensnennung weiterverwendet und bearbeitet werden, auch kommerziell. Code-Beispiele darin stehen unter Apache 2.0. Ausgenommen sind Marke und Logo sowie gekennzeichnete Zitate Dritter. Was das genau heißt.
Newsletter
Erhalte praxisnahe Tutorials, Best Practices und echte Lösungen für die häufigsten DevOps-Probleme direkt in Dein Postfach.
Datenschutz: Deine E-Mail-Adresse wird DSGVO-konform in Deutschland gespeichert. Keine Spam, nur relevante Inhalte. Eine E-Mail alle 1 bis 2 Wochen.