K8s Deployment Strategies — Recreate and RollingUpdate

 

When we update an app in Kubernetes — new image, config change, replica count Kubernetes needs to replace the old pods with new ones. The deployment strategy controls how that happens. Get it wrong and you either have downtime you didn’t expect, or two versions of your app running at the same time when they shouldn’t be.

https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-intro/

https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

Two strategies: Recreate and RollingUpdate.

Node subnets in this cluster:

10.42.0.x  →  k3s-cp-01
10.42.1.x  →  k3s-worker-02
10.42.2.x  →  k3s-worker-01

Step 1 — Create the Namespace

Every app gets its own namespace. It keeps things clean and resources are isolated, you can set permissions per namespace, and kubectl get pods only shows what’s relevant.

We create a dedicated namespace for this app rather than dumping everything into default.

kubectl create namespace newapp

Set it as default context so we don’t have to type -n newapp on every command:

k config set-context default --namespace=newapp

Confirm:

k config get-contexts

Expected output:

CURRENT   NAME      CLUSTER   AUTHINFO   NAMESPACE
*         default   default   default    newapp

Step 2 — Create the Deployment

Rather than writing the manifest from scratch, we use --dry-run=client to generate a valid base YAML and redirect it to a file. 

kubectl create deployment my-newapp --image=httpd --replicas=10 --dry-run=client -o yaml > deploy.yaml

Edit deploy.yaml and add the ports:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-newapp
  name: my-newapp
  namespace: newapp
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-newapp
  template:
    metadata:
      labels:
        app: my-newapp
    spec:
      containers:
        - name: httpd
          image: httpd
          ports:
            - containerPort: 80

Apply it:

k apply -f deploy.yaml

Check pods — -o wide shows the node each pod landed on:

k get pods -o wide

Expected output:

NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE
my-newapp-f6544cd7d-96r7n   1/1     Running   0          35s   10.42.0.155   k3s-cp-01
my-newapp-f6544cd7d-cdf94   1/1     Running   0          35s   10.42.0.154   k3s-cp-01
my-newapp-f6544cd7d-czq2v   1/1     Running   0          35s   10.42.1.230   k3s-worker-02
my-newapp-f6544cd7d-jmhrj   1/1     Running   0          35s   10.42.2.152   k3s-worker-01
my-newapp-f6544cd7d-pzqfz   1/1     Running   0          35s   10.42.2.153   k3s-worker-01
my-newapp-f6544cd7d-rdm4f   1/1     Running   0          35s   10.42.2.154   k3s-worker-01
my-newapp-f6544cd7d-rpx2c   1/1     Running   0          35s   10.42.0.157   k3s-cp-01
my-newapp-f6544cd7d-tx2ql   1/1     Running   0          35s   10.42.0.156   k3s-cp-01
my-newapp-f6544cd7d-xhcct   1/1     Running   0          36s   10.42.1.229   k3s-worker-02
my-newapp-f6544cd7d-zbdmv   1/1     Running   0          35s   10.42.1.231   k3s-worker-02

10 pods spread automatically across all 3 nodes and the scheduler figured it out based on available resources on each node. 

Check deployment:

k get deployments.apps

Expected output:

NAME        READY   UP-TO-DATE   AVAILABLE   AGE
my-newapp   10/10   10           10          53s

Step 3 — Exec Into a Pod

We exec into a pod to verify networking to see if they can talk to each other. This is important to understand because Kubernetes pod networking means every pod gets its own IP and can reach any other pod in the cluster regardless of which node it’s on.

k exec -ti my-newapp-f6544cd7d-96r7n -- /bin/bash

The base httpd image doesn’t include ping so install it:

apt update && apt install -y iputils-ping

Ping a pod on a different node and pick an IP from the output above that’s on a different node: by default all IP’s will communicate across pods.

ping 10.42.0.157

Step 4 — Strategy: Recreate ( Parallel Remediation )

Recreate is the simpler of the two strategies. When you apply an update, Kubernetes kills every running pod immediately, waits for them all to be gone, then starts the new ones. The app is completely down during that gap.

strategy sits directly under spec  same indent level as replicas, selector, and template:

spec:
  replicas: 10
  selector:
    ...
  strategy:        # 2 spaces — directly under spec
    type: Recreate
  template:
    ...

Create deploy.recreate.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-newapp
  name: my-newapp
  namespace: newapp
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-newapp
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: my-newapp
    spec:
      containers:
        - name: httpd
          image: httpd:2.4-trixie
          ports:
            - containerPort: 80

Apply it:

k apply -f deploy.recreate.yaml

Open a second terminal and watch pods to see the Recreate behaviour in action:

k get pods -w

You will see all 10 old pods terminate simultaneously, then 10 new ones start. There is no overlap.

Check pods after:

k get pods -o wide

Expected output:


Step 5 — Strategy: RollingUpdate

RollingUpdate is the default strategy in Kubernetes and the right choice for production workloads. Instead of killing everything at once, it replaces pods gradually  so our app stays available throughout the update.

Two settings control the pace:

  • maxUnavailable — how many pods can be down at once during the update. Default 25%.
  • maxSurge — how many extra pods can run above your desired count during the update. Default 25%.

Create deploy.rollingupdate.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-newapp
  name: my-newapp
  namespace: newapp
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-newapp
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: my-newapp
    spec:
      containers:
        - name: httpd
          image: httpd:2.4-trixie
          ports:
            - containerPort: 80

Apply it:

k apply -f deploy.rollingupdate.yaml

Watch the rollout:

k rollout status deployment/my-newapp

Expected output:

 

With maxUnavailable: 1 and maxSurge: 1 on 10 replicas — at most 9 pods down, at most 11 pods running at any point. One old pod terminates, one new pod starts, repeat until all 10 are updated.

Check rollout history — every apply that changes the pod spec creates a new revision:

k rollout history deployment/my-newapp

Expected output:

REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>

Roll back to the previous revision if something goes wrong:

k rollout undo deployment/my-newapp

Commit to Git

git add .
git commit -m "tested recreate and rolling update"
git push

Summary

 RecreateRollingUpdate
DowntimeYesNo
Old + new pods togetherNoYes briefly
Extra configNonemaxUnavailable, maxSurge
Use casedev/testProduction, stateless
DefaultNoYes

 

(Visited 42 times, 1 visits today)

By Ash Thomas

Ash Thomas is a seasoned IT professional with extensive experience as a technical expert, complemented by a keen interest in blockchain technology.