news 2026/10/21 18:13:56

K8s集群扩容实战:从HPA到Cluster Autoscaler的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
K8s集群扩容实战:从HPA到Cluster Autoscaler的完整指南

1. 扩容前先想清楚:你缺的到底是Pod还是节点

先说一个我踩过好几次的坑。某个大促前夜,业务方跑过来跟我说"集群扛不住了,赶紧扩容",语气急得像机房已经冒烟了。结果我打开监控一看,CPU总使用率不到30%,集群里几百个节点一大半都在闲着,真正的问题是有个服务的Pod全都调度到同一批机器上去了,而那批机器的本地盘快写满了。

这种场景下你扩再多的Pod都没用,因为Pod还是会被调度到那几台“热”节点上。真正该做的是调整调度策略、重新打散Pod,而不是闭着眼睛加机器。

所以在聊K8s扩容之前,我强烈建议你先花十分钟回答三个问题:

  1. 现在卡住业务的瓶颈是什么?是Pod副本数不够,还是单Pod的资源配额太小,还是节点总数不够?
  2. 你的扩容是“临时救火”还是“长期规划”?这决定了你是手动扩容快速生效,还是需要配置HPA、Cluster Autoscaler做自动化。
  3. 你扩出来的资源,业务真的能用上吗?有些时候应用本身的单实例吞吐已经到上限了,加副本不加并发配置,扩了等于白扩。

我个人的经验是,把K8s扩容拆成两个层面来看会清楚很多。第一层是工作负载层,也就是Deployment、StatefulSet里的副本数调整,以及HPA、VPA这类自动扩缩容机制;第二层是基础设施层,也就是集群节点、NodePool的扩缩容,以及控制面组件(etcd、kube-apiserver)的容量规划。这两层经常被混在一起讨论,但实际上它们是两个完全不同的决策路径,扩容方式、生效时间、风险等级都天差地别。

这篇文章我就按这两条线展开,把我在生产环境里做K8s扩容的完整思路、命令、参数和踩坑记录都写出来。

2. 工作负载层扩容:HPA的正确打开方式

2.1 HPA不是配好了就完事,算法你得先懂

HPA(Horizontal Pod Autoscaler)是K8s里最常用的工作负载层扩容手段,但很多人对它的理解停留在“设置一个CPU阈值,超过就扩容”这个层面。如果你真的按这个朴素理解去配置,大概率会在流量高峰看到一些让你困惑的现象。

HPA的核心算法是这样的:它会周期性(默认15秒,可以通过--horizontal-pod-autoscaler-sync-period调整)获取Pod的监控指标,然后计算一个期望副本数,计算公式是:

期望副本数 = ceil(当前副本数 × (当前指标值 / 期望指标值))

举个例子,你的Deployment当前有10个副本,HPA配置的CPU目标利用率是50%,现在实际CPU使用率是75%,那么期望副本数就是ceil(10 × (75% / 50%)) = ceil(15) = 15。如果实际使用率是30%,那期望副本数就是ceil(10 × (30% / 50%)) = 6。

这个公式看起来简单,但有几个隐藏细节:

  • 指标采集延迟:Metrics Server默认15秒采集一次节点和Pod的指标,HPA再15秒同步一次,所以从流量上涨到Pod真正扩出来,中间至少有30秒以上的延迟。如果你们的业务是陡峭的尖峰流量,比如秒杀、抢购,光靠HPA大概率来不及,需要配合“提前扩容”或者KEDA这类事件驱动的方案。
  • 扩容瞬间的指标回落:新Pod启动后,CPU使用率会被平摊到更多Pod上,指标值会快速回落。如果回落幅度很大,HPA可能很快就触发缩容,副本数又降回去。这就形成了“扩容-缩容”的抖动。解决方法是配置behavior字段,给缩容设置更长的稳定窗口(stabilizationWindowSeconds),甚至直接禁止缩容到某个水位以下。

2.2 实操:一个带缓冲的HPA配置长什么样

我现在的标准做法是,给核心业务配置HPA时不要只配CPU和内存,还要把behavior字段加上,同时给一个minReplicas保底值。下面这个配置是我在模拟项目X(一个电商网关服务)里实际用过的,你可以直接抄走改改:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: gateway-hpa namespace: prod spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: gateway minReplicas: 5 maxReplicas: 50 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 20 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 60 # 允许每60秒最多翻倍副本数 - type: Pods value: 4 periodSeconds: 60 # 同时也允许每60秒最多增加4个Pod,取两个策略中的较大值 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 70

这里有几个参数值得展开说说。

scaleDown的stabilizationWindowSeconds: 300:意思是缩容决策要等5分钟稳定窗口,也就是说即使指标已经降下来了,HPA也会再观察5分钟,确认指标没有反弹才执行缩容。这对于处理有抖动的业务特别管用。policies里我配了每次最多缩20%的Pod数,防止一次性缩掉一大半副本导致服务能力断崖式下跌。

scaleUp侧的policies我配了两个,一个是百分比策略,允许副本数每60秒最多翻倍;一个是固定数量策略,允许每60秒最多增加4个Pod。HPA会取这两个策略计算结果中更大的那个值。为什么要配翻倍策略?因为如果当前只有5个副本,流量突然涨了10倍,按固定4个Pod的扩法要十几个周期才能跟上,而按翻倍策略5→10→20→40,两个周期就追上来了。这是处理突发流量非常实用的小技巧。

2.3 别忘了给单Pod预留突发余量

配置HPA的时候,很多人只盯着Deployment的requests,却忽略了limits和Pod内的具体线程配置。这会导致一个很尴尬的场面:HPA疯狂扩容,副本数从5扩到了50,但每个Pod的能力并没有提升——因为它们虽然拿到了更多的CPU配额,但应用内部的线程池、连接池、协程数还是按照1个Pod的标准配置的。

举一个真实例子。我们有个Java服务,requests配了1核,limits配了2核,HPA按CPU 60%扩容。按说CPU打到60%就会扩容,但实际观察到的现象是:副本数确实涨了,但单Pod的RT(响应时间)没有下降。后来查下去才发现,这个服务用了一个默认线程池,核心线程数只有10,所有请求都在这个线程池里排队。加Pod只能增加排队通道,不能增加处理能力——因为每个Pod进来之后,线程池还是只有10个线程在干活。

所以我的建议是:在做K8s扩容方案时,一定要把“单Pod吞吐能力”作为一个独立变量来评估。你需要知道一个Pod在目标负载下每秒能处理多少请求、数据库连接池占用多少、下游连接的KeepAlive上限是多少。这些参数决定了“扩多少副本才够”。

3. 从HPA到VPA:为什么我不建议新手直接上VPA

3.1 VPA能解决什么问题,解决不了什么问题

VPA(Vertical Pod Autoscaler)是另一种扩容思路——它不增加副本数,而是动态调整单个Pod的CPU和内存requests。听起来很美好,但我在生产环境里很少直接启用VPA的自动模式(Auto模式),原因后面会说。

VPA解决的核心问题是“requests配得不合理”。很多团队配Pod资源的时候是拍脑袋定的,比如统一给1核2G,结果有的服务高峰期内存冲到1.8G,有的服务平时只用0.1核。VPA会持续监控Pod的实际用量,然后给出一个建议值,或者直接帮你把requests改掉。

但VPA有几个非常实际的问题:

  • Pod需要重启才能生效。VPA调整的是requests,而requests只能在Pod创建时生效,所以VPA改完之后会驱逐并重建Pod。这可不是无感的。
  • 和HPA同时用容易打架。如果你对一个Deployment同时开HPA和VPA,VPA会去调整requests,HPA会基于新的requests重新计算副本数,两者经常互相干扰。官方推荐的做法是区分场景:要么用HPA处理负载波动,要么用VPA处理资源规格优化,不要同时对同一个工作负载开两个。
  • VPA的recommender需要时间学习。刚部署完VPA,它需要积累一段时间的监控数据才能给出合理的推荐值,通常是24小时以上。指望它“装完就精准”是不现实的。

3.2 我实际用的VPA方式:先当建议工具用

我现在的做法是,给VPA配mode: Off或者mode: Initial,把它当成一个“资源建议工具”来用,而不是自动改配置的工具。流程是这样的:

  1. 部署VPA并设置updateMode: "Off",它不会改任何Pod,只是持续输出推荐值。
  2. 跑一周,观察推荐值和实际使用率的分布。
  3. 每周挑几个服务,手动把requests调成VPA推荐值的1.2倍左右,留出20%的余量。
  4. 同时记录调整前后HPA的行为变化,确认没有出现奇怪抖动。

为什么留20%余量?因为VPA的推荐值本身就是基于历史分位数算出来的,比如percentile: 95的推荐值意味着95%的时间用量不会超过它。但线上流量是有随机性的,留一点余量能避免因为某个异常波动就频繁触发扩容。

3.3 什么时候VPA的Auto模式可能是合适的

也不能一棍子打死VPA Auto模式。有一种场景我确实会启用:StatefulSet配合本地盘或有状态服务。有状态服务扩缩容很麻烦,因为涉及数据分片、数据迁移,不是加个副本就行。这种情况下用VPA去微调每个Pod的资源配额,反而比加副本更安全。

不过即使在这种场景,我也有一个硬性要求:必须配置maxAllowed,并且这个上限要比实际情况低10%左右。VPA如果没有上限约束,一旦某个容器出现内存泄漏,它会无限调大requests,直到把节点资源耗光。这算是VPA使用中的安全底线。

4. 扩容到最后必然要面对的问题:节点池与Cluster Autoscaler

4.1 Pod扩容到顶之后的瓶颈

工作负载层扩容做到一定程度,你一定会遇到另一个瓶颈:Pod想扩,但节点资源不够了。具体表现是Pod一直Pending,看事件会看到一堆0/5 nodes are available: insufficient cpu之类的提示。

到了这一步,扩容的对象就从Pod变成了节点。手动加节点不难,云服务商的控制台点几下按钮就行,但问题是生产环境流量是动态变化的,手动加节点不仅慢,而且容易加多——加多了成本扛不住,加少了又不够。所以Cluster Autoscaler(以下简称CA)就成了节点层扩容的核心工具。

CA的工作原理并不复杂:它持续关注那些因为资源不足而Pending的Pod,一旦发现Pod长时间调度不上去,就会触发节点池扩容,申请新的节点;反过来,当节点的资源利用率持续偏低,且上面的Pod可以被调度到其他节点时,它会排空这台节点并缩容。

4.2 配CA前先把NodePool规划好

CA虽然好用,但它只会帮你加机器,不会帮你选机型。所以在配置CA之前,你必须先把节点池规划好。我见过太多团队直接把所有业务混在一个节点池里开CA,结果流量一起来,CA把一批性能型机器加进来了,但那些业务根本用不上高性能CPU,纯属浪费钱。

我的规划方式是分成三类节点池:

节点池类型机型建议主要用途
Web/无状态池通用型,CPU和内存均衡网关、Web服务、前端应用
计算型池CPU核数多、主频高数据处理、AI推理、批处理任务
内存型池内存比例高缓存类、内存数据库类负载

通过给不同工作负载打nodeSelector或nodeAffinity,强制它们落到对应的节点池。这样CA扩容时就能按节点池维度申请,不会出现“加了一堆计算型机器给Web服务当CPU闲鱼用”的情况。

4.3 Cluster Autoscaler的关键参数与避坑

CA的部署本身不难,大部分云服务商都有托管版的CA(比如ACK的节点自动伸缩、EKS的Karpenter/CA、TKE的节点池伸缩),自建集群也可以用Helm部署开源版。但参数配置上,有几个坑值得单独说说。

第一个坑:scale-down相关的参数默认值太激进。CA默认的缩容判断条件包括:节点利用率低于50%(scale-down-utilization-threshold默认0.5),并且Pod可以成功调度到其他节点。但在实际业务中,有些节点的利用率是动态波动的,可能只是某一瞬间低了一下就被判为缩容候选,结果节点还没删干净,流量又上来了,Pod又Pending了。我的建议是:

  • 提高缩容阈值判断周期,把scale-down-delay-after-add和scale-down-unneeded-time调大,比如缩容前至少观察10分钟。
  • 降低缩容利用率阈值到0.4甚至0.3。宁可多留一点闲置机器,也不要频繁扩缩容。节点创建和销毁是有成本的,尤其是每次新节点加入后,镜像拉取、Pod调度、就绪检查都要时间。

第二个坑:CA和HPA的响应速度叠加可能导致“扩容雪崩”。高峰期时,HPA先扩Pod,Pod Pending,CA再扩节点,节点从申请到Ready通常要几分钟。如果你们的业务流量是持续上涨的,这个链路最后可能会导致:HPA把副本数拉满、CA把扩容额度用尽、但Pod因为镜像还没拉完而长时间ContainerCreating。解决思路是在流量高峰期前手动预扩一批Pod和节点(比如提前2小时把副本数从10调到30),让CA先处于一种“有富余节点”的状态。这个操作我每次大促前都会做,算是验证过多次的成熟做法。

第三个坑:多节点池分配不均。CA默认会选择一个节点池来扩容,但它并不总是选到你想让它选的那个。如果你的多个节点池优先级没有配置,CA可能因为某个池里便宜的机型而把新流量都塞进去,然后那个池子被塞爆,其他池子闲置。解决办法是给节点池配置expander策略,比如priority模式,按你定义的优先级顺序选择扩容的池。

4.4 Karpenter/弹性节点和CA的取舍

如果你用EKS或者自建K8s但集群在云上,还有一个选择是Karpenter。它在某些场景下比CA更聪明——CA本质上是“发现资源不足然后申请整个节点”,Karpenter可以更细粒度地根据Pod的调度需求(比如GPU型号、可用区、实例类型)直接创建最合适的实例。Karpenter的调度效率更高,尤其适合异构负载比较多的集群。

不过Karpenter的学习成本比CA高不少,而且如果你的工作负载相对标准化(都是占CPU内存比较均衡的Web服务),CA配合精心规划的节点池已经完全够用。我的原则是:业务简单就上CA,业务复杂再研究Karpenter。

5. 控制面扩容:最容易被忽视的容量天花板

5.1 控制面也会成为瓶颈,尤其是etcd

很多人做K8s扩容只盯着业务节点和工作负载,忽略了控制面组件本身的容量。事实上,当你的集群规模跨过某个量级(比如节点数超过500、Pod数超过1万),控制面的问题就会逐渐浮出水面。

排在最前面的通常是etcd。etcd是K8s所有状态数据的存储层,它的写入能力是有硬上限的——受到磁盘IOPS、网络延迟、Raft共识协议等多方面限制。一旦etcd写入延迟变高,整个集群的调度、Pod状态上报都会变慢,表现就是:Pod创建速度骤降、kubectl get nodes响应超时、节点上报状态异常。

这里面典型的诱因是事件(Event)过多。很多团队没有配置事件保留策略,一堆Pod反复崩溃重建,会产生海量Event写入etcd。Event对象在etcd里占据的存储和写入带宽比你想象的大得多。我处理过的一个案例:某个命名空间里一个异常Deployment每小时产生几十万个Event,直接把etcd写入IO打到了80%以上,整个集群的控制面都跟着受影响。

5.2 控制面扩容的实操建议

控制面扩容不像工作负载层那样“加个副本就行”,它更依赖前置规划和架构调整。我自己总结出来的几条经验:

etcd层:

  • 控制Event数量。要么给核心命名空间设置Event的TTL(通过kube-apiserver的--event-ttl参数),要么接入更完善的事件采集系统(比如采集到外部存储,而不是全量留在集群里)。
  • 定期做etcd defragmentation(碎片整理)。etcd的存储是B+树实现的,删除大量key之后空间不会自动回收,需要执行etcdctl defrag。建议每个季度做一次,操作前必须在低峰期且做好备份。
  • 如果确实到了etcd容量瓶颈,最根本的办法是数据分拆——把一部分工作负载迁移到另一个K8s集群里。这是最不想做但最有效的扩容方式。

kube-apiserver层:

  • 给kube-apiserver预留足够的CPU和内存,同时关注它的LIST请求数量。有些客户端(比如各种Controller、Operator)会频繁LIST全量资源,导致apiserver CPU打满。可以通过--max-requests-inflight限制并发请求数,但要小心不要压到正常调度请求。
  • apiserver是无状态的,支持水平扩容。自建集群时多部署几个apiserver副本,然后通过负载均衡分发请求,这个方案对提高读能力很有效,一般不需要像etcd那样做数据分拆。

Node和kubelet层:

  • 当节点数超过几百个时,kubelet节点心跳和Pod状态上报会给apiserver和etcd带来可观的压力。可以通过调大kubelet的--node-status-update-frequency(默认10秒)到15秒甚至30秒来降低上报频率,减轻控制面压力。

5.3 探测控制面容量是否到顶的三个手段

生产环境里,我习惯用这三个手段快速判断控制面是否已经到容量瓶颈:

  1. 看apiserver的延迟指标:重点关注apiserver_request_duration_seconds的P99值,如果长时间超过1秒,说明控制面已经有点吃紧。
  2. 看etcd的写入延迟:etcd_disk_wal_fsync_duration_seconds如果P99超过几十毫秒,说明磁盘IO可能是瓶颈。
  3. 看调度延迟:kube_scheduler_pending_pods如果持续有积压,同时节点资源明明充足,那就不是业务资源不够,而是调度链路处理不过来了。

这三个手段配合前面说的工作负载扩容方案,基本能覆盖我从业务Pod到控制面的完整扩容判断链路。

6. 大促前的扩容演练清单

理论说了这么多,最后分享一个我每逢大促前都会完整执行的扩容检查清单。这个清单是我在多次大促保障中迭代出来的,你可以把它当成你的“最后一公里”保险。

  1. 检查HPA配置的点位:确认所有核心服务的HPA存在、minReplicas合理、behavior字段有缩容稳定窗口。不要有大促前才第一次上HPA的服务,至少要提前两周跑过完整链路。
  2. 检查Cluster Autoscaler的扩容测试:用一个临时Deployment设置超量副本,确认CA能够在15分钟内完成节点扩容。如果超过15分钟,说明节点池配额、镜像拉取链路里有问题,大促前必须解决。
  3. 检查镜像拉取速度:新节点启动后,从节点注册到Pod Ready的时间是多少?如果在5分钟以上,建议检查镜像仓库是否有节点本地缓存、镜像大小是否异常。
  4. 检查节点池配额:云服务商的节点配额上限是不是够用?如果你规划扩容到100台机器,但云账户的配额只有50台,大促当天怎么扩都不够。
  5. 检查etcd和apiserver延迟:大促前一周的监控数据拉出来看一眼P99延迟,如果有明显上升趋势,提前处理。
  6. 和业务团队对齐“扩容峰值预期”:你们预估的最大QPS是多少?对应副本数是多少?节点数是多少?这些数字必须提前算出来写进文档,而不是大促当天现场拍脑袋。

这套演练我建议大促前至少跑两次,一次提前两周做“故障演练+容量摸底”,一次提前一天做“全链路最终确认”。跑过几轮之后,你会发现大促当天的扩容操作基本就是“按脚本执行,看监控确认”,不会再有那种心跳加速的慌乱感。

我个人这几年做扩容最大的体会是:K8s扩容不是一道“加机器”的计算题,而是一套从工作负载到基础设施、从自动策略到人工预案的完整工程体系。希望这篇基于实战经验的总结,能帮你少走一些我走过的弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 18:10:59

LoRA 服务化推理核心算子拆解:从 vLLM 调度到 Ascend NPU 实现

做 LLM 服务化的同学,这两年应该没少跟 LoRA 打交道。一个基座模型挂几十上百个 LoRA adapter,是低成本做垂直场景外挂最常见的玩法;而 vLLM-Ascend,就是把主流高性能推理框架 vLLM 迁移到 Ascend NPU 上跑通的一套工程实现。我在…

作者头像 李华
网站建设 2026/10/21 18:10:59

CNAS检验机构认可规范文件清单使用指南:版本跟踪与体系文件自查

简介:这份PDF是CNAS检验机构认可规范文件清单,更新至2021年1月19日,面向检验检测机构的质量负责人、体系工程师及认可申报人员,帮助其快速掌握现行有效的认可规则、准则与应用说明,避免误用已废止版本。资源包共1个PDF…

作者头像 李华
网站建设 2026/10/21 18:10:24

JMeter+Dubbo插件直连RPC接口,高效性能压测实践指南

做后端服务性能验证的时候,很多测试同学的第一反应是把服务接口按 HTTP 方式压一遍。这个思路本身没错,但如果被测对象是 Dubbo 服务,链路里一旦带上网关、鉴权、负载均衡这些环节,压出来的 TPS 和响应时间其实是“整条链路”的表…

作者头像 李华
网站建设 2026/10/21 18:09:37

opencode架构解析:双会话内核与事件溯源机制

如果你用 opencode 用得久了,大概率会有一种感觉:它看起来只是一个跑在终端里的 AI 编码助手,但内部的结构远比“调一下大模型、然后把结果贴出来”要复杂得多。从用户视角看,你输入一句话,它在终端里改文件、跑命令、…

作者头像 李华
网站建设 2026/10/21 18:09:25

通达信筹码获利比例指标公式源码:五色柱区间与实战用法

简介:这份文档面向股票技术分析初学者与通达信指标爱好者,提供筹码获利比例指标的完整公式源码与研判方法,帮助读者理解如何借助获利比例判断个股强弱趋势与所处阶段。资源包共1个doc文件,大小约302KB,内容以指标源码、…

作者头像 李华
网站建设 2026/10/21 18:08:44

纯Python手写BP神经网络:从链式法则到反向传播完整实现

1. 项目概述与整体设计思路1.1 这个项目要解决什么问题BP神经网络这个名字,搞过机器学习的人应该都不陌生。不管是TensorFlow还是PyTorch,底层那套自动求导、梯度反传的机制追根溯源都是在模拟BP算法的工作流程。但问题在于,框架用久了以后&a…

作者头像 李华