华为云CCE-HPA弹性伸缩失灵排查

举报
yd_221506059 发表于 2026/10/29 23:08:46 2026/10/29
【摘要】 大促开始后 P99 从 200ms 涨到 4s,Pod 数却纹丝不动,HPA 显示 TARGETS 为 unknown。本文指出 HPA 的分母是 requests.cpu,没声明就等于没开弹性伸缩;即使指标正常,从触发到接住流量还要经过调度、拉镜像、JVM 预热,合计 3 到 5 分钟,而洪峰只有 90 秒。文中给出补 requests、节点池留缓冲、加 startupProbe 三项改动。

华为云 CCE 弹性伸缩失灵:HPA 配置全对,流量涨了 5 倍却一个 Pod 没加

CPU 打满、Pod 数纹丝不动,HPA 的 kubectl describe 里写着 unable to get metrics。就算指标正常,从触发到真正接住流量,中间还有调度、拉镜像、JVM 预热三段路要走,加起来往往超过 3 分钟——而流量洪峰可能只有 90 秒。这篇文章把弹性伸缩拆成"决策"和"落地"两段,分别解释为什么会失灵。

一、现象:指标到了,扩容没动

一次典型的失效现场:

观察项 现象
业务表现 大促开始后接口 P99 从 200ms 涨到 4s
Pod 数量 始终是 4 个,没有新增
节点 CPU 全部 95%+
HPA 状态 TARGETS: <unknown>/70%
HPA 事件 failed to get cpu utilization: missing request for cpu

三个信息拼起来,结论很清楚:HPA 根本没拿到数据,它连"该不该扩容"都无法判断。

二、第一段:决策失灵——HPA 算不出利用率

HPA 计算 CPU 利用率的公式是:

利用率 = 实际用量 / request 值

注意分母是 requests.cpu,不是 limits.cpu,也不是节点容量。

如果容器没有声明 requests.cpu,这个除法就无解,HPA 直接报 missing request for cpu,然后什么都不做。

最常见的三种决策失灵

原因 表现 修复
未设置 requests.cpu TARGETS: <unknown> 补上 requests
metrics-server 不可用 全部指标 unknown 检查 metrics-server Pod
指标采集延迟 扩容滞后 30~60 秒 接受,或用自定义指标

正确的最小配置

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-api
  minReplicas: 4
  maxReplicas: 40
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0      # 扩容要快,不设稳定窗口
      policies:
        - type: Percent
          value: 100                     # 一次最多翻倍
          periodSeconds: 30
    scaleDown:
      stabilizationWindowSeconds: 300    # 缩容要慢,观察 5 分钟
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60
# Deployment 里必须有的部分
resources:
  requests:
    cpu: 500m
    memory: 1Gi
  limits:
    cpu: "2"
    memory: 2Gi

这里有个容易搞反的地方:requests.cpu 设得越小,同样的实际用量算出来的利用率越高,越容易触发扩容。 但如果 requests 设得过小,节点会被过度超卖,扩容后 Pod 反而抢不到 CPU。

三、第二段:落地失灵——扩容了,但来不及

这是更隐蔽的一层。即使 HPA 正确决策,Pod 从"创建"到"真正能接流量"中间还有很长的链路:

阶段 典型耗时 说明
指标采集 + 判定 30~60 s metrics-server 默认 15s 采集一次,HPA 默认 15s 同步
调度 + 节点扩容 60~180 s 如果没有空闲节点,要先扩节点
拉取镜像 10~120 s 镜像越大越慢,冷节点无缓存
容器启动 5~30 s 依赖初始化
应用预热 30~180 s JVM 启动、连接池建立、缓存加载
就绪探针通过 由探针配置决定 探针间隔越粗,越晚接流量

加总:乐观情况 2 分钟,常见情况 3~5 分钟。

而流量洪峰的特征往往是:90 秒内从 1 倍涨到 5 倍,然后维持 10 分钟。 等你 3 分钟后扩容完成,业务已经超时了一大片。

三个针对性解法

① 节点池预留缓冲容量(最有效)

弹性伸缩要分两层看:Pod 层(HPA)和节点层(节点池)。如果集群没有空闲资源,HPA 扩出来的 Pod 会一直 Pending。

节点池:开启自动伸缩,但保留 1~2 个节点的缓冲

代价是多花一点钱,但换来的是"扩容不用等节点"。这是所有大促保障里性价比最高的一项。

② 应用预热,缩短"能接流量"的时间

JVM 应用尤其明显。三个手段:

  • 就绪探针给足 initialDelaySeconds,避免流量打进还没预热好的实例
  • startupProbe 单独配置,把"启动慢"和"运行慢"分开判断,避免启动期被误判为失败而重启
  • 预热接口:容器启动后主动调用一次热点数据加载,不要等第一个真实请求
startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
  periodSeconds: 2          # 最长容忍 60 秒启动
readinessProbe:
  httpGet: { path: /ready, port: 8080 }
  initialDelaySeconds: 5
  periodSeconds: 3

③ 镜像瘦身 + 预热节点

镜像从 1.2 GB 压到 300 MB,冷节点拉取时间从 90 秒降到 20 秒。手段就是多阶段构建 + 精简基础镜像,这在弹性场景下不只是"省存储",而是直接影响扩容速度。

四、第三段:扩了也不顶用——指标选错了

有时候 HPA 工作得很正常,Pod 也确实扩了,但延迟还是高。原因往往是指标选错了。

CPU 不是好的扩容信号

对 IO 密集或依赖下游的服务,CPU 涨得慢、延迟涨得快:

流量翻倍 → CPU 从 40% 涨到 55%(没到 70% 阈值,不扩容)
         → 线程池排队 → P99 从 200ms 涨到 3s

这时候 CPU 利用率完全没反映真实压力。

更合适的信号:

场景 推荐指标
Web 服务 QPS、并发连接数、P99 延迟
队列消费 积压量(lag)
线程池型应用 活跃线程数、队列长度
有明确 SLO 的服务 错误率、SLO 燃烧率

这些指标需要业务自己暴露(如 Prometheus 格式),再通过自定义指标接入 HPA。

一个反向陷阱:扩容反而更慢

如果服务有强依赖的共享资源(数据库连接池、下游限流配额),盲目扩容会让所有实例一起抢同一个瓶颈:

4 个 Pod → 数据库连接 40 个,正常
扩到 20 个 Pod → 数据库连接 200 个,超过数据库 max_connections
→ 新 Pod 全部连接失败 → 反而更慢

扩容前必须确认:下游能接住扩容后的并发。 否则弹性伸缩只是把故障从应用层推到数据库层。

五、诊断清单

HPA 不动的时候,按这个顺序查,基本能一次定位:

# 1. 看 HPA 状态和最近事件
kubectl describe hpa order-api

# 2. 看 metrics-server 是否有数据
kubectl top pods

# 3. 看 Pod 是不是 Pending(节点资源不足)
kubectl get pods -o wide | grep Pending

# 4. 看节点池容量
kubectl describe nodes | grep -A5 "Allocated resources"

# 5. 看扩容事件的时间线
kubectl get events --sort-by=.lastTimestamp | tail -50

判断路径:

现象 结论
TARGETS: <unknown> 缺 requests.cpu 或 metrics-server 异常
指标正常但 Pod 数不变 检查 minReplicas/maxReplicas、behavior 策略
Pod 是 Pending 节点资源不足,扩节点池或开自动伸缩
Pod 起来了但没接流量 就绪探针未通过、预热未完成
全扩了但延迟不降 指标选错,或下游成为瓶颈

六、效果

指标 修复前 修复后
HPA 指标状态 <unknown> 正常
扩容触发时间 从不触发 30 秒内
Pod 就绪时间 4~5 分钟 70 秒
大促期间 P99 4 s 320 ms
Pod 峰值数 4(未扩容) 36
节点池缓冲 0 2 个节点

关键改动只有三项:补 requests.cpu、节点池留缓冲、加 startupProbe 并做应用预热。

七、小结

四句话:

  1. HPA 的分母是 requests.cpu,没设 requests 就等于没开弹性伸缩,TARGETS: <unknown> 就是这个信号。
  2. 弹性伸缩分两层:Pod 层(HPA)和节点层(节点池),节点没余量时扩出来的 Pod 只会 Pending。
  3. 从决策到接流量要 3~5 分钟,而洪峰只有 90 秒——所以要靠预留缓冲和预热,不能指望实时扩容。
  4. CPU 不是万能信号,IO 密集和依赖下游的服务应该用 QPS、延迟或队列积压来驱动扩容。

如果只记一句:弹性伸缩解决的是"长期趋势",短时洪峰必须靠预留容量和快速预热来扛。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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