Containers & Orchestration

Kubernetes HPA: Configuring and Verifying Autoscaling

For engineers comfortable with Deployments but new to autoscaling: from installing metrics-server, writing an HPA, to load-testing and actually watching replicas scale out.

By LaoHand Team·8 min read·Updated 2026-09-06

Prerequisite: Metrics-Server Before HPA Has Anything to Compute

HPA computes desired replicas from metrics, which come from metrics-server (CPU/memory) or custom/external sources. Without metrics-server, the HPA object exists but always shows <unknown> and never scales.

kind and minikube sometimes ship without it; install explicitly. Prove readiness by kubectl top nodes and kubectl top pods returning numbers.

metrics-server samples the kubelet summary API every ~15s; mind image mirroring and TLS flags in restrictive environments before tuning HPA.

# 安装 metrics-server(最新 release 见其 GitHub)
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 确认采集正常
kubectl top nodes
kubectl top pods

kubectl get apiservices | grep metrics

Write a First HPA: CPU-Based with min/max and Threshold

HPA uses averages: it sums Pod CPU, divides by their requests to get a utilization %, and scales out past the target (e.g. 60%) and back in when clearly below it for a sustained window.

Key point: the Deployments must set resources.requests or HPA has no denominator. The target may be an absolute value, but utilization % is the common choice.

Cold-start and jitter matter: scale-out can jump straight to max, while scale-down respects a stabilizationWindowSeconds (default 300s). Keep min at least 1-2 for availability, max within budget.

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

# Deployment 侧必须有 requests:
#   resources:
#     requests: { cpu: 250m, memory: 256Mi }

Mistake: HPA Stuck on <unknown>, Never Scaling

The classic symptom: kubectl get hpa shows <unknown>/-- under TARGETS and replicas never move. Causes in order: metrics-server missing or not authorized, Deployment lacking resources.requests, namespace resource quotas, or a misnamed custom metric.

Diagnose fast: kubectl describe hpa api-hpa shows conditions plus an "unable to retrieve metrics" message; then kubectl top pods to see whether CPU data even exists in the namespace.

Fix in order: make top return numbers (fix metrics-server), ensure requests exist, only then suspect the HPA yaml itself. Do not rewrite yaml endlessly while ignoring the data source.

# 症状
kubectl get hpa
# NAME      REFERENCE   TARGETS   MINPODS   MAXPODS   REPLICAS
# api-hpa   Deployment/api  <unknown>/60%  2  10  2

# 看根因
kubectl describe hpa api-hpa
# ... Unable to retrieve metrics for resource cpu ...

kubectl top pods -n <ns>
# 空或 error: metrics not available yet  -> 先修 metrics-server

Verify It Truly Scales: Load It to Blow Past the Threshold

Configuring without verifying is the same as not configuring. The most direct check: push temporary CPU load onto one Pod in the Deployment and watch HPA pull replicas from min upward in minutes.

Use busybox with a CPU-burning loop, or kubectl run a `while :; do :; done` under resource limits and watch. Just do not saturate the cluster so far that it can neither sprawl nor recover.

Clean up afterward: delete the loader Pod and drop the Deployment back to min (or clean the HPA env), so test load does not sit in production eating resources.

# 给目标 Deployment 派一个 CPU 燃烧 Pod
kubectl run loader \
  --image=busybox \
  --restart=Never \
  --requests='cpu=300m' \
  -- sh -c "while true; do :; done"

# 观察扩展(等待 stabilization 后)
kubectl get hpa api-hpa -w
kubectl get pods -w

# 收尾
kubectl delete pod loader
kubectl scale deployment api --replicas=2

Going Further: Multi and Custom Metrics (Memory, External Queue)

CPU alone is blind to I/O-bound or batch work; add a memory metric, either Utilization against the request or an absolute Value in MB.

Queue-backlog scaling (Kafka lag, Celery task count) is the canonical external-metric case and needs an external-metric adapter such as KEDA.

With several metrics HPA takes the maximum of the per-metric desired values, so a low-scoring metric never hides a high-pressure one.

spec:
  metrics:
  - type: Resource
    resource:
      name: cpu
      target: { type: Utilization, averageUtilization: 60 }
  - type: Resource
    resource:
      name: memory
      target: { type: Utilization, averageUtilization: 75 }
  # 或外部队列积压(经 KEDA / external adapter)
  # - type: External
  #   external:
  #     metric:
  #       name: kafka_lag
  #     target: { type: AverageValue, averageValue: "20" }

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.