Skip to main content

Introduction

Kubernetes components can each run their own versions. For example:
ComponentExample version
kube-apiserverv1.32.0
kube-schedulerv1.32.0
kubeletv1.32.0
kube-proxyv1.32.0
controller-managerv1.32.0
etcdv3.5.17
corednsv1.12.0
It is not mandatory for all components to share the same version. However, kube-apiserver is the primary component — all other components like kube-scheduler, kubelet, kube-proxy, and controller-manager must be at a version less than or equal to the kube-apiserver version to avoid compatibility issues. The etcd and coredns versions are independent.
If you deployed your cluster with kubeadm, you can use it to plan and execute the upgrade:
kubeadm upgrade plan          # shows available upgrade paths and component versions
kubeadm upgrade apply <version>

Upgrade process

This example upgrades a cluster with one master and two worker nodes from v1.21.0 to v1.22.0. Replace version numbers with your actual target version.
1

Upgrade the master node

When the master node is being upgraded, control plane components (kube-apiserver, kube-scheduler, kube-controller-manager) go down temporarily. Worker nodes continue to run their existing workloads, but you cannot deploy, modify, or delete resources while the control plane is unavailable.
# Replace x with your target minor version, e.g. 1.31, 1.32
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.x/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.x/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo apt-get update

sudo apt update
sudo apt-cache madison kubeadm   # copy the version you want to upgrade to

# Replace x in 1.31.x-* with the latest patch version
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.31.x-*' && \
sudo apt-mark hold kubeadm

kubeadm version
sudo kubeadm upgrade plan

# Replace x with the patch version you picked
sudo kubeadm upgrade apply v1.31.x

# kubectl get nodes still shows the old version at this point
# because it reports the kubelet version, not the kube-apiserver version
# Drain the node before upgrading kubelet
kubectl drain <node-to-drain> --ignore-daemonsets

# Replace x in 1.31.x-* with the latest patch version
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.31.x-*' kubectl='1.31.x-*' && \
sudo apt-mark hold kubelet kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

kubectl uncordon <node-to-uncordon>
2

Upgrade the worker nodes

There are three strategies for upgrading worker nodes:

All at once

Upgrade all worker nodes simultaneously. Simple, but causes downtime as all pods are evicted at once.

One at a time

Upgrade nodes sequentially. Pods are rescheduled to remaining nodes. No downtime, but takes longer.

Add new nodes

Provision new nodes with the new version, move workloads to them, then remove the old nodes. Best for cloud providers. No downtime.
The following commands upgrade one worker node at a time. Repeat for each worker node.
# Replace x with your target minor version, e.g. 1.29, 1.31, 1.32
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.x/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.x/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo apt-get update

# Replace x in 1.31.x-* with the latest patch version
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.31.x-*' && \
sudo apt-mark hold kubeadm

# Run upgrade node on the worker node itself
sudo kubeadm upgrade node

# Run this on the control plane node to drain the worker
kubectl drain <node-to-drain> --ignore-daemonsets

# Replace x in 1.31.x-* with the latest patch version
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.31.x-*' kubectl='1.31.x-*' && \
sudo apt-mark hold kubelet kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

# Run this on the control plane node to uncordon the worker
kubectl uncordon <node-to-uncordon>

Build docs developers (and LLMs) love