Skip to main content
Course/Cluster Ops/Upgrading a Cluster with kubeadm
3/55 hours
Cluster Ops

Upgrading a Cluster with kubeadm

Execute a safe, one-minor-version-at-a-time cluster upgrade: control plane first, then workers β€” with zero application downtime.

advanced5 hoursRead β†’ Lab β†’ Quiz β†’ Practice
🧠 Brain Warm-Up
🧠 Brain Warm-Up: Kubernetes components tolerate limited version skew between each other. The kubelet on a node may be one minor version behind the API server, but not two. What would happen if you upgraded a 5-node cluster's control plane from 1.28 to 1.30 in one jump, skipping 1.29? Think about what breaks before reading.

Kubernetes Upgrade Strategy

Kubernetes follows a strict N-1 version skew policy between components. You must upgrade one minor version at a time:

Allowed:   v1.28 β†’ v1.29 β†’ v1.30
Forbidden: v1.28 β†’ v1.30   (skips v1.29)

Why One Minor Version at a Time?

The kube-apiserver and kubelet may differ by at most one minor version. If you skip a version:

  • The kubeadm upgrade apply logic may not have migration steps for the skipped version
  • API deprecations that were removed in the skipped version cause silent failures
  • etcd schema changes may be incompatible across two minor versions

Upgrade Sequence Overview

Control Plane Node:
  β‘  apt-mark unhold kubeadm && apt-get install kubeadm=1.30.0-00
  β‘‘ kubeadm upgrade plan              ← shows available upgrade targets
  β‘’ kubeadm upgrade apply v1.30.0    ← upgrades: apiserver, controller-manager,
  β”‚                                      scheduler, etcd manifests, and kubeadm addons
  β‘£ apt-get install kubelet=1.30.0-00 kubectl=1.30.0-00
  β‘€ systemctl daemon-reload && systemctl restart kubelet
  β‘₯ kubectl get nodes                 ← control plane node shows v1.30.0

Worker Nodes (repeat for each):
  β‘  kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
  β‘‘ On the worker: apt-get install kubelet=1.30.0-00 kubectl=1.30.0-00
  β‘’ systemctl daemon-reload && systemctl restart kubelet
  β‘£ kubectl uncordon <node>           ← scheduling resumes

What kubeadm upgrade apply Does

kubeadm upgrade apply updates the control plane static pod manifests and the cluster ConfigMaps (kubeadm-config, kubelet-config) but does not upgrade the kubelet binary. The kubelet must be upgraded separately on every node.

Node Drain Deep Dive

kubectl drain node-2 --ignore-daemonsets --delete-emptydir-data
    β”‚
    β”œβ”€ Cordon the node (SchedulingDisabled) β€” no new pods land here
    β”œβ”€ Evict all regular pods (graceful termination, PodDisruptionBudgets respected)
    β”œβ”€ --ignore-daemonsets: skip DaemonSet pods (they can't be moved anyway)
    └─ --delete-emptydir-data: force-evict pods using emptyDir (data is lost)

Result: node appears "Ready,SchedulingDisabled" in kubectl get nodes

Comparison: Control Plane vs Worker Upgrade

StepControl PlaneWorker Node
Update kubeadmYes (required first)No (kubeadm upgrade node, not apply)
Run upgrade commandkubeadm upgrade applykubeadm upgrade node
Drain before upgradeOptional (taint manually)Required
Upgrade kubeletYesYes
Uncordon afterN/A if not drainedYes