2026年11月13日

2026年11月13日

Kubernetes Cluster Upgrade: Pre-Checks, kubeadm, Nodes and EKS

This article is a localized edition of the Japanese original. Read the Japanese original

You upgrade a Kubernetes cluster one minor version at a time, in this order: control plane, add-ons, then nodes. The procedure itself is short; most upgrades that stall do so because of insufficient checks beforehand. The typical culprits are manifests that use removed APIs, Pods that keep a node from being drained, and an etcd backup someone forgot to take. This article covers what to check before upgrading, the procedure for clusters built with kubeadm, how to replace nodes one at a time, and the flow on managed services such as EKS.

Kubernetes ships roughly three minor versions a year, and each is supported for about 14 months (as of October 2026). Leave a cluster alone for about a year and it nears the end of support, so it is realistic to plan upgrades as recurring work.

What to Check Before Upgrading

1. Target version and order

You cannot skip minor versions when upgrading the control plane. To go from 1.31 to 1.33, you go through 1.32. Patch versions (for example 1.32.3 to 1.32.8) can be skipped.

Also check the rules on version differences between components (version skew).

  • kubelet must not be newer than kube-apiserver. It may be older within a certain range (up to three minor versions under the current policy).
  • kubectl must be within one minor version of kube-apiserver, older or newer.

These rules are why the control plane goes first and the nodes follow. The allowed skew has changed in the past, so check the "Version Skew Policy" page for the version you are upgrading to.

2. Are you using APIs that will be removed?

When the minor version goes up, deprecated API versions may be removed. Manifests and Helm charts written against a removed API version can no longer be applied with apply after the upgrade.

The efficient way to check is to look up which APIs the target version removes in the official "Deprecated API Migration Guide", then run a tool that inspects your manifests. Tools such as pluto and kubent (kube-no-trouble) search manifests, in-cluster resources and Helm releases for use of old APIs.

# Check the "requests to deprecated APIs" recorded by kube-apiserver
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis

Anything that shows up in this metric is proof that something in the cluster is actually calling an old API. The calls may come from outside your manifest repository, such as CI/CD, operators or monitoring tools.

3. Can the nodes be drained?

When you update a node, you move its Pods to other nodes (drain). The following kinds of Pods can stall this process or interrupt your service.

  • A Deployment with only one replica: the service stops while the Pod moves.
  • A PodDisruptionBudget (PDB) that is too strict: if minAvailable equals the replica count, not a single Pod can be evicted and the drain never progresses.
  • Pods that keep data on the node's local disk (emptyDir): the contents are lost when the Pod is evicted.

Run important apps with two or more replicas, and use a PDB to define how many may be down at the same time.

4. Back up etcd

Downgrading the control plane is basically not possible. When something goes wrong, your last resort is restoring from an etcd snapshot, so take one right before you start. On a cluster built with kubeadm, run the following on a control plane node (the certificate paths are the kubeadm defaults).

sudo ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  snapshot save /var/backups/etcd-$(date +%Y%m%d).db

Copy the snapshot to another server as well. Back up your application data (persistent volumes and databases) separately too: etcd only holds the definitions of your Kubernetes resources.

5. Try it in a test environment first

If you have a test cluster with the same configuration, upgrade it first and check that your applications work. Also check each add-on's release notes (CNI, Ingress controller, cert-manager and so on) to confirm it supports the new version.

Procedure for Clusters Built with kubeadm

This section assumes Ubuntu/Debian with packages installed from the pkgs.k8s.io repository. That repository is split by minor version, so you change the URL to the repository for the target version before updating. The legacy apt.kubernetes.io repository is no longer available, so do not run commands from old guides as they are.

Control plane (first node)

# After switching the repository version
sudo apt-get update
sudo apt-mark unhold kubeadm
sudo apt-get install -y kubeadm='1.33.x-*'   # x is an available patch number
sudo apt-mark hold kubeadm

kubeadm version
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.33.x

kubeadm upgrade plan shows which versions you can upgrade to from the current one and how each component will change. If it reports errors or warnings, resolve them before moving on to apply. Besides updating static Pods such as kube-apiserver, apply also replaces CoreDNS and kube-proxy with the new versions.

On the second and subsequent control plane nodes, run sudo kubeadm upgrade node instead of apply.

kubelet and kubectl on each node

Go through the nodes one at a time, control plane nodes included, using the following flow.

# From your workstation: evict Pods and mark the node unschedulable
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

# On the target node:
# (on worker nodes, update kubeadm here and run sudo kubeadm upgrade node)
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet='1.33.x-*' kubectl='1.33.x-*'
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet

# From your workstation: make the node schedulable again
kubectl uncordon <node-name>
kubectl get nodes

With --delete-emptydir-data, the contents of emptyDir volumes are deleted. Before adding it, check that no Pod holds data you cannot afford to lose. If a drain does not finish, look for the PDB blocking eviction with kubectl get pdb -A. Once a node is back to Ready and you have confirmed the apps are healthy, move on to the next node.

Replacing Nodes Instead

Instead of updating existing nodes, you can add nodes running the new version and then drain and delete the old ones in turn. This is the common approach for clusters in the cloud. You can switch the OS and container runtime to a new image at the same time, and if something fails you can delete the new nodes and go back to the old ones.

To be even more cautious, you can build an entirely separate cluster on the new version, move your applications over, and then switch with DNS or a load balancer (blue-green). It takes more work, but if the control plane has a problem you can go back to the original cluster.

On Managed Services

On EKS, GKE and AKS, updating the control plane takes a single command or a few clicks in the console. The approach to pre-checks and the order of upgrading the nodes afterward are the same, though. If you manage EKS with eksctl, the flow looks like this.

eksctl upgrade cluster --name wp-cluster --version 1.33 --approve
eksctl upgrade nodegroup --cluster wp-cluster --name ng-general --kubernetes-version 1.33

After the control plane, update add-ons such as vpc-cni and coredns to versions that support the new release. EKS also has a feature that warns you about things like deprecated API usage before an upgrade, which you can check in the console or the CLI. GKE may upgrade automatically depending on the release channel, and AKS is upgraded with az aks upgrade. Each service's features and default behavior can change, so check the official documentation every time you upgrade.

Checks After the Upgrade

  • With kubectl get nodes, are all nodes Ready and on the expected version?
  • With kubectl get pods -A, are there no Pods in CrashLoopBackOff or Pending?
  • Can you open your apps through the Ingress (for WordPress, the front page and the admin)?
  • Are the monitoring and log collection agents still sending data?

For Pods with problems, look for the cause in the Events of kubectl describe pod and in kubectl logs --previous.

Summary

  • Upgrade one minor version at a time, in the order control plane, add-ons, nodes. Never make kubelet newer than kube-apiserver.
  • Find APIs that will be removed in advance with the guide and tools, and use apiserver_requested_deprecated_apis to identify the actual callers.
  • Review PDBs and replica counts so a drain does not take your service down.
  • Back up etcd with a snapshot, along with your application data, right before you start. Downgrades are basically not possible.
  • With kubeadm, switch to the per-version pkgs.k8s.io repository before running kubeadm upgrade plan / apply.

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲