GCP ACE: Scaling in GKE — HPA, Cluster Autoscaler, and Node Auto-Provisioning
“Autoscaling” in GKE isn’t one mechanism — it’s three, operating at different levels, and the exam expects you to know which one does what.
Horizontal Pod Autoscaler (HPA) — scales Pod count
The HPA is a control loop that adjusts a workload’s replicas to match observed demand. It scales horizontally — adding or removing Pods — not vertically (changing a Pod’s own CPU/memory, which is a separate mechanism, VPA).
For CPU and memory metrics, the HPA needs the Metrics Server installed, and utilization is measured as a percentage of the container’s CPU request — meaning the target workload must set resources.requests.cpu, or the HPA has nothing to compute a percentage against.
Imperative creation:
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
Declarative:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
kubectl get hpa php-apache
kubectl describe hpa php-apache # shows current metrics and scaling events — the first stop when it's not scaling as expected
The scaling formula:
desiredReplicas = ceil[ currentReplicas × ( currentMetricValue / desiredMetricValue ) ]
Example: 4 replicas, current average CPU utilization 200%, target 50% → ceil[4 × (200/50)] = 16 replicas.
Two stabilizers worth remembering for the exam:
- Tolerance: if the ratio is within ~10% of 1.0, no scaling happens — avoids flapping on noise.
- Scale-down stabilization window: 300 seconds by default — the HPA won’t act on a single low reading; it looks at the highest recommendation across that whole window before scaling down.
If multiple metrics are defined, the HPA computes a desired count for each and uses the largest.
Cluster Autoscaler — scales node count within a pool
Different layer entirely: this resizes a node pool, adding or removing the actual VMs, between a minimum and maximum you set.
- Scale-up trigger: Pods that can’t be scheduled due to insufficient resources. It decides based on resource requests, not actual utilization — so a Pod with high requests and low real usage can still trigger a scale-up.
- Scale-down trigger: nodes that are underutilized enough that their Pods could fit on fewer nodes — down to the pool’s configured minimum.
- Node creation: happens in parallel, not one at a time, up to the pool’s or cluster’s limits.
- Drain timeout on scale-down: one hour on GKE 1.32.7-gke.1079000+ (10 minutes on older versions) — not configurable. If a node can’t drain gracefully within that window, it’s forcibly terminated.
- Pod eviction priority tiers: Pods with
PriorityClassbelow 1000000000 get up to the full drain timeout to evict; Pods at or above 1000000000 get only 10 minutes, and only after the lower tier finishes. - Backoff on failed node creation: starts at 5 minutes, grows exponentially, caps at 30 minutes.
gcloud container clusters create example-cluster \
--num-nodes=2 \
--location=us-central1-a \
--node-locations=us-central1-a,us-central1-b,us-central1-f \
--enable-autoscaling --min-nodes=1 --max-nodes=4
Node auto-provisioning (NAP) — scales node pools themselves
One level up again: instead of resizing an existing pool, NAP creates and deletes entire node pools to match pending workload shapes (for example, a workload needing a GPU or TPU gets a new pool provisioned for it automatically, then removed when no longer needed). On Autopilot clusters, this is how node pools work by default — you never create one yourself.
A naming note: current GKE docs call the capability node pool auto-creation. Cluster-level node auto-provisioning (--enable-autoprovisioning, with cluster-wide CPU and memory limits) is one way to turn it on in Standard clusters; ComputeClasses are the other. Exam questions still use “node auto-provisioning”, so recognize both names.
| HPA | Cluster Autoscaler | Node auto-provisioning | |
|---|---|---|---|
| Scales | Pod replica count | Nodes within an existing pool | Node pools themselves |
| Trigger | Metric vs. target (e.g. CPU %) | Unschedulable Pods / underutilized nodes | Pending workloads needing a pool shape that doesn’t exist |
| Bounds | minReplicas / maxReplicas | Pool’s min-nodes / max-nodes | Cluster-wide resource limits you configure |
Exam Takeaways
- HPA scales Pod count based on a metric (commonly CPU, as a percentage of the request, not the limit) — it needs Metrics Server and a CPU request set on the target.
- HPA formula:
ceil[currentReplicas × (currentMetric / desiredMetric)], clamped tomin/maxReplicas, with ~10% tolerance and a 300-second scale-down stabilization window by default. - Cluster Autoscaler resizes an existing node pool based on requests, not real utilization, between
--min-nodesand--max-nodes. - Cluster Autoscaler’s node drain timeout on scale-down is 1 hour (GKE 1.32.7-gke.1079000+) or 10 minutes on older versions — not configurable.
- Node auto-provisioning creates/deletes entire node pools to fit pending workload shapes; it’s how Autopilot manages nodes by default.
- If a question is about Pod count reacting to load → HPA. If it’s about not enough node capacity for Pods → Cluster Autoscaler. If it’s about a workload needing a node shape (GPU/TPU) that doesn’t exist yet → node auto-provisioning.