Amazon EKS — Cluster Upgrades Hands-On Lab
In this lab, you will run through the pre-upgrade checks, upgrade the control plane, upgrade add-ons, and upgrade node groups on an EKS cluster.
Prerequisites
- Running EKS cluster (version N-1, e.g., 1.29)
- AWS CLI, kubectl, eksctl configured
- pluto installed (
brew install FairwindsOps/tap/pluto)
Step 1 — Pre-Upgrade Assessment
Check Current Version
CLUSTER_NAME="my-cluster"
CURRENT_VERSION=$(aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.version" --output text)
echo "Current version: $CURRENT_VERSION"
Check EKS Upgrade Insights
aws eks list-insights --cluster-name $CLUSTER_NAME --output table
Scan for Deprecated APIs
pluto detect-all-in-cluster
pluto detect-helm --output wide
Check Add-on Compatibility
TARGET_VERSION="1.30"
for addon in vpc-cni coredns kube-proxy aws-ebs-csi-driver; do
echo "=== $addon ==="
CURRENT=$(aws eks describe-addon --cluster-name $CLUSTER_NAME --addon-name $addon --query "addon.addonVersion" --output text 2>/dev/null)
echo "Current: $CURRENT"
LATEST=$(aws eks describe-addon-versions --addon-name $addon --kubernetes-version $TARGET_VERSION \
--query "addons[0].addonVersions[0].addonVersion" --output text)
echo "Latest for $TARGET_VERSION: $LATEST"
echo ""
done
Verify PDBs and Node Health
echo "=== PodDisruptionBudgets ==="
kubectl get pdb --all-namespaces
echo ""
echo "=== Node Status ==="
kubectl get nodes -o wide
echo ""
echo "=== Unhealthy Pods ==="
kubectl get pods --all-namespaces --field-selector status.phase!=Running,status.phase!=Succeeded
Step 2 — Upgrade Control Plane
echo "Upgrading control plane to $TARGET_VERSION..."
aws eks update-cluster-version \
--name $CLUSTER_NAME \
--kubernetes-version $TARGET_VERSION
# Get update ID
UPDATE_ID=$(aws eks list-updates --name $CLUSTER_NAME --query "updateIds[0]" --output text)
# Monitor progress
while true; do
STATUS=$(aws eks describe-update --name $CLUSTER_NAME --update-id $UPDATE_ID --query "update.status" --output text)
echo "Status: $STATUS ($(date))"
if [[ "$STATUS" == "Successful" ]] || [[ "$STATUS" == "Failed" ]]; then
break
fi
sleep 30
done
Verify:
aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.{Version:version,Status:status}"
Step 3 — Upgrade Add-ons
# kube-proxy first
KUBE_PROXY_VERSION=$(aws eks describe-addon-versions --addon-name kube-proxy --kubernetes-version $TARGET_VERSION \
--query "addons[0].addonVersions[0].addonVersion" --output text)
aws eks update-addon \
--cluster-name $CLUSTER_NAME \
--addon-name kube-proxy \
--addon-version $KUBE_PROXY_VERSION \
--resolve-conflicts OVERWRITE
echo "Upgrading kube-proxy to $KUBE_PROXY_VERSION"
# VPC CNI
VPC_CNI_VERSION=$(aws eks describe-addon-versions --addon-name vpc-cni --kubernetes-version $TARGET_VERSION \
--query "addons[0].addonVersions[0].addonVersion" --output text)
aws eks update-addon \
--cluster-name $CLUSTER_NAME \
--addon-name vpc-cni \
--addon-version $VPC_CNI_VERSION \
--resolve-conflicts PRESERVE
echo "Upgrading vpc-cni to $VPC_CNI_VERSION"
# CoreDNS
COREDNS_VERSION=$(aws eks describe-addon-versions --addon-name coredns --kubernetes-version $TARGET_VERSION \
--query "addons[0].addonVersions[0].addonVersion" --output text)
aws eks update-addon \
--cluster-name $CLUSTER_NAME \
--addon-name coredns \
--addon-version $COREDNS_VERSION \
--resolve-conflicts OVERWRITE
echo "Upgrading coredns to $COREDNS_VERSION"
Wait for all add-ons:
for addon in kube-proxy vpc-cni coredns; do
echo "Waiting for $addon..."
while true; do
STATUS=$(aws eks describe-addon --cluster-name $CLUSTER_NAME --addon-name $addon --query "addon.status" --output text)
echo "$addon: $STATUS"
if [[ "$STATUS" == "ACTIVE" ]]; then break; fi
sleep 10
done
done
Step 4 — Upgrade Node Groups
NODE_GROUPS=$(aws eks list-nodegroups --cluster-name $CLUSTER_NAME --query "nodegroups[]" --output text)
for ng in $NODE_GROUPS; do
echo "Upgrading node group: $ng"
aws eks update-nodegroup-version \
--cluster-name $CLUSTER_NAME \
--nodegroup-name $ng
done
Monitor:
for ng in $NODE_GROUPS; do
echo "=== $ng ==="
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name $ng \
--query "nodegroup.{Status:status,Version:version,ReleaseVersion:releaseVersion}"
done
Watch nodes:
watch kubectl get nodes -o custom-columns='NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion,STATUS:.status.conditions[-1].type,AGE:.metadata.creationTimestamp'
Step 5 — Post-Upgrade Validation
echo "=== Cluster Version ==="
kubectl version --short
echo ""
echo "=== Node Versions ==="
kubectl get nodes -o custom-columns='NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion'
echo ""
echo "=== System Pods ==="
kubectl get pods -n kube-system
echo ""
echo "=== DNS Test ==="
kubectl run dns-test --image=busybox:latest --restart=Never --rm -it -- nslookup kubernetes.default
echo ""
echo "=== All Pods Health ==="
kubectl get pods --all-namespaces --field-selector status.phase!=Running,status.phase!=Succeeded | head -20
Cleanup
No extra cleanup needed — you upgraded the existing cluster in place.