Scaling before it hurts
An autoscaler reacts to the past, and new pods take time to arrive. Watch a spike drop requests while help is still starting up, fix it without paying for the peak all day, then find out why a queue worker never scales at all.
A Kubernetes HorizontalPodAutoscaler (HPA) keeps changing how many copies of a pod run, to hold a metric near a target. Its arithmetic fits on one line, from the Kubernetes docs:
desiredReplicas = ceil( currentReplicas × currentMetricValue ÷ desiredMetricValue )
Four pods averaging 100% CPU against a 50% target: 4 × 100 ÷ 50 = 8 pods. Four pods at 25%: 2 pods. The rest of the docs is about the ways that simple line is slowed down on purpose:
- It runs every 15 seconds by default, not continuously.
- Inside a tolerance of 10% it does nothing, so it doesn’t twitch at noise.
- CPU utilization is measured against the pod’s CPU request, not its limit or the node.
- A pod that isn’t Ready yet is set aside. When the result would be a scale-up, the controller “conservatively assumes that the not-yet-ready pods are consuming 0% of the desired metric”. Slow-starting pods hold back the next scale-up as well as the first.
- Scaling down waits: it takes “the highest recommendation from within” a 5-minute window, so a brief dip doesn’t throw pods away that the next minute needs.
- The Metrics API reports each pod's CPU as a share of what it requested.
- Every 15 seconds the HPA reads it and works out the count: current pods times current value over target, rounded up.
- It writes that count into the Deployment, which creates pods. They serve nothing until they are Ready - and until then they count as 0% towards the next decision.
- Going down is deliberately slow: the highest recommendation of the last five minutes wins, so a brief lull does not undo the work.
The gap that drops requests
Add up the delays: up to 15 seconds before the HPA looks, then however long a new pod takes to pull its image, start, and pass its readiness probe. During that gap, the pods you already have take all of the spike. If the target is 90%, there’s almost no headroom to absorb it, and the overflow is dropped.
Three settings decide how big that gap is. Try them against a spike that quadruples traffic in two minutes:
Four pods are at 90% of their CPU request; the HPA target is 60%. How many pods will it ask for?
ceil(4 × 90 / 60) = ceil(6) = 6. The 5-minute window only applies to scaling down; scaling up happens at the next 15-second check.
Scaling on the wrong thing
CPU is a good signal only when CPU is the work. A worker that spends its time waiting on a slow API, a database or a disk barely uses any CPU, so an HPA on CPU sees an idle service while its queue grows by the minute. Raising the limits doesn’t help, because the metric never asks for more pods. Scale on what the work actually is: queue depth, requests in flight, or consumer lag. On Kubernetes that’s usually KEDA, which can “drive the scaling of any container in Kubernetes based on the number of events needing to be processed” and works alongside the HPA rather than replacing it.
A worker's queue is growing, its pods sit at 15% CPU, and the HPA (target 50% CPU) has not scaled. What is the fix?
CPU is not what limits this worker, so no CPU target describes its load. A 10% target would scale it - all the time, busy or not. Queue length is the work itself.
Everything above ran in your browser. Now do it on a real cluster with tilt lab 05-scale-it (profile observability). You need Docker, kind and Tilt, about 15 GB of free disk and 8 GB of spare RAM. Setup guide · Can my machine run it?
Lab 05 puts an HPA (min 1, max 4, 50% CPU, a 30-second scale-down window) on the hello app. First check it can see anything at all:
kubectl apply -f labs/05-scale-it/hpa.yaml
kubectl -n hello get hpa hello<unknown>/50% under TARGETS means no metrics, and the HPA will sit there forever without complaint. metrics-server comes with the observability profile. Then load it and watch:
./labs/05-scale-it/load.sh 90
kubectl -n hello get hpa,pods -wWhen you’re done, follow the lab’s clean-up. Deleting the HPA doesn’t undo its last decision: the Deployment keeps whatever count it was left at.
Sources
Missions and quizzes above earn their own XP. This marks the lesson done.