14. Deployments, ReplicaSets, and rollouts
14.1 The Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalog
labels: { app.kubernetes.io/name: catalog, app.kubernetes.io/part-of: orbit }
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels: { app.kubernetes.io/name: catalog } # IMMUTABLE after creation
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # extra pods allowed above replicas during rollout
maxUnavailable: 0 # never go below desired capacity
minReadySeconds: 10 # a new pod must stay Ready 10s before it "counts"
progressDeadlineSeconds: 300
template:
metadata:
labels: { app.kubernetes.io/name: catalog, app.kubernetes.io/part-of: orbit }
spec:
containers:
- name: catalog
image: orbit-catalog:dev
ports: [{ name: http, containerPort: 8080 }]
readinessProbe: { httpGet: { path: /readyz, port: http }, periodSeconds: 5 }
Deployment → manages ReplicaSets (one per pod-template version) →
manages Pods. A rolling update scales the new ReplicaSet up and the old
one down within the maxSurge/maxUnavailable budget, gated on
readiness. That is why readiness probes are what make rollouts safe.
| Setting | Effect |
|---|---|
maxUnavailable: 0, maxSurge: 1 |
slowest, safest; needs spare capacity for 1 extra pod |
maxUnavailable: 25%, maxSurge: 25% |
default; faster, temporarily reduced capacity |
strategy: Recreate |
kill all, then start new; for apps that can't run two versions at once (some DB-migration-coupled apps, single-writer apps) |
minReadySeconds |
catches "starts fine, crashes 5 s later" before the rollout proceeds |
progressDeadlineSeconds |
marks the rollout Progressing=False so CI/CD can detect a stuck deploy |
14.2 Rolling out and rolling back
kubectl set image deploy/catalog catalog=orbit-catalog:v2 # (demo only; GitOps in real life)
kubectl rollout status deploy/catalog --timeout=5m # CI uses this exit code
kubectl rollout history deploy/catalog
kubectl rollout undo deploy/catalog # back to previous ReplicaSet
kubectl rollout undo deploy/catalog --to-revision=3
kubectl rollout restart deploy/catalog # new pods, same spec (e.g. reload a Secret)
kubectl rollout pause deploy/catalog # batch several changes into one rollout
Try a broken rollout to see the safety net:
kubectl set image deploy/catalog catalog=orbit-catalog:does-not-exist
kubectl get pods -l app.kubernetes.io/name=catalog # old pods still serving; new one ImagePullBackOff
kubectl rollout status deploy/catalog # eventually: "exceeded its progress deadline"
kubectl rollout undo deploy/catalog
With maxUnavailable: 0, a bad image never reduces capacity. The
rollout stalls with all old pods still serving.
Best practice:
kubectl rollout undois a break-glass tool. In a GitOps setup the next sync reverts it. The durable rollback is reverting the Git commit (§36).
14.3 Why selector and labels matter
The selector is how a ReplicaSet finds "its" pods. If two Deployments' selectors overlap, they fight over pods. Use the recommended labels consistently:
labels:
app.kubernetes.io/name: orders # what it is
app.kubernetes.io/instance: orders-prod # which instance
app.kubernetes.io/version: "1.4.2" # NOT in the selector (it changes every release)
app.kubernetes.io/component: api
app.kubernetes.io/part-of: orbit
app.kubernetes.io/managed-by: kustomize