DEV Community

Cover image for Rollout & Rollback Kubernetes Deployment with Hands-on Samples
Γ–mer Berat Sezer
Γ–mer Berat Sezer

Posted on

Rollout & Rollback Kubernetes Deployment with Hands-on Samples

What is Rollout & Rollback?

  • A rollout is the process of deploying or distributing a new version of an application.

  • A rollback is the act of returning to a prior, stable version of the application or deployment. In K8s, this is used to undo a rollout that caused issues, ensuring the application runs a known good version.

When is K8s rollback used?

For following situations, rollback may save to go back to the previous stable state:

  • Buggy Release: A new deployment introduces a critical bug causing application crashes.
  • Failed Health Checks: The new version fails Kubernetes liveness or readiness probes.
  • Increased Latency: Users report slow response times after deployment.
  • Database Migration Issues: The new release depends on a database schema change that was not applied.
  • Memory/CPU Spikes: The updated application consumes excessive resources, affecting performance.
  • Configuration Errors: A misconfigured environment variable in the new version causes failures.
  • Unexpected Dependency Conflicts: A library upgrade in the new release breaks compatibility.
  • Security Vulnerability: The deployed version introduces a critical security risk that needs immediate rollback.
  • Rollback for Canary Deployment: A percentage of users report issues during phased rollout, requiring a revert.
  • Auto Scaling Failures: The new version does not handle traffic spikes properly, leading to outages.

Two Hands-on samples for different update strategies:

  • Hands-on Sample#1: Rollout with Recreate Strategy
  • Hands-on Sample#2: Rollout with RollingUpdate Strategy

Hands-on Sample#1: Rollout with Recreate Strategy

This scenario shows how to roll out deployments with recreate strategy.

Steps

  • Run minikube (in this scenario, K8s runs on WSL2-Ubuntu 20.04) ("minikube start")
user@k8s:$ minikube start
πŸ˜„  minikube v1.35.0 on Ubuntu 20.04
✨  Automatically selected the docker driver
πŸ“Œ  Using Docker driver with root privileges
πŸ‘  Starting "minikube" primary control-plane node in "minikube" cluster
🚜  Pulling base image v0.0.46 ...
πŸ”₯  Creating docker container (CPUs=2, Memory=3100MB) ...
❗  Failing to connect to https://registry.k8s.io/ from inside the minikube container
πŸ’‘  To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/
🐳  Preparing Kubernetes v1.32.0 on Docker 27.4.1 ...
    β–ͺ Generating certificates and keys ...
    β–ͺ Booting up control plane ...
    β–ͺ Configuring RBAC rules ...
πŸ”—  Configuring bridge CNI (Container Networking Interface) ...
πŸ”Ž  Verifying Kubernetes components...
    β–ͺ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass

πŸ„  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Enter fullscreen mode Exit fullscreen mode

YAML File Explanation:

  • replicas: 5 => create 5 replicas
  • matchLabels: app: recreate => labelselector of deployment: selects pods which have "app:recreate" labels
  • strategy: type: Recreate => deployment roll up strategy: recreate => Delete all pods firstly and create Pods from scratch.
  • labels: app: recreate => labels the pod with "app:recreate"
  • recreate-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rcdeployment
  labels:
    team: development
spec:
  replicas: 5                        
  selector:
    matchLabels:                     
      app: recreate
  strategy:                        
    type: Recreate
  template:
    metadata:
      labels:                     
        app: recreate
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
Enter fullscreen mode Exit fullscreen mode
  • Run deployment:
kubectl apply -f recreate-deployment.yaml
deployment.apps/rcdeployment created
Enter fullscreen mode Exit fullscreen mode
  • Watching pods' status (kubectl get pods, or kubectl get pods -o wide)
user@k8s:$ kubectl get pods -o wide
NAME                            READY   STATUS              RESTARTS   AGE   IP       NODE       NOMINATED NODE   READINESS GATES
rcdeployment-687df9bcb7-gt96n   0/1     ContainerCreating   0          12s   <none>   minikube   <none>           <none>
rcdeployment-687df9bcb7-jn4xb   0/1     ContainerCreating   0          12s   <none>   minikube   <none>           <none>
rcdeployment-687df9bcb7-pmxfp   0/1     ContainerCreating   0          12s   <none>   minikube   <none>           <none>
rcdeployment-687df9bcb7-w9n88   0/1     ContainerCreating   0          12s   <none>   minikube   <none>           <none>
rcdeployment-687df9bcb7-z97qn   0/1     ContainerCreating   0          12s   <none>   minikube   <none>           <none>

user@k8s:$ kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
rcdeployment-687df9bcb7-gt96n   1/1     Running   0          76s   10.244.0.3   minikube   <none>           <none>
rcdeployment-687df9bcb7-jn4xb   1/1     Running   0          76s   10.244.0.6   minikube   <none>           <none>
rcdeployment-687df9bcb7-pmxfp   1/1     Running   0          76s   10.244.0.5   minikube   <none>           <none>
rcdeployment-687df9bcb7-w9n88   1/1     Running   0          76s   10.244.0.4   minikube   <none>           <none>
rcdeployment-687df9bcb7-z97qn   1/1     Running   0          76s   10.244.0.7   minikube   <none>           <none>
Enter fullscreen mode Exit fullscreen mode
  • Watching replica set's status:
user@k8s:$ kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
rcdeployment-687df9bcb7   5         5         0       23s

user@k8s:$ kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
rcdeployment-687df9bcb7   5         5         5       2m41s
Enter fullscreen mode Exit fullscreen mode
  • Update image version, after new replicaset and pods are created, old ones are deleted.
user@k8s:$ kubectl set image deployment rcdeployment nginx=httpd
deployment.apps/rcdeployment image updated
Enter fullscreen mode Exit fullscreen mode
  • With "recreate" strategy, pods are terminated:
user@k8s:$ kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
rcdeployment-588d5d854c   5         5         0       0s
rcdeployment-687df9bcb7   0         0         0       4m2s
Enter fullscreen mode Exit fullscreen mode
  • New pods are creating:
user@k8s:$ kubectl get pods -o wide
NAME                            READY   STATUS              RESTARTS   AGE   IP       NODE       NOMINATED NODE   READINESS GATES
rcdeployment-588d5d854c-42vjj   0/1     ContainerCreating   0          9s    <none>   minikube   <none>           <none>
rcdeployment-588d5d854c-8llhg   0/1     ContainerCreating   0          9s    <none>   minikube   <none>           <none>
rcdeployment-588d5d854c-dhpzz   0/1     ContainerCreating   0          9s    <none>   minikube   <none>           <none>
rcdeployment-588d5d854c-fgvgr   0/1     ContainerCreating   0          9s    <none>   minikube   <none>           <none>
rcdeployment-588d5d854c-j4qhv   0/1     ContainerCreating   0          9s    <none>   minikube   <none>           <none>
Enter fullscreen mode Exit fullscreen mode
  • New replicaset created:
user@k8s:$ kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
rcdeployment-588d5d854c   5         5         5       21s
rcdeployment-687df9bcb7   0         0         0       4m23s

user@k8s:$ kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
rcdeployment-588d5d854c-42vjj   1/1     Running   0          24s   10.244.0.11   minikube   <none>           <none>
rcdeployment-588d5d854c-8llhg   1/1     Running   0          24s   10.244.0.10   minikube   <none>           <none>
rcdeployment-588d5d854c-dhpzz   1/1     Running   0          24s   10.244.0.8    minikube   <none>           <none>
rcdeployment-588d5d854c-fgvgr   1/1     Running   0          24s   10.244.0.12   minikube   <none>           <none>
rcdeployment-588d5d854c-j4qhv   1/1     Running   0          24s   10.244.0.9    minikube   <none>           <none>
Enter fullscreen mode Exit fullscreen mode
  • Delete this deployment:
user@k8s:$ kubectl delete -f recreate-deployment.yaml
deployment.apps "rcdeployment" deleted

user@k8s:$ kubectl get pods -o wide
NAME                            READY   STATUS      RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
rcdeployment-588d5d854c-42vjj   0/1     Completed   0          4m53s   10.244.0.11   minikube   <none>           <none>
rcdeployment-588d5d854c-8llhg   0/1     Completed   0          4m53s   10.244.0.10   minikube   <none>           <none>
rcdeployment-588d5d854c-dhpzz   0/1     Completed   0          4m53s   10.244.0.8    minikube   <none>           <none>
rcdeployment-588d5d854c-fgvgr   0/1     Completed   0          4m53s   10.244.0.12   minikube   <none>           <none>

user@k8s:$ kubectl get pods -o wide
No resources found in default namespace.
Enter fullscreen mode Exit fullscreen mode

Hands-on Sample#2: Rollout with RollingUpdate Strategy

This scenario shows how to roll out deployments with rollingUpdate strategy.

YAML File Explanation:

  • matchLabels: app: rolling => labelselector of deployment: selects pods which have "app:rolling" labels
  • type: RollingUpdate => deployment roll up strategy: rollingUpdate, Pods are updated step by step, all pods are not deleted at the same time.
  • maxUnavailable: 2 => shows the max number of deleted containers. Total:10 container; if maxUnava:2, min:8 containers run in that time period
  • maxSurge: 2 => shows that the max number of containers. Total:10 container; if maxSurge:2, max:12 containers run in a time
  • labels: app: rolling => labels the pod with "app:rolling"
  • rolling-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rolldeployment
  labels:
    team: development
spec:
  replicas: 10                     
  selector:
    matchLabels:                     
      app: rolling
  strategy:
    type: RollingUpdate              
    rollingUpdate:                   
      maxUnavailable: 2              
      maxSurge: 2                    
  template:
    metadata:
      labels:                       
        app: rolling
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
Enter fullscreen mode Exit fullscreen mode
  • Run deployment:
user@k8s:$ kubectl apply -f rolling-deployment.yaml
deployment.apps/rolldeployment created
Enter fullscreen mode Exit fullscreen mode
  • Watching pods' status:
user@k8s:$ kubectl get pods -o wide
NAME                             READY   STATUS              RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
rolldeployment-6b947d555-4bx8h   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-58xjd   1/1     Running             0          11s   10.244.0.14   minikube   <none>           <none>
rolldeployment-6b947d555-99rxm   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-d2sff   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-f6jx4   1/1     Running             0          11s   10.244.0.13   minikube   <none>           <none>
rolldeployment-6b947d555-g2ldl   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-mkzlm   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-qpt7g   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-s8cct   0/1     ContainerCreating   0          11s   <none>        minikube   <none>           <none>
rolldeployment-6b947d555-zfl8l   1/1     Running             0          11s   10.244.0.15   minikube   <none>           <none>
Enter fullscreen mode Exit fullscreen mode
  • Watching replica set's status:
user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   10        10        10      82s
Enter fullscreen mode Exit fullscreen mode
  • Run edit command, it opens vim editor to edit:
user@k8s:$ kubectl edit deployment rolldeployment --record
Enter fullscreen mode Exit fullscreen mode
  • Find image definition, press i for insert mode, change to httpdinstead of nginx, press ESC, press :wq to save and exit (by default opening with vi text editor app)
  strategy:
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 2
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: rolling
    spec:
      containers:
      - image: nginx    # <= update with different image with httpd
        imagePullPolicy: Always
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP
Enter fullscreen mode Exit fullscreen mode
  • New pods are creating with new version:
user@k8s:$ kubectl get pods -o wide
NAME                             READY   STATUS              RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
rolldeployment-6b947d555-4bx8h   1/1     Running             0          4m42s   10.244.0.22   minikube   <none>           <none>
rolldeployment-6b947d555-58xjd   1/1     Running             0          4m42s   10.244.0.14   minikube   <none>           <none>
rolldeployment-6b947d555-99rxm   1/1     Running             0          4m42s   10.244.0.18   minikube   <none>           <none>
rolldeployment-6b947d555-d2sff   1/1     Running             0          4m42s   10.244.0.19   minikube   <none>           <none>
rolldeployment-6b947d555-f6jx4   1/1     Running             0          4m42s   10.244.0.13   minikube   <none>           <none>
rolldeployment-6b947d555-g2ldl   1/1     Running             0          4m42s   10.244.0.16   minikube   <none>           <none>
rolldeployment-6b947d555-qpt7g   1/1     Running             0          4m42s   10.244.0.20   minikube   <none>           <none>
rolldeployment-6b947d555-s8cct   0/1     Completed           0          4m42s   10.244.0.17   minikube   <none>           <none>
rolldeployment-9b659bdf9-f78w2   0/1     ContainerCreating   0          9s      <none>        minikube   <none>           <none>
rolldeployment-9b659bdf9-gtdv5   0/1     ContainerCreating   0          8s      <none>        minikube   <none>           <none>
rolldeployment-9b659bdf9-jbbss   0/1     ContainerCreating   0          8s      <none>        minikube   <none>           <none>
rolldeployment-9b659bdf9-kssgd   0/1     ContainerCreating   0          1s      <none>        minikube   <none>           <none>
rolldeployment-9b659bdf9-wrxpk   1/1     Running             0          9s      10.244.0.23   minikube   <none>           <none>
Enter fullscreen mode Exit fullscreen mode
  • New replicaset created:
user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       4m58s
rolldeployment-9b659bdf9   10        10        10      25s
Enter fullscreen mode Exit fullscreen mode
  • Run new deployment version:
user@k8s:$ kubectl set image deployment rolldeployment nginx=httpd:alpine --record=true
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/rolldeployment image updated
Enter fullscreen mode Exit fullscreen mode
  • New pods are creating with new version:
user@k8s:$ kubectl get pods
NAME                             READY   STATUS              RESTARTS   AGE
rolldeployment-7bd8467cd-8nrzd   1/1     Running             0          6s
rolldeployment-7bd8467cd-f6jrf   0/1     ContainerCreating   0          3s
rolldeployment-7bd8467cd-n7594   1/1     Running             0          8s
rolldeployment-7bd8467cd-p2n7b   1/1     Running             0          9s
rolldeployment-7bd8467cd-p9bnx   1/1     Running             0          18s
rolldeployment-7bd8467cd-q89x5   1/1     Running             0          5s
rolldeployment-7bd8467cd-qb5lb   1/1     Running             0          18s
rolldeployment-7bd8467cd-qx9gj   1/1     Running             0          18s
rolldeployment-7bd8467cd-skv5l   1/1     Running             0          18s
rolldeployment-7bd8467cd-smcvv   0/1     ContainerCreating   0          4s
rolldeployment-9b659bdf9-jbbss   1/1     Terminating         0          4m56s
rolldeployment-9b659bdf9-kssgd   1/1     Terminating         0          4m49s
Enter fullscreen mode Exit fullscreen mode
  • New replicaset created:
user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       9m39s
rolldeployment-7bd8467cd   10        10        10      27s
rolldeployment-9b659bdf9   0         0         0       5m6s
Enter fullscreen mode Exit fullscreen mode
  • To show history of the deployments (important: --record should be used to add old deployment versions in the history list):
user@k8s:$ kubectl rollout history deployment rolldeployment
deployment.apps/rolldeployment
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl edit deployment rolldeployment --record=true
3         kubectl set image deployment rolldeployment nginx=httpd:alpine --record=true
Enter fullscreen mode Exit fullscreen mode
  • Rollback to previous version (with undo: "kubectl rollout undo deployment/rolldeployment"):
user@k8s:$ kubectl rollout undo deployment/rolldeployment
deployment.apps/rolldeployment rolled back
Enter fullscreen mode Exit fullscreen mode
  • Pod status:
user@k8s:$ kubectl get pods
NAME                             READY   STATUS              RESTARTS   AGE
rolldeployment-7bd8467cd-8nrzd   1/1     Running             0          6m57s
rolldeployment-7bd8467cd-f6jrf   1/1     Running             0          6m54s
rolldeployment-7bd8467cd-n7594   1/1     Running             0          6m59s
rolldeployment-7bd8467cd-p2n7b   1/1     Running             0          7m
rolldeployment-7bd8467cd-p9bnx   1/1     Running             0          7m9s
rolldeployment-7bd8467cd-q89x5   1/1     Running             0          6m56s
rolldeployment-7bd8467cd-qx9gj   1/1     Running             0          7m9s
rolldeployment-7bd8467cd-smcvv   1/1     Running             0          6m55s
rolldeployment-9b659bdf9-5klq5   0/1     ContainerCreating   0          2s
rolldeployment-9b659bdf9-8rdgm   0/1     ContainerCreating   0          2s
rolldeployment-9b659bdf9-drb54   0/1     ContainerCreating   0          2s
rolldeployment-9b659bdf9-gv8m8   0/1     ContainerCreating   0          2s
Enter fullscreen mode Exit fullscreen mode
  • It is also to pause rollout:
user@k8s:$ kubectl rollout undo deployment/rolldeployment
deployment.apps/rolldeployment rolled back

user@k8s:$ kubectl rollout pause deployment/rolldeployment
deployment.apps/rolldeployment paused

user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       19m
rolldeployment-7bd8467cd   6         6         2       10m
rolldeployment-9b659bdf9   6         6         6       14m
Enter fullscreen mode Exit fullscreen mode
  • There are 12 pods instead of 10 pods, because of the rolling update:
user@k8s:$ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
rolldeployment-7bd8467cd-78sjw   1/1     Running   0          18s
rolldeployment-7bd8467cd-cvjx6   1/1     Running   0          18s
rolldeployment-7bd8467cd-cvxqt   1/1     Running   0          18s
rolldeployment-7bd8467cd-krwpf   1/1     Running   0          18s
rolldeployment-7bd8467cd-ks8z4   1/1     Running   0          14s
rolldeployment-7bd8467cd-xc52z   1/1     Running   0          13s
rolldeployment-9b659bdf9-4tt79   1/1     Running   0          2m57s
rolldeployment-9b659bdf9-8rdgm   1/1     Running   0          3m3s
rolldeployment-9b659bdf9-drb54   1/1     Running   0          3m3s
rolldeployment-9b659bdf9-h6jzq   1/1     Running   0          2m54s
rolldeployment-9b659bdf9-jtch2   1/1     Running   0          2m55s
rolldeployment-9b659bdf9-tnthr   1/1     Running   0          2m56s
Enter fullscreen mode Exit fullscreen mode
  • While rollback undo, it was paused:
user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       19m
rolldeployment-7bd8467cd   6         6         6       10m
rolldeployment-9b659bdf9   6         6         6       14m
Enter fullscreen mode Exit fullscreen mode
  • Resume the pause of rollout of deployment:
user@k8s:$ kubectl rollout resume deployment/rolldeployment
deployment.apps/rolldeployment resumed

user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       23m
rolldeployment-7bd8467cd   10        10        6       14m
rolldeployment-9b659bdf9   2         2         2       19m

user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       23m
rolldeployment-7bd8467cd   10        10        8       14m
rolldeployment-9b659bdf9   0         0         0       19m

user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       23m
rolldeployment-7bd8467cd   10        10        9       14m
rolldeployment-9b659bdf9   0         0         0       19m

user@k8s:$ kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
rolldeployment-6b947d555   0         0         0       23m
rolldeployment-7bd8467cd   10        10        10      14m
rolldeployment-9b659bdf9   0         0         0       19m
Enter fullscreen mode Exit fullscreen mode
  • Delete deployment:
user@k8s:$ kubectl delete -f rolling-deployment.yaml
deployment.apps "rolldeployment" deleted

user@k8s:$ kubectl get rs
No resources found in default namespace.

user@k8s:$ kubectl get pods
No resources found in default namespace.
Enter fullscreen mode Exit fullscreen mode
  • Delete minikube:
minikube delete
πŸ”₯  Deleting "minikube" in docker ...
πŸ”₯  Deleting container "minikube" ...
πŸ”₯  Removing /home/omer/.minikube/machines/minikube ...
πŸ’€  Removed all traces of the "minikube" cluster.
Enter fullscreen mode Exit fullscreen mode

Conclusion

This post showed the mechanism of the rollout, rollback between versions. K8s rollout ensures smooth deployment of new application versions with minimal downtime using rolling updates.

If an issue arises, rollback allows quick restoration to a stable previous version, reducing service disruption. These features enhance application availability, reliability, and DevOps efficiency.

If you found the tutorial interesting, I’d love to hear your thoughts in the blog post comments. Feel free to share your reactions or leave a comment. I truly value your input and engagement πŸ˜‰

For other posts πŸ‘‰ https://dev.to/omerberatsezer 🧐

Follow for Tips, Tutorials, Hands-On Labs for AWS, Kubernetes, Docker, Linux, DevOps, Ansible, Machine Learning, Generative AI.

Top comments (0)