HPA 看起来配置很简单:CPU 超过多少就扩容,低于多少就缩容。但真正出问题时,经常不是 HPA 本身坏了,而是指标、requests、readiness、稳定窗口这些东西一起影响了结果。
我一般按下面的顺序查。
我遇到过最典型的误判是:业务说“CPU 都 80% 了为什么不扩容”,结果看的是节点 CPU 或容器 limit,而 HPA 算的是当前使用量相对于 requests 的 utilization。指标口径没对齐时,后面所有判断都会偏。
先看 HPA 当前状态
1
2
3
| kubectl get hpa -n default
kubectl describe hpa api-server -n default
|
kubectl get hpa 里重点看:
TARGETS 有没有显示 <unknown>。MINPODS、MAXPODS 是否符合预期。REPLICAS 是当前副本数还是已经被限制住了。
describe hpa 里重点看 Conditions 和 Events。比如指标拿不到、目标对象无法 scale、已经达到 maxReplicas、缩容被稳定窗口延迟。
确认 metrics-server 正常
CPU、内存这类 resource metrics 通常依赖 metrics.k8s.io,一般由 metrics-server 提供。
1
2
3
4
5
6
| kubectl get apiservice v1beta1.metrics.k8s.io
kubectl describe apiservice v1beta1.metrics.k8s.io
kubectl top node
kubectl top pod -n default
|
如果这里都拿不到数据,HPA 大概率也拿不到。不要一上来改 HPA YAML。
CPU utilization 依赖 requests
这是 HPA 排查里很常见的坑。
如果 HPA 用的是 CPU utilization:
1
2
3
4
5
6
7
| metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
|
这里的 60% 不是“宿主机 CPU 的 60%”,而是 Pod 当前 CPU 使用量相对于 container resources.requests.cpu 的比例。
换成公式大概就是:
1
2
| current utilization = current CPU usage / requests.cpu
desired replicas = ceil(current replicas * current utilization / target utilization)
|
真实实现还会考虑缺失指标、未 Ready Pod、容忍区间和稳定窗口,但这个公式能帮人先把口径对齐。
所以 Deployment 里需要有 requests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: nginx:1.27
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
|
更稳一点可以用 jsonpath 看 requests:
1
2
| kubectl get deploy api-server -n default \
-o jsonpath='{range .spec.template.spec.containers[*]}{.name}{" requests="}{.resources.requests}{"\n"}{end}'
|
一个完整的 HPA 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
- type: Pods
value: 4
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 30
periodSeconds: 60
selectPolicy: Max
|
maxReplicas 是硬上限。指标再高,也不会超过这个值。
多个 metrics 同时配置时,HPA 会分别计算期望副本数,然后取最大的那个。
scaleDown.stabilizationWindowSeconds: 300 表示缩容会参考过去一段时间的建议值,避免指标刚降下来就马上删 Pod。
为什么负载下来了还不缩容
常见原因:
- 还在 scale down stabilization window 内。
- 某个指标仍然建议较高副本数。
- metrics 获取失败,HPA 为了保守跳过缩容。
- minReplicas 限制住了。
- Deployment 还在 rollout,观察值不稳定。
Kubernetes 默认的 scale down 稳定窗口是 300 秒。不要期待请求量一掉下来副本马上减少。
这其实是好事。缩容太快会把刚恢复的系统再次打抖,尤其是有连接池、缓存预热、JVM 或大模型服务这种启动成本时。很多时候我们抱怨“不缩容”,其实是 HPA 在帮你避免频繁震荡。
为什么负载上来了也不扩容
我会按这个顺序查:
1
2
3
4
5
| kubectl top pod -n default -l app=api-server
kubectl describe hpa api-server -n default
kubectl get deploy api-server -n default -o jsonpath='{.status.readyReplicas}{" / "}{.status.replicas}{"\n"}'
|
常见原因是指标没拿到、CPU request 没配、还没超过容忍区间、Pod 还没 Ready,或者已经到 maxReplicas 了。
我会额外看一个细节:当前是否正在 rollout。新 Pod 还没稳定 Ready 时,HPA 的观测和 Deployment 的滚动发布会互相影响。这个时候不要急着同时改 HPA、改 requests、改镜像版本,否则最后很难判断到底是哪一个动作生效。
如果一个 Deployment 已经被 HPA 管理,我倾向于不要在日常 apply 的 manifest 里固定写死 spec.replicas。否则每次 apply 都可能把副本数改回 YAML 里的值,和 HPA 的调整互相打架。
排查 HPA 不要只盯着 HPA YAML。真正有用的信息通常分散在 metrics-server、Pod requests、HPA Conditions、Deployment rollout 状态和 readiness 里。
参考资料