Chapter 10 of 47 · Controllers, part 2

Deployment: updates and rollbacks

A Deployment is a manager of ReplicaSets. You get everything a ReplicaSet does, plus the thing it could not do: change the version of your app with no downtime, and go back if the new version is broken.

Deployment → ReplicaSet → Pod

A Deployment owns ReplicaSets, which own PodsDeployment posts owns a current ReplicaSet with three v2 Pods and an old ReplicaSet scaled to zero, kept for rollback. Deployment postsyou write this ReplicaSet posts-c4b21image v2 · replicas 3 · current ReplicaSet posts-7d9f8image v1 · replicas 0 · history Pod v2 Pod v2 Pod v2 0 Pods, but the recipe for v1is still here for a rollback
You manage the Deployment. It manages ReplicaSets. Each ReplicaSet manages its Pods.

Each version of your Pod template gets its own ReplicaSet. Updating means: scale the new ReplicaSet up, scale the old one down. Rolling back means doing the same in reverse.

The YAML

Look familiar? Change kind from ReplicaSet to Deployment and you have a working Deployment. The extra fields are optional.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: posts
spec:
  replicas: 3
  selector:
    matchLabels:
      app: posts
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  revisionHistoryLimit: 10
  template:
    metadata:
      labels:
        app: posts
    spec:
      containers:
        - name: posts
          image: micro-posts:v1
          ports:
            - containerPort: 4000

kind: Deployment

Same replicas, selector and template as a ReplicaSet. The Deployment creates a ReplicaSet from them for you; you never write the ReplicaSet yourself.

Rolling update, live

Ship v2 and watch the two ReplicaSets trade Pods. Then ship the broken v3, see the rollout stop by itself, and roll back.

How fast it rolls

Setting (replicas: 4)What happens
maxSurge: 1, maxUnavailable: 0Up to 5 Pods, never fewer than 4 ready. Safe, one at a time.
Default: 25%, 25%Up to 5 Pods, at least 3 ready. A bit faster.
maxSurge: 0, maxUnavailable: 1Never more than 4 Pods (no spare capacity needed), but one fewer during the update.
type: RecreateAll 4 old Pods stop, then 4 new start. Downtime; use only when two versions must never run together.

The kubectl commands

  1. $ kubectl apply -f deployment.yaml
    deployment.apps/posts created
    $ kubectl get deploy,rs,pods
    NAME                    READY   UP-TO-DATE   AVAILABLE
    deployment.apps/posts   3/3     3            3
    
    NAME                               DESIRED   CURRENT   READY
    replicaset.apps/posts-7d9f8c6b5d   3         3         3
    
    NAME                         READY   STATUS
    pod/posts-7d9f8c6b5d-4kq2p   1/1     Running
    pod/posts-7d9f8c6b5d-m7x4t   1/1     Running
    pod/posts-7d9f8c6b5d-zt9wn   1/1     Running

    One file created three layers. Names chain: posts → posts-7d9f8c6b5d → posts-7d9f8c6b5d-4kq2p. The hash identifies the Pod template.

  2. $ kubectl set image deployment/posts posts=micro-posts:v2
    deployment.apps/posts image updated
    $ kubectl rollout status deployment/posts
    Waiting for deployment "posts" rollout to finish: 1 out of 3 new replicas have been updated...
    Waiting for deployment "posts" rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "posts" successfully rolled out

    Quick way to change the image. The declarative way: edit the image in deployment.yaml and kubectl apply again.

  3. $ kubectl get rs
    NAME               DESIRED   CURRENT   READY
    posts-7d9f8c6b5d   0         0         0      ← v1, kept
    posts-c4b21f7a9e   3         3         3      ← v2

    The old ReplicaSet stays at 0 for rollbacks.

  4. $ kubectl rollout history deployment/posts
    REVISION  CHANGE-CAUSE
    1         <none>
    2         <none>

    Every template change is a revision. Set the kubernetes.io/change-cause annotation to fill in CHANGE-CAUSE.

  5. $ kubectl rollout undo deployment/posts
    deployment.apps/posts rolled back
    $ kubectl rollout undo deployment/posts --to-revision=1

    Back to the previous revision, or a specific one.

  6. $ kubectl scale deployment/posts --replicas=5
    deployment.apps/posts scaled

    Scale the Deployment, never its ReplicaSet directly: the Deployment would just set it back.

  7. $ kubectl delete deployment posts
    deployment.apps "posts" deleted

    Deletes its ReplicaSets and their Pods too.

Recap

Quick check

You change replicas: 3 to 5 and apply. Does that create a new ReplicaSet and a new revision?
Show answer

No. Only changes to the Pod template create a new ReplicaSet. Changing replicas just scales the current one.

Why did the broken v3 never take the site down in the simulator?
Show answer

With maxUnavailable: 0, an old Pod is removed only after a new one is ready. The v3 Pod never became ready, so all old Pods kept serving. In a real cluster, add a readiness probe (chapter 13) so that "ready" means the app actually answers, not just that the process started.

Deployments: common questions

What is the difference between a Deployment and a ReplicaSet?

A ReplicaSet keeps N Pods running. A Deployment manages ReplicaSets, creating a new one per version, which gives you rolling updates and rollbacks.