Kubernetes HPA 弹性伸缩调优:CPU 阈值失效的五个真实原因与自定义指标改造

举报
yd_292302676 发表于 2026/10/26 09:44:20 2026/10/26
【摘要】 HPA 配了 CPU 阈值却不扩容?按 requests 分母、limit 限流、metrics 窗口、behavior 策略、readiness 延迟五个原因逐项排查,并给出改用 QPS 自定义指标扩容的 HPA 配置。

一个"配了 HPA 却不扩容"的现场

上个月帮一个团队看 CCE 集群,现象很典型:接口 QPS 从 800 涨到 2600,HPA 的 CPU 阈值设的是 70%,副本数上限 30,结果整整四十分钟副本数停在 4 个没动,最后靠人工把副本拉到 24 才扛住。事后复盘,配置项一个都没写错,错的是几个默认值和几个"大家以为不重要"的字段。

这篇把排查过程中真正起作用的五个原因按出现频率排一遍,每个都给定位命令,最后给出用自定义指标替换 CPU 阈值的改法。

原因一:分母错了——requests 没设或设得离谱

HPA 的 CPU 利用率不是一个绝对数字,而是 实际用量 / requests。requests 没设,利用率算不出来,HPA 会直接放弃这个指标;requests 设成 100m 而实际稳态用量 600m,利用率常年 600%,一上来就顶到上限满扩容,然后抖动。

kubectl get hpa -n prod api-server -o yaml | grep -A3 "currentMetrics"
kubectl top pod -n prod -l app=api-server

把 currentMetrics 里的 averageUtilization 和 kubectl top 的绝对值对一下,如果比值和 requests 对不上,问题就在 requests。经验值:把 requests 设成 P95 稳态用量,不要设成"最低能跑起来的量"。

原因二:limit 卡住 CPU,利用率永远到不了阈值

这是最隐蔽的一个。requests 200m、limit 300m,Pod 一忙就被 CFS 限流(throttle),实际 CPU 用量被硬压在 300m,利用率最高只有 150%。如果阈值设 200%,看起来"很宽松",实际上 Pod 一直在被限流、响应时间飙升,而 HPA 认为"还没到阈值不用扩"。

kubectl get pod -n prod api-xxx -o jsonpath='{.spec.containers[0].resources}'
# 看 cgroup 限流次数
kubectl exec -n prod api-xxx -- cat /sys/fs/cgroup/cpu.stat | grep throttled

nr_throttled 持续增长就说明被限流了。这时候要么提高 limit,要么把 limit 去掉只留 requests(CPU 是可压缩资源,留 requests 保证调度,不设 limit 让突发能跑满),要么干脆换成按 QPS 扩,见最后一节。

原因三:metrics-server 的窗口比你想象的慢

HPA 默认读的是 metrics-server 的 15 秒采样、60 秒窗口平均值。刚启动的 Pod 在 --horizontal-pod-autoscaler-cpu-initialization-period(默认 5 分钟)内的指标会被忽略,--horizontal-pod-autoscaler-initial-readiness-delay 默认 30 秒。

也就是说:流量在 10 秒内翻倍,HPA 至少要 60~90 秒才反应过来,再加上新 Pod 拉镜像、启动、readiness 通过的时间,真实生效往往在 3 分钟以后。想更快只能缩短窗口,代价是指标抖动会直接传导成副本抖动。

kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/prod/pods | head -c 500

这条命令返回空或者报 the server could not find the requested resource,说明 metrics-server 没装好或证书有问题——HPA 的 kubectl describe 里会出现 failed to get cpu utilization: unable to get metrics。这个报错很多人扫一眼就跳过了,其实它是根因。

原因四:behavior 的默认值把扩容压住了

从 Kubernetes 1.18 起 behavior 是稳定字段,默认 scaleUp.stabilizationWindowSeconds=0、scaleDown.stabilizationWindowSeconds=300。但很多团队从老模板抄过来的 YAML 里带了一段 policies,把每 60 秒最多扩 1 个 Pod 写死了:

behavior:
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Pods
      value: 1          # 每 60s 只加 1 个
      periodSeconds: 60

流量翻三倍的时候,这个策略需要 20 分钟才能扩到目标副本。要么改成 Percent 类型一次扩 100%,要么直接删掉 policies 用默认(默认是 4 个 Pod 或 100% 取大者,60 秒周期)。

原因五:readinessProbe 让新 Pod "迟到"

HPA 扩出来的 Pod 要经过 initialDelaySeconds + 若干次 periodSeconds 才 Ready,未 Ready 的 Pod 不计入可用副本,Service 也不会把流量给它。如果 initialDelaySeconds: 120 是从单体应用时代抄过来的,扩容就等于延迟两分钟生效。

正确做法是把 initialDelaySeconds 降到 5~10 秒,用 startupProbe 单独兜住慢启动,readiness 只判断"能不能接流量"而不是"依赖全不全"。

改造:用 QPS 这类自定义指标替代 CPU

CPU 是滞后指标。请求已经在排队了 CPU 才涨,等 CPU 到阈值,用户已经在骂了。业务侧真正想表达的是"每个 Pod 每秒能扛多少请求",这个用 Prometheus Adapter 暴露成 custom metric 就能直接扩。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-server
  namespace: prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-server
  minReplicas: 4
  maxReplicas: 40
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "120"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 15
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30
    scaleDown:
      stabilizationWindowSeconds: 300

指标来源可以直接用华为云 AOM 的容器监控,也可以用自建 Prometheus 配 prometheus-adapter 把 rate(http_requests_total[1m]) 映射成 http_requests_per_second。注意 averageValue 是"每个 Pod 的平均值",HPA 会算出 总QPS / 目标值 得到期望副本数,所以这个数字必须来自压测:单 Pod 在 P99 可接受的前提下能扛多少。

一张排查顺序表

现象 先看什么 典型修法
HPA 显示 unknown kubectl describe hpa 的事件 修 metrics-server / 补 requests
利用率很高但不扩 是否已达 maxReplicas 调上限
扩了但没效果 readinessProbe 耗时 降 initialDelay、加 startupProbe
Pod 一直 throttle cpu.stat 的 nr_throttled 提 limit 或去掉 limit
扩缩容来回抖 behavior 窗口 加大 scaleDown 窗口

最后一句

HPA 本身不难,难的是它依赖的三个前提:指标可信、requests 合理、扩容能及时生效。这三件事任何一件不成立,阈值调多少都是玄学。与其反复试 60% 还是 75%,不如先把 kubectl describe hpa 的事件流和 nr_throttled 这两个数看清楚,再决定要不要上自定义指标。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。