GKE ComputeClasses: Debugging
First Check: GKE Version
If fields are ignored or fail with "not supported," the control plane is likely too old.
- Verify CRD:
kubectl describe crd computeclasses.cloud.google.com - Check Versions:
gcloud container clusters describe <CLUSTER> --format="value(currentMasterVersion,currentNodeVersion)"
Symptom 1: ComputeClass Config Error
Check status.conditions on the ComputeClass object via kubectl describe ComputeClass <NAME>.
- Common Error:
location config with specific reservations enabled. - Fix: Remove
location.zonesfrom the reservation priority — zones come fromreservations.specific[].zonesinstead. Onlylocation.zonescollides; a policy-onlylocation.locationPolicy(e.g.BALANCED) may remain.
Symptom 2: Scale-Up Failure (Pods Pending)
Check Autoscaler Visibility logs (docs).
- Log Filter:
log_id("container.googleapis.com/cluster-autoscaler-visibility") - Asset:
assets/log-autoscaler-events.sh <cluster-name>(Live tail).
messageId |
Meaning | Fix |
|---|---|---|
scale.up.error.out.of.resources |
GCE stockout | Add zone/family |
| : : : fallbacks. : | ||
scale.up.error.quota.exceeded |
Project quota cap | Raise quota in |
| : : : target region. : | ||
scale.up.error.ip.space.exhausted |
Subnet full | Expand subnet |
| : : : ranges. : | ||
scale.up.no.scale.up |
No priority | Check Pod requests |
| : : matched : vs shapes. : |
Symptom 3: Trapped in Pending (GPU Tolerations Missing)
-
Symptom: Pod requesting a GPU ComputeClass is stuck in
PendingwithnoScaleUplogs. -
Cause: GKE auto-taints GPU nodes (
nvidia.com/gpu:NoSchedule). Scheduler refuses placement without a toleration. -
Fix: Add toleration to pod spec:
tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"
Symptom 4: Wrong Nodes Provisioned (E2 Fallback Trap)
- Symptom: Requested specific nodes (e.g.,
C3orN4), but GKE provisions defaultE2nodes. - Cause:
whenUnsatisfiable: ScaleUpAnywayprovisions generic E2 nodes to start the pod if preferred hardware fails. - Fix: Set
whenUnsatisfiable: DoNotScaleUpto strictly enforce hardware list.
Symptom 5: Active Migration Blocked
- Symptom: Spot capacity returned, but pods stuck on On-Demand nodes.
- Cause: Pod Disruption Budgets (PDBs) block eviction. Active migration strictly honors PDBs.
- Fix: Ensure PDBs allow at least 1 disruption.
maxUnavailable: 0blocks migration. - Common GKE blocker — system-managed pods: Non-DaemonSet pods in system
namespaces (
kube-system,gke-managed-*,gmp-system) often carry tight PDBs + low replicas, so the source node cannot drain (also blocks ordinary scale-down). Checkkubectl get pdb -Aand the autoscalernoScaleDownreason; raise replicas to add PDB headroom or isolate them onto a separate ComputeClass. - Note: PDBs /
safe-to-evictonly gate voluntary disruption; Spot preemption is involuntary and ignores both.
Symptom 6: ImageType Fragmentation Bug (Pre-1.33.5)
- Symptom: Autoscaler creates hundreds of tiny, fragmented node pools.
- Cause: Explicitly defining
imageType: UBUNTU_CONTAINERD(or COS) on versions older than 1.33.5-gke.1862000 (and 1.34.1-gke.2541000). - Fix: Upgrade cluster or temporarily remove
imageType.
Symptom 7: Pods Ignoring ComputeClass
- Fixes: Ensure pod has
nodeSelector: cloud.google.com/compute-class: <NAME>. Translate non-GKE node selectors — a generic/AWS-stylemachine-family: c4won't match; use GKE-nativecloud.google.com/machine-family: c4(family) ornode.kubernetes.io/instance-type(shape), or better, move the constraint into the ComputeClasspriorities[]. Verify manual pools have correct label/taint. Check if Pod requests exceed priority bounds.
Symptom 8: "ANY" Reservation Bypasses Fallbacks
- Cause:
reservations.affinity: AnyBestEffortfalls back to On-Demand at GCE layer. - Fix: Use
affinity: Specificwith named reservations.
Symptom 9: Disk/PV Attachment Fail
- Cause: Mixing Gen 4 VMs (Hyperdisk) and Gen 2 (PD) in the same priority list.
- Fix: Do not mix generations for workloads with attached PVs. Or (GKE
1.35.3-gke.1290000+): back the data PVs with the built-in
dynamic-rwoStorageClass (type: dynamic+use-allowed-disk-topology: "true") — the autoscaler becomes disk-topology-aware and scales up only compatible nodes, so a mixed-generationpriorities[]no longer attach-fails.
Symptom 10: Zonal PV Deadlock (Pending Pods)
- Symptom: StatefulSet pod is Pending because disk is in zone B but node is in zone A.
- Fix: Do not hardcode
locationin priorities. Use aStorageClasswithvolumeBindingMode: WaitForFirstConsumerso the disk provisions in the chosen node's zone — the built-indynamic-rwo(GKE 1.35.3-gke.1290000+) already sets this plususe-allowed-disk-topology: "true".
Symptom 11: List Loops / Backoff
- Cause: >10 priorities. Unobtainable shapes enter a 5-minute cooldown. Long lists expire upper-tier cooldowns before reaching the bottom, causing an infinite loop.
- Fix: Trim list; remove redundant rules.
Symptom 12: Pods on Low-Priority Nodes
- Symptom: Pods land on existing low-priority nodes (e.g., On-Demand) instead of triggering scale-up for available high-priority nodes (e.g., Spot).
- Cause: ComputeClass controls node provisioning, not pod scheduling. K8s schedules pods on any existing node with capacity before scaling up.
- Fix:
- ActiveMigration: Set
optimizeRulePriority: trueto eventually move workloads to higher-priority nodes. - PriorityClass: Use native K8s PriorityClass for pod-level preemption.
- Kueue: Use Kueue for complex batch/AI/ML fair-sharing and queueing.
- ActiveMigration: Set
Useful Commands
kubectl get nodes -L cloud.google.com/compute-class
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.nodeSelector["cloud.google.com/compute-class"]=="<name>") | .metadata.name'
gcloud logging read 'log_id("container.googleapis.com/cluster-autoscaler-visibility")' --freshness=1h --limit=50