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 metricsWrite 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-serverVerify 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=2Going 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" }