☰
Kubernetes HPA水平自动扩缩容:原理、配置与调优
2026/10/21 4:42:43 网站建设 项目流程

1. 为什么服务的副本数必须"自己动"

1.1 固定副本部署的尴尬处境

我见过不少团队,服务上线之后就把副本数定死在一个固定数值上,轻易不动。比如一个业务预估日均十万请求,运维给开了四个副本;等白天流量冲到高峰,四个副本CPU使用率直接拉满,晚上流量跌下去,四个副本又全在空转。资源成本一分没少,服务体验却因为容量不够时好时坏,这是固定部署模式最无解的矛盾。

这种模式不是不能用,但它要求你对业务流量的预判极其准确。可惜现实业务从来不是一条平线,活动来了流量瞬间翻三倍,某个接口被外部调用方异常重试也会导致负载骤升。等你在监控大屏上发现异常再手动扩容,用户其实已经经历了几分钟的响应超时。尤其是在半夜,从接到报警到完成操作,通常要二十分钟以上,而这二十分钟的损失可能比白天一个小时的还大。

也有人想过,干脆把副本数常年开高一点不就行了?我们不是没试过。结果是成本虚高,业务低谷期三四个副本就能扛住的量,常年开十来个,一个月下来多烧的资源够再做几个新功能。就算成本能扛住,集群资源总量也有上限,不可能永远靠堆副本解决问题。

1.2 HPA控制器到底做了什么

HPA的全称是HorizontalPodAutoscaler,中文常叫水平Pod自动扩缩控制器。它做的事情用一句话说:盯住服务的负载指标,负载高了自动加副本,负载低了自动减副本,整个过程不需要人半夜爬起来点鼠标。它是一套自动化的控制逻辑,核心思路和家用空调的温控器很像——你设定一个期望温度,空调自己根据传感器读数决定继续制冷还是停机,不需要你手动去拨开关。

放在容器编排平台上,HPA就是那个"温控器",Pod副本数就是它控制的"压缩机功率"。具体来说,HPA会周期性读取你所指定工作负载的监控指标,比如所有Pod的CPU平均使用率,然后用一个固定公式算出"当前应该有多少个Pod",再把这个数字同步给工作负载控制器,由它来真正执行新增或删除Pod的操作。整个过程是持续运行的闭环,默认十几秒就会执行一次判断,不是一次性任务,也不是定时脚本。

它和固定计划的最大区别在于:HPA是基于实时反馈驱动的,不是基于预测。你不需要提前约定几点扩容、几点缩容,也不需要因为天气变化临时改计划。只要指标在变,它就能跟着变。往深了说,这是一种典型的闭环控制系统,反馈、比较、执行三步循环往复,系统自动逼近你设定的目标状态。

1.3 哪些场景适合交给HPA

HPA不是万能药,不是所有服务都适合用副本数来应对负载。根据我的实际经验,适合它的场景有几个共同特征。

第一是服务本身无状态。副本之间不共享会话、不绑定本地文件,随便扩容缩容都不会破坏数据一致性,比如无状态Web服务、API网关、消息消费者这类。第二是负载有比较明显的波峰波谷,比如白天高夜间低,或者活动期和日常期差异很大,HPA能在这中间省下大量资源。第三是工作负载已经正确配置了资源请求,因为HPA计算CPU利用率依赖的就是requests值,没配requests的Pod在指标计算上会直接失败。

反过来,也有几个场景我建议谨慎用甚至别用。数据库、缓存这类有状态组件,副本数的变化并不能简单等同于读写能力的线性扩展,数据同步、一致性、主从切换都可能成为隐患。强依赖本地存储或IP白名单的中间件,扩容后新实例接入本身要人工审批,自动扩缩反而会制造更多麻烦。批处理任务或短生命周期的任务容器也不适合,它们自己跑完就退出,副本数动态调整的意义不大,应该交给队列长度和调度逻辑去管理。

2. HPA的工作原理与计算逻辑

2.1 一条完整的数据链路

要在集群里用好HPA,你得先搞清楚一条指标数据是怎么从Pod一路走到HPA控制器手里的。这条链路上一共有四个核心角色,缺一环都不行。

第一个是指标采集器,它以守护进程的方式跑在每个工作节点上,负责抓取节点上所有容器的CPU、内存等资源使用数据。第二个是指标聚合层,把所有采集器上报的数据聚合成一个统一接口,也就是常说的metrics API,对外暴露每个Pod的实时资源用量。第三个是HPA控制器本身,它循环调用这个API,把拿到的平均值和目标值做比较,计算出需要的副本数。第四个就是工作负载控制器,HPA把计算出的期望副本数写到工作负载对象上,由它最终完成Pod的创建和销毁。

用一句话概括:采集器给数据,聚合层做汇总,HPA算差异,工作负载去执行。这个过程和我们平时做容量管理的思路完全一致,只是把原来的人肉判断变成了代码计算。还有一个小细节:HPA调整的是工作负载的副本数字段,真正的Pod调度动作仍然由副本控制器完成,HPA本身不直接创建和销毁Pod。

这里有个关键点要特别重视:metrics API返回的是当前使用量,而HPA计算利用率时要拿它和Pod的requests值做除法。如果Pod没有配置requests,HPA就拿不到"利用率"这个指标,会一直显示Unknown状态,不执行任何扩缩容操作。这是新手配置HPA后半天不生效的第一大原因,后面第3.2节我会再展开讲。

2.2 期望副本数怎么算出来的

HPA的默认计算逻辑并不复杂,核心就是一个公式:

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

如果你配置的是CPU平均利用率,那么当前指标值就是所有Pod当前CPU使用率的平均值,期望指标值就是你设定的目标百分比。举一个实战中的例子:当前工作负载有4个副本,平均CPU使用率是85%,你设定的目标是50%,那么期望副本数就是4 × (85 ÷ 50) = 6.8,向上取整后是7。HPA会在这个周期内把副本数从4调成7,让每个Pod分到的流量压力降到50%附近。

反过来,如果当前平均使用率是30%,目标是50%,计算结果是4 × (30 ÷ 50) = 2.4,向上取整是3。但注意,缩容不会立刻发生。HPA在设计上对缩容非常谨慎,会要求指标持续低于目标一段时间后真正动手,避免流量一抖就白白缩掉一批Pod。

计算过程中还有一个隐藏细节:如果某个Pod刚启动还在初始化,指标可能为0,它不会被当作稳定的负载样本参与平均。HPA对这类Pod有自己的容错逻辑,你不需要担心单独一个异常实例就把平均值带歪。但如果整个工作负载都是刚扩展出来的新Pod,指标本身还在爬坡阶段,HPA的第一次计算可能会有偏差,这也是为什么我建议新Pod启动要尽量快速就绪。

2.3 防抖机制:冷却窗口与稳定窗口

你可能会想,指标每分钟都在变,那HPA岂不是每分钟都在改副本数?如果按秒级响应去缩放,高峰刚过去两分钟副本就被缩没了,下个高峰又得重新扩容,Pod反复创建销毁,整个系统的稳定性反而会更差。所以HPA内置了缓冲机制,最核心的就是稳定窗口。

稳定窗口的准确含义是:HPA在决定调整副本数之前,会参考过去一段时间内所有计算出的期望值,而不仅仅是最近一次。如果最近一次计算的期望值突然很低,但前几次都很高,它会综合判断,避免做出一时冲动的缩容决定。默认情况下,缩容方向的稳定窗口明显更长,通常在五分钟上下;扩容方向的缓冲则很短,因为多创建几个Pod顶多浪费点资源,不会造成服务中断。

还有一个容易被忽略的机制:HPA在真正扩容时通常不是一口气从2个副本直接跳到10个,而是有单次变化幅度约束的。每次同步周期内能增加的副本数有限,这样既防止副本数量暴涨导致节点资源瞬间打满,也给新Pod的启动留出了时间。这种逐步试探的策略在业务量陡增时尤其重要,否则副本数暴涨的同时镜像还没拉完,扩了等于白扩。

2.4 API版本差异:从v1到v2

HPA的API接口从最早期的版本演进到现在的v2版本,主要变化是能力大幅扩展。早期版本只支持基于CPU使用率进行扩缩容,配置方式也比较简陋,一旦业务需要看内存或者自定义业务指标就完全无能为力了。

v2版本引入了完整的metrics数组,你可以同时配置CPU、内存,还可以通过自定义指标接入配置QPS、请求延迟、队列深度等业务指标。同时v2的配置对象里增加了behavior字段,让运维人员可以真正调整扩缩容的行为——比如允许快速扩容但限制缩容速度,或者给某个指标单独设置冷却时间。

简单说,早期版本解决的是"有没有HPA"的问题,v2版本解决的是"HPA听不听话"的问题。如果团队还在使用旧版本,我建议尽快迁移到v2。后面我给出的所有配置示例都基于v2,这也符合当前主流的实践标准。

3. 从零落地:配置一套可用的HPA

3.1 第一步:确认指标链路是通的

很多人写完HPA配置发现不起作用,第一反应是去研究计算公式,其实真正的问题往往出在指标链路。合理顺序应该是先确认集群里能拿到资源指标,再去做扩缩容配置。

判断链路是否通畅的方法很简单,在集群命令行工具里执行查看节点资源用量的命令,能正常显示每个节点的CPU和内存占用,说明采集和汇总这层没问题;再直接查看某一个Pod的用量,如果也能正常输出数值,说明指标已经能按Pod维度查到了,HPA最依赖的输入数据已经准备就绪。

如果执行后提示接口不存在或者直接报错,那就要先解决指标采集问题。通常你需要安装指标采集组件,它会在每个节点上运行,并向集群内部的API聚合层暴露指标数据。安装完成后,再重复一次上面的检查命令,直到能看到数据为止。这一步是整个HPA落地的地基,地基不稳后面做什么都是白搭。注意,不同环境里指标组件的部署方式差别很大,有的默认已经装好,有的需要手动安装并配置聚合器,别只看官方文档,一定要结合自己的集群实际情况去验证。

3.2 第二步:确认工作负载写了资源请求

前面反复提到requests字段,现在展开说。HPA计算CPU利用率时必须知道"每个Pod允许使用多少CPU",这个值就是通过资源请求写在工作负载配置里的。公式是:使用率 = 当前使用量 ÷ 请求值。比如一个Pod的requests.cpu是500m,当前实际用了450m,那CPU使用率就是90%。

反之,如果Pod定义里完全没有requests,指标链路虽然能查到绝对用量,比如450m,但HPA没法把它换算成利用率,只能把状态标记为Unknown。这也是我排查"HPA不扩缩容"问题时第一个检查的地方。

配置requests时有常见误区:有人把它理解为硬上限,抄了一个很低的值。其实requests本质是调度器用来决定"这个Pod需要多少资源"的预留值,是下限;limits才是硬上限。如果requests设得太低,HPA会低估负载,迟迟不扩容;如果设得过高,节点上能塞下的Pod数量会变少,资源利用率反而下降。所以我的习惯是基于压测数据来定,或者参考近一周的历史监控取P50到P75的用量,不靠拍脑袋。

下面是一个带资源请求的Deployment配置片段:

apiVersion: apps/v1 kind: Deployment metadata: name: web-server spec: replicas: 3 template: spec: containers: - name: web image: your-registry/web:latest resources: requests: cpu: 500m memory: 512Mi limits: cpu: "1" memory: 1Gi

3.3 第三步:编写并应用HPA清单

指标链路通了,工作负载也带上了资源请求,终于可以写HPA自己的配置文件了。先看典型结构:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-server minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60

逐个字段说清楚。scaleTargetRef明确告诉HPA管理的是哪个工作负载,这里指定的是刚才的Deployment。minReplicas和maxReplicas定义边界,就算计算结果低于2,HPA也不会把副本数缩到少于2个;流量再大也不会超过10个。这两个值通常由运维根据可用性要求和服务容量上限来定,min至少要保证基础可用性,max至少要覆盖高峰容量需求。

metrics数组里写要监控的指标。上面例子监控CPU利用率,目标是60%。当所有Pod平均使用率超过60%时,HPA会尝试扩容;低于60%时,在冷却窗口满足后逐步缩容。target的类型除了Utilization还有AverageValue,前者是百分比,后者是绝对数值。比如你想按"每个Pod平均CPU不超过1核"来管理,可以用AverageValue配置1000m。

写完后执行应用命令加载配置,再用查看状态命令观察HPA的数值变化。正常状态时会显示当前副本数、目标副本数、指标的当前值和目标值。如果指标显示Unknown,回到3.1节检查链路,别在这个阶段瞎调参数。

3.4 压测验证:让扩容真实发生一次

配置上线后不做一次真实压测,心里总是不踏实。我的做法是在隔离环境里对服务发起持续几分钟的高并发请求,观察HPA是否真的能把副本数扩上去。压测工具不限,能模拟预期流量的就行,关键是让负载保持在一定水平以上,持续足够长的时间。

先记录初始状态:副本数2,CPU使用率大约百分之十几。然后启动压测,把并发请求逐步拉高。大约一两个同步周期之后,HPA状态里的CPU使用率会开始往70%以上走,副本数开始变化,从2变成4,再过一会儿变成6。整个过程不需要任何人点扩容按钮,完全是控制器自己完成的,看到这个现象基本可以放心了。

压测期间我会重点观察几个点:新扩展的Pod是否正常接收流量、有没有因为镜像拉取慢导致启动时间过长、有没有节点资源不足导致Pod一直处于Pending状态。如果这些都没问题,说明扩容环节是健康的。压测结束后,通常再等一个完整冷却周期,确认缩容也能正常触发——只要负载下降,稳定窗口结束后副本数应自动回落。如果没有回落,就要去查缩容策略和稳定窗口设置。

4. 参数调优:让伸缩跟上业务的节奏

4.1 目标指标的选择方法

把HPA跑通只是第一步,真正让它用得顺手,还得仔细选指标和设目标值。最常见的错误是把CPU目标值写得很激进,比如设成90%。你可能会以为这样能榨干资源,但实际上CPU使用率达到90%时,Pod响应时间往往已经开始劣化,HPA在90%才触发扩容,等于是在悬崖边才踩刹车,风险极高。

我一般建议把CPU目标值设置在50%到70%之间,具体看服务的延迟敏感度。如果是面向用户的接口服务,可以更保守一些,比如50%,因为用户等不起;如果是内部异步任务,可以放宽到80%,晚几秒处理问题不大。核心判断标准是"服务在这个负载下是否还能有令人满意的响应时间",这只有压测能给答案,别光靠经验猜。

如果服务是内存密集型的,比如缓存类、大数据处理类,CPU反而不是最核心的指标,内存使用率才是。HPA的metrics数组支持同时配多个指标,计算时按每个指标分别算出期望副本数,最终取结果较大的那个作为最终值。但我不建议一上来就加太多指标,先找最直接反映服务容量的那一个,把链路调通,再慢慢叠加,否则排障时你会分不清是哪个指标触发了扩容。

4.2 用behavior字段调教扩缩容节奏

HPA从v2开始提供behavior字段,这基本是高级玩家必备。默认行为虽然能用,但默认的缩容节奏可能不符合业务预期。比如流量每分钟都在波动,默认缩容稳定窗口可能太保守,导致高峰过后副本数还停在高位;或者流量突增时默认扩容速度不够快,高峰期初期就会丢弃请求。

通过behavior可以分别控制扩容和缩容的策略。下面给一个常见配置:

behavior: scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 - type: Pods value: 4 periodSeconds: 15 selectPolicy: Max scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 10 periodSeconds: 60

scaleUp里的两项策略分别表示:每次最多扩100%的当前副本数,同时最多增加4个Pod。selectPolicy: Max表示两个策略分别计算后取结果更大的一个执行。如果想把扩容调得更激进,可以加大value值;想更保守就调小。periodSeconds可以理解为一个统计窗口,HPA只会在这个时间窗口内执行一次对应的调整幅度,时间越短调整频率越快。

scaleDown部分我习惯把稳定窗口设得比默认更长一些,比如300秒甚至更长,同时限制单次缩容的百分比。这么做的原因很简单:缩容的风险比扩容大得多。Pod销毁后如果流量又回来了,新Pod启动还需要时间,那段时间服务容量是整体下降的,所以宁可让高副本状态多维持一阵,也别在流量波动时仓促缩掉。

4.3 和集群节点层面的自动扩缩容配合

HPA只负责调整工作负载的副本数,但节点数是有限的。如果集群节点总数不变,副本数增长到一定程度就会撞上资源天花板。比如maxReplicas设成20,但整个集群所有节点的CPU配额加起来只能容纳15个同类Pod,那当Deployment扩容到接近上限时,新Pod会长时间处于Pending状态,服务容量并没有真正提升。

要解决这种瓶颈,需要把HPA和集群节点层的自动扩缩容配合使用。节点层负责根据集群整体的资源申请量动态调整节点数量,出现大量Pending Pod时自动增加节点;HPA负责决定一个工作负载需要多少副本。两层控制器的目标不同,逻辑上是互补关系。

配合时有一个参数特别值得注意:maxReplicas不要设成一个脱离资源规划的空洞数字。如果节点层扩容需要几分钟,而业务流量高峰只能撑一两分钟,那扩容再多副本也救不回那一段损失。更务实的做法是先评估高峰时段的真实容量缺口,再给maxReplicas留少量余量,同时压测验证从触发扩容到新Pod真正就绪的总延迟时间。只有在延迟可接受的前提下,高maxReplicas才有实际意义。

4.4 业务自定义指标:解决"CPU不高但服务很慢"的问题

CPU和内存指标虽然通用,但它们只能反映第一层的资源消耗,不能反映业务真正的承载能力。举个例子,一个消息消费服务,每个Pod的CPU使用率只有30%,看起来负载很低,但消息积压却在持续上涨。这时候用CPU做HPA的触发条件,它永远不会扩容,因为"积压"这个真正的风险指标根本没进入计算范围。

要解决这类问题,就需要接入自定义指标。主流做法是在集群里部署一个自定义指标适配层,把业务侧上报的指标,比如处理延迟、队列深度、每秒请求数,暴露成标准指标接口,然后HPA在配置里直接引用这些指标名。例如服务上报了一个名叫mq_consume_lag的指标,表示消费积压数量,就可以在HPA里配置当平均积压数超过100时扩容,低于20时缩容。

加自定义指标的好处明显,但代价是要多维护一条指标链路。业务代码要主动上报指标,适配层要保持稳定运行,指标数据的延迟也要控制在一两个采集周期以内。否则可能出现一种奇怪的现象:业务指标已经涨到阈值了,适配层延迟很久才把数据传过来,HPA只能看到一个严重滞后的数字,等它反应过来,高峰已经过去一半。所以在业务指标链路没有彻底跑稳定之前,建议保留一个基础资源指标作为兜底。

5. 高频问题与排错实录

5.1 常见问题速查表

现象可能原因排查方向
HPA状态显示Unknown指标链路不通,或Pod没有资源请求先用资源查看命令确认指标接口可用,再检查工作负载的requests字段
副本数一直不变化指标计算滞后,或稳定窗口太长查看HPA状态里的指标值,确认目标值是否真的未超阈值
扩容很慢,高峰期响应超时单次扩容幅度太小,新Pod启动时间长调整behavior的scaleUp策略,优化Pod启动和就绪速度
缩容过快,流量一降副本就没了缩容稳定窗口太短参考流量历史波动频率,调大scaleDown稳定窗口
副本数到maxReplicas后仍扛不住工作节点资源不足,或max设置太低检查Pending Pod和节点容量,必要时联动节点层扩容
同时配置内存和CPU后行为异常HPA取的是结果较大的副本数,某个指标失真逐个指标分析期望值,剔除与业务关联度弱的指标

这张表是日常排查最常用的参考。但要注意,HPA的异常通常不是单点问题,很多现象是多因叠加。比如Unknown状态可能是指标链路故障加Pod没有requests同时存在,排查时按数据链路的顺序逐层确认最稳妥。

5.2 一次"Unknown"指标的完整排查过程

拿一个我实际处理过的模拟项目X举例。当时现象是HPA创建后状态一直显示Unknown,副本数始终是初始值,不管怎么压测都没反应。我第一步做的不是去查HPA配置,而是先看集群指标接口是否返回数据。

直接调用指标API,发现返回错误,说明指标聚合层根本没有注册。再进一步查看组件状态,发现指标采集相关服务没有正常运行。查看它所在节点的日志,发现是证书过期导致认证失败。把证书重新轮换后,指标接口恢复,资源查看命令能正常输出数据了,HPA状态也在一两个同步周期内从Unknown变成正常的目标值。

整个排查过程最大的经验是:排查HPA问题要沿着数据链路从前往后查,先确认最底层的数据有没有,再逐层往上。很多人一上来就改HPA的behavior配置,其实问题出在指标链路上,改再多也没用。指标链路通了之后,如果还有其他异常,再考虑是计算公式问题还是冷却窗口问题,按这个顺序做,效率高很多。

5.3 使用HPA过程中的几条重要提醒

最后分享几条实际运维中积累的提醒。第一条是关于手动扩缩容的冲突。HPA会根据指标持续调整副本数,如果你在它运行期间手动改了副本数,下一轮同步时HPA又会把副本数拉回按指标计算出的值。所以上了HPA的工作负载,扩容操作就尽量交给它,手动干预只应该出现在紧急事故处理时,而且事后要确认HPA没有被异常覆盖。

第二条是缩容时一定要配置优雅终止。每个Pod在收到删除信号到真正销毁之间有一个缓冲期,HPA缩容触发的是标准删除流程,如果你的服务没有处理旧连接的关闭,突然被销毁的Pod会给用户带来连接重置。建议在服务里实现优雅退出逻辑,并在工作负载配置里给终止宽限期设置足够的时间,确保存量请求被处理完。

第三条是不要盲目追求副本数量。扩容到10个副本不代表服务就能扛住10倍流量,节点资源总量、Pod调度分布、数据库和下游依赖的容量都会成为新的瓶颈。见过一个服务靠HPA把副本从3扩到15,结果下游数据库先被打挂,服务反而更不稳定。HPA只是弹性链路的一环,容量规划仍然要写在你的运维清单上。

我个人做完一套HPA配置后,最后一步通常是顺手开启事件采集。HPA每次扩缩容都会产生对应的事件记录,日志里有据可查。这件事做一次不费劲,但在事后复盘和排障时特别有用。想搞清楚"刚才到底是谁把副本数改了3个",看事件记录比翻监控快得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询