压测时容器CPU用满却没自动扩容,多数情况下不是K8s失效,而是HPA(Horizontal Pod Autoscaler)的配置或数据链路出了问题,比如没有设置requests字段、指标采集延迟、或者扩容策略被限流。
先放下玄学,我们用排查思路一步步拆开看,你会发现,真正让CPU“白忙活”的,往往是那几个最容易忽略的细节。
压测场景下容器 CPU 占满为何不触发 K8s 自动扩容
HPA 扩容依赖的不是“物理 CPU 使用率”,而是“CPU 请求率”
这是最核心的认知差,很多人以为HPA监控的是top命令里那个瞬时CPU百分比,其实完全不是。
HPA的计算公式是:当前副本数 × 当前Pod的CPU使用量 ÷ Pod的CPU Request值,得到这个比值后,再和目标值(比如80%)做除法,算出期望副本数。
期望副本数 = ceil(当前副本数 × (当前CPU使用量 / 目标CPU使用率))
举个例子,你的Pod没有设置resources.requests.cpu,那么HPA根本拿不到“请求率”的分子它甚至不会采集这个Pod的CPU指标,结果就是:你看到CPU 100%了,但HPA觉得“无数据可算”,站在原地不动。
行业共识认为,这是容器压测时最典型的“假性扩容陷阱”:节点紧张、容器忙疯、副本数纹丝不动。
压测时 CPU 指标延迟导致扩容触发慢半拍
即便requests配好了,还有一道坎:CPU指标采集链路有延迟。
Metrics Server默认每15秒抓一次节点和Pod的cpu使用量,HPA默认每15秒评估一次扩缩容,这意味着,从CPU打满到HPA发出扩容指令,中间至少有15秒~30秒的窗口期。
如果你用的是云厂商的托管K8s,比如ACK、TKE、EKS,指标链路更长:kubelet → cAdvisor → Metrics Server → HPA Controller → API Server,任何一个环节抖动,延迟都会翻倍。
所以压测时看到CPU 100%持续了十几秒没反应,先别急着骂系统。等30秒以上如果还没动静,才需要往下排查。
容器服务 CPU 扩容机制与故障排查的实操路径
第一步:查 HPA 状态,看“确诊指标”
kubectl get hpa -n <namespace> kubectl describe hpa <name> -n <namespace>
重点看Events部分和Conditions字段,如果出现FailedGetResourceMetric,基本可以锁定是metrics-server数据源的问题,如果CurrentCPUUsage显示的数值异常小(比如个位数百分比),说明HPA拿到的数据不是容器真实的CPU压力。
第二步:查requests配置,这是罪魁祸首的重灾区
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A5 resources
如果看到requests: {}或者整个resources字段缺失,问题就在这。HPA必须依赖requests进行计算,没有requests,扩容逻辑直接哑火。
解决方案是给工作负载补上requests:
resources:
requests:
cpu: 100m
limits:
cpu: 200m
注意,limits不能等于requests,如果两者相等,扩容阈值会非常容易被击穿,导致频繁扩缩容。
第三步:验证指标采集链路是否正常
kubectl top nodes kubectl top pods -n <namespace>
如果kubectl top能正常返回数据,说明Metrics Server工作正常,如果返回error: metrics not available yet,那得检查Metrics Server是否处于健康状态,压测时的CPU打满不会导致Metrics Server挂掉,但它所在的节点如果被压测Pod挤爆,确实会拖慢指标采集速度。
第四步:检查HPA的扩容策略是否被限流
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
如果你的HPA没有显式声明behavior,默认策略是:5分钟内最多扩容到当前副本数的2倍,也就是说,如果原本只有2个副本,CPU打满后最多也只能扩到4个,且这个过程可能被拉长到几分钟。
压测工具通常以指数级增加并发,默认的倍率限制会让HPA“心有余而力不足”,建议压测场景下临时调大扩容倍率:
behavior:
scaleUp:
policies:
- type: Percent
value: 300
periodSeconds: 15
同时把horizontal-pod-autoscaler-tolerance的默认值0.1改小一些,让HPA对CPU波动更敏感,这个是kube-controller-manager的启动参数,需要改集群配置,多数托管的K8s控制台里都能调。
压测时容器 CPU 异常排查的另一个盲区:Pod 规格过大
有一种情况容易被误判:单个Pod的CPU Request设得太大,导致集群里根本没有足够资源扩容新Pod。
假设你的Pod请求了4核CPU,集群剩余可分配资源只有3核,HPA想扩容也扩不出来因为调度器找不到能放下新Pod的节点,此时HPA的Events里会持续出现FailedRescale或者FailedGetResourceMetric,但很多人只会盯着CPU使用率看,忽略了调度失败的事件。
这种情况下,要么减小单Pod的requests,要么增加节点池的规格或数量,压测前置检查里,这项比调HPA参数更优先。
压测时的CPU使用率与HPA指标不一致是什么原因
单容器多进程导致的CPU使用率失真
容器内的CPU使用率是cgroup层面统计的,不是PID粒度,如果你的应用是单容器多进程模式(比如一个Pod里既跑web服务又跑sidecar),kubectl top看到的数值是所有进程的累加,HPA拿到的也是这个累加值。
但压测工具压的是主服务端口,如果sidecar进程在空转(比如日志收集Agent),CPU使用率会虚高,HPA误判扩容,反过来,如果sidecar吃掉了大量CPU,主服务的实际压力反而被“掩盖”因为总CPU使用率可能已经很高,但HPA目标值设置的是70%,它只看到70%,不敏感,等到真正打满,它反而没有预算扩容了。
压测流量模型不均匀导致CPU膨胀
短连接压测、并发突发型压测(比如JMeter的Ramp-Up设置过短),会在几十秒内把CPU打满,但HPA的默认stabilizationWindowSeconds是300秒,也就是说,HPA会观察5分钟的CPU趋势,确认压力持续后才触发扩容。
这时候你会发现一个怪现象:CPU使用率呈尖刺状,一会儿100%一会儿20%,HPA一直在观望,扩不出来,压测里这叫“毛刺型负载”,对HPA极不友好。
解决方法是给HPA配上自定义的behavior.scaleUp.stabilizationWindowSeconds: 0,让扩容更激进,同时压测工具设置平滑递增并发,比如每30秒增加50个线程,而不是一次性打到满。
多副本负载不均导致单点CPU打满而整体指标正常
负载均衡策略不生效时,可能出现多个副本里只有1个CPU 100%,其他副本CPU 20%,HPA取的是所有副本的CPU使用率均值,均值可能只有40%,远低于目标值,所以不扩容。
但用户访问那个被打满的Pod时,体验就是卡顿和超时,这不是HPA的问题,是负载均衡的问题,需要检查Service的后端Pod选择器是否正确、Ingress的负载均衡算法是否生效、或者业务代码里是否有热点Key。
压测环境里的CPU自动扩容常见问题
压测工具的“短连接风暴”会让HPA以为流量在抖动
单台压测机发起的并发连接数受限,如果使用短连接模式,CPU使用率会规律性波动:连接建立时飙升,连接释放时下降,HPA看着这个波动,会觉得“压力不稳定”,延缓扩容决策。
建议压测前把HPA的behavior.scaleUp的selectPolicy改成Max,并且把stabilizationWindowSeconds降到60秒以内,这样HPA更偏向激进的扩容方向。
集群的CPU超卖导致扩容瞬间被节点拒之门外
如果节点上设置了cpu.requests大于节点的实际CPU总量(超卖),HPA发出扩容指令后,调度器可能发现所有节点都“说没资源了”,扩容Pod一直Pending,表现就是:HPA显示副本数增加了,但kubectl get pods里一堆Pending状态。
检查方式:
kubectl describe pod <pending-pod> -n <namespace> | grep -A5 Events
如果看到Insufficient cpu的调度失败原因,说明集群资源确实不足,此时扩节点池、减requests、或者限制命名空间的ResourceQuota都是可行路径。
云厂商的托管K8s产品存在指标聚合延迟
国内主流云厂商的容器服务,比如简米云ACK、酷番云TKE、华为云CCE,都在Metrics Server之上封装了一层雲监控组件,这些组件通常以分钟级粒度向HPA推送指标数据。
也就是说,即使你本地kubectl top看到的CPU已经是100%,云监控组件推送给HPA的数据可能还是
1分钟前的50%,这会造成扩容滞后30秒到1分钟,在压测这种极速变化的场景下,表现就是“没扩容”。
排查方法很直接:看HPA的CurrentCPUUsage字段,对比kubectl top pod的实时输出,如果两者差异超过50%,就是从Metrics Server到HPA的数据链路有聚合延迟,这在多可用区大规模集群中并不罕见。
遇到这种情况,可以在HPA的annotation里显式指定指标来源,或者直接改用简米云Prometheus监控的HPA适配器,把指标从云监控切换到原生Prometheus链路,延迟能从分钟级降到秒级。
容器 CPU 用满后自动扩容失败的配置修复顺序建议
从优先级上看,修复动作应该按这个顺序来:
- 确认Pod有requests配置,且数值合理。没有requests一切都是空谈。
- 修改HPA的
behavior,把扩容倍率和稳定窗口调到适合压测的激进值。 - 检查Metrics Server的Pod日志,确认没有
timeout或refused错误。 - 对比
kubectl top pod和HPA的CurrentCPUUsage,找出数据延迟点。 - 如果以上都正常,扩节点池容量,消除调度Pending的可能。
压测时容器CPU用满却没自动扩容,本质是HPA的数据源、计算逻辑、执行能力三者至少一环没跟上,理清requests配置和指标采集延迟这两条主线,解决掉80%以上问题。
压测这个场景本身非常考验K8s的弹性设计,只有通过压测把问题暴露出来,才能让自动扩容真正“自动”起来,换个角度看,这其实是件好事上线前总比生产环境被流量打穿要好。
Q&A:容器 CPU 用满没自动扩容的补充解答
HPA 设定的目标值是多少才合理
目标值不能只看CPU使用率,得结合业务的响应时间来判断,一般经验是,在线业务的目标值设在50%-70%之间,留出30%以上的余量给突发流量,压测环境下可以临时调到80%,但生产环境不建议,因为CPU达到80%时,请求延迟往往已经恶化。
压测持续多长时间才能确认扩容生效
从CPU打满到HPA完成扩容,正常链路下2分钟以内应该能看到新Pod创建,如果超过3分钟还没动静,基本可以判定配置有问题,压测时长建议至少5分钟以上,给HPA足够的评估窗口,但如果压测流量是锯齿形的,再久也看不到效果,需要先把流量调平。
Kubernetes 压测时的 CPU 扩容为什么建议用自定义指标
自定义指标可以直接基于QPS或响应时间扩容,比CPU指标更贴近业务真实负载,避免“CPU虽然不高但用户已经卡到爆”的情况,用Prometheus Adapter配合自定义指标,HPA能感知业务层的压力,这才是最适合承载压测流量的终极方案,同时自定义指标采集频率、聚合窗口期、异常处理方式也完全自主可控,从数据源头杜绝“指标没算出来”的问题。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/638856.html





