Symptoms
az aks upgradeoraz aks nodepool upgradefails after a long wait.provisioningStateisFailedor stuck onUpgrading.- Some nodes are on the new version, some on the old; some are cordoned (
SchedulingDisabled). - Errors mention
Too many requests, eviction,QuotaExceededorSubnetIsFull.
1. Read the actual error
az aks show -g rg-aks -n aks-prod --query "{state:provisioningState, version:kubernetesVersion}" -o table
az aks nodepool list -g rg-aks --cluster-name aks-prod \
--query "[].{pool:name, state:provisioningState, version:orchestratorVersion, count:count}" -o table
az monitor activity-log list -g rg-aks --offset 6h --status Failed \
--query "[].{op:operationName.localizedValue, msg:properties.statusMessage}" -o json | head -60
| Error contains | Cause | Fix |
|---|---|---|
Eviction, PodDrainFailure, Too many requests | A PDB allows zero disruptions | Step 2 |
QuotaExceeded | Not enough vCPU quota for surge nodes | Step 3 |
SubnetIsFull, InsufficientSubnetSize | No IPs for surge nodes | Step 3 |
VMExtensionProvisioningError | Node bootstrap failed (often egress blocked) | Step 4 |
2. Blocking PodDisruptionBudgets
kubectl get pdb -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,ALLOWED:.status.disruptionsAllowed,MIN:.spec.minAvailable,MAX:.spec.maxUnavailable
kubectl get nodes -o wide | grep -i SchedulingDisabled
Any PDB with ALLOWED = 0 blocks draining. Typical culprits: minAvailable: 1 on a one-replica deployment, or a deployment whose pods are all unhealthy.
Fix by scaling the workload up so a disruption is allowed, or temporarily relax the PDB:
kubectl scale deploy/payments -n prod --replicas=2
kubectl patch pdb payments-pdb -n prod --type merge -p '{"spec":{"minAvailable":null,"maxUnavailable":1}}'
Warning
Note the original PDB and put it back after the upgrade. Deleting PDBs to get an upgrade through is how you turn a maintenance into an outage.
3. Quota or subnet
Each surge node needs vCPU quota and, with Azure CNI (non-overlay), IPs for the node and its pods.
az vm list-usage -l westeurope --query "[?contains(name.value,'Family')].{name:name.localizedValue, used:currentValue, limit:limit}" -o table
az aks nodepool update -g rg-aks --cluster-name aks-prod -n userpool --max-surge 1
Lower max-surge uses fewer extra resources but upgrades slower. Request a quota increase or fix subnet space for a permanent fix.
4. Reconcile the cluster
Once the blocker is fixed, run an update with no changes. This makes AKS re-apply the desired state and usually clears Failed:
az aks update -g rg-aks -n aks-prod
az aks nodepool upgrade -g rg-aks --cluster-name aks-prod -n userpool --kubernetes-version <target>
Uncordon any node left cordoned by the failed attempt only if it is on a supported version and healthy:
kubectl uncordon aks-userpool-12345678-vmss000003
5. Verify
kubectl get nodes -o wide
kubectl get pods -A --field-selector status.phase!=Running
Check the application's own health endpoint, not just pod status.
Prevent it next time
- Gate upgrades with a pre-check: every PDB must have
disruptionsAllowed >= 1. - Run critical deployments with at least two replicas across zones.
- Keep spare quota and subnet space for surge nodes, or use Azure CNI Overlay.
- Upgrade a non-production cluster on the same version first, and use planned maintenance windows.