Command Palette

Search for a command to run...

Hectal
PHASE 4Intermediate ~14 min· topic 3 of 4

Topic 4.3

Horizontal & Vertical Pod Autoscaling

In one line

HPA automatically changes how MANY pods are running based on real load; VPA automatically adjusts how much CPU/memory EACH pod requests — two different axes of the same 'stop guessing, let it adapt' idea.

0/4 · 0%

Think of it like this

A restaurant responding to a dinner rush by calling in MORE staff (horizontal scaling — more identical workers) versus giving each existing staff member a bigger station and more tools to handle more volume individually (vertical scaling — bigger individual units). Kubernetes offers dedicated automation for both.

Key ideas

  1. 01

    The HORIZONTAL POD AUTOSCALER (HPA) automatically adjusts a Deployment's (or StatefulSet's) REPLICA COUNT based on observed metrics — most commonly average CPU or memory utilization across the pods, but also configurable against custom application-level metrics (like queue depth or request rate) with additional setup.

  2. 02

    An HPA is configured with a target metric and a target value (average CPU utilization: 70%) plus min/max replica bounds — it continuously compares actual observed usage against the target and scales the replica count up or down to try to keep actual usage near that target, entirely automatically, checking at a regular interval (commonly every 15 seconds).

  3. 03

    The VERTICAL POD AUTOSCALER (VPA) takes the opposite approach: instead of changing HOW MANY pods exist, it automatically adjusts the RESOURCE REQUESTS/LIMITS (Topic 4.1) of EACH pod based on its own observed historical usage — genuinely useful for right-sizing a workload's resource requests without manual guesswork, though applying a VPA recommendation typically requires recreating the pod (since resources can't be changed on a running pod in most Kubernetes versions).

  4. 04

    HPA and VPA are NOT typically combined on the exact same metric for the exact same workload (they can actively conflict — HPA trying to add more pods while VPA is simultaneously trying to make each existing pod bigger, in response to the same underlying signal) — a common real pattern is VPA in 'recommendation only' mode (surfacing suggested values for a human to review) alongside HPA actually doing the automatic live scaling.

  5. 05

    kubectl get hpa shows an HPA's current state directly — its target metric, current value, and current/min/max replica counts — genuinely useful for confirming an HPA is actually reacting correctly to real load, rather than sitting inactive because metrics-server isn't reporting data or the target metric was mis-specified.

In your stack

  • →

    A genuinely important, distinct gotcha for Node specifically: a single Node.js process is fundamentally single-threaded for its own JavaScript execution — a CPU-bound Node app under load will max out ONE core regardless of how many CPU cores the container's limit allows, which means HORIZONTAL scaling (more pod replicas, each still single-threaded) is usually far more effective than requesting a bigger CPU limit per pod, since a bigger limit alone doesn't let one Node process use more than one core for compute-bound work.

Code & diagrams

hpa.yamlmarkdown

Scale between 2 and 10 replicas, targeting 70% average CPU utilization.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
hpa.shmarkdown

Watch it scale in real time under generated load.

kubectl apply -f hpa.yaml

# See the HPA's live status: target vs current, and replica bounds
kubectl get hpa my-app-hpa
# NAME         REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS
# my-app-hpa   Deployment/my-app  12%/70%   2         10        2

# Generate real load to trigger scaling (from a throwaway pod)
kubectl run load-generator --rm -it --image=busybox -- \
  sh -c "while true; do wget -q -O- http://my-app-svc; done"

# Watch the HPA react live
kubectl get hpa my-app-hpa -w
# TARGETS climbs, REPLICAS increases automatically as load continues

# Stop the load generator (Ctrl+C, or delete the pod) and watch it scale back down
# — note: scale-DOWN is deliberately slower/more conservative than scale-up by default

Explain it without notes

01

Why might HPA and VPA actively conflict if both are configured to react to the exact same metric on the exact same workload?

02

An HPA's target is 70% CPU, current usage is showing 12%, and replicas have stayed at the minimum the whole time. Is this working correctly?

Practice

01

Create the example HPA on a Deployment with a readiness/liveness probe already configured, then generate artificial load and watch kubectl get hpa -w react in real time.

02

Stop the load and observe the HPA scale back down — note how much more conservative (slower) the scale-down behavior is compared to how quickly it scaled up.

Trade-offs

  • ↔

    Autoscaling removes a huge amount of manual capacity-planning guesswork, but it's only as good as the metric it's actually watching — CPU-based HPA works well for CPU-bound workloads, but a workload that's actually bottlenecked on something else entirely (a slow downstream database, a memory leak) won't be helped at all by adding more CPU-triggered replicas, which is exactly why choosing the RIGHT metric to scale on matters as much as configuring autoscaling at all.

Done when you can

  • I can explain the difference between HPA (more pods) and VPA (bigger pods).

  • I can create an HPA and observe it scale a Deployment's replica count under real load.

  • I understand why HPA and VPA aren't typically combined on the exact same metric.