水平自动伸缩按需扩缩容
自动伸缩器根据当前用量与目标值推算出期望副本数,因此选对指标,往往比一开始把副本数定成多少更重要。 指标决定副本数 HPA 并不直接盯着负载看,它按周期采样你配置的指标,把实时读数与你设定的目标值比出比率,再由它推导出期望副本数:超过目标就扩容,不足就缩容,分母是当前 pod 的平均值。所以在它背后,选哪个指标、怎么聚合(按 pod 还是按集群)、以及取多长的窗口,才是真正决定「什么算负载」的东西。算出来的副本还会被 maxReplicas 与最小副本数夹住,并在默认同步周期里逐次校准。想让伸缩可预测,靠的是理解这条指标本身,而不是记住某一个常数。 扩容更激进、缩容更克制 缩容被刻意设计得不对称:控制器绝不会在利用率刚刚下降的那一瞬间动手,因为它要躲开瞬时尖峰,免得副本数像橡皮筋一样来回震荡。它会等一个稳定窗口(downscale-stabilization),守住 minReplicas 这条底线,然后才真正缩。扩容总是更快,因为排队那一刻用户已经感到了慢;缩容放慢,是因为留着多余副本远比让集群来回颠簸便宜。一套务实的参数是:下限留足真实尖峰的余量,目标值取一个能消化负载的比值,并把稳定窗口当成「我容忍多少负载噪声」的加注,而不是凭手感乱调的旋钮。 指标才是薄弱环节 整台控制器只会继承你喂给它的指标,指标怎样,伸缩器就怎样追。指标滞后于真实需求,控制器就总是在追赶慢半拍的数字;指标找一个与真正瓶颈相关性很差的代步量,伸缩器就是在缩错的东西。所以 HPA 最难的不是那几行 YAML,而是选哪个指标、定多少目标。给一条延迟敏感的服务配 CPU 目标、给 I/O 敏感的工作配内存目标,往往会让伸缩器朝反方向走。先搞清真正先饱和的是什么——队列深度、在途请求、连接占用——选择你可以在一个窗口内行动的平均值,并验证「当这个指标上升时,真实延迟确实跟着变坏」。到那时,HPA 才是伸缩你的流量的工具,而不是一台在废坐标上乱猜的机器。 坏掉的样子 指标一旧,伸缩就开始来回晃。管线延迟一两分钟时,控制器拿到的永远是上一波负载:尖峰过去,它才把副本加上去;加上去又被缩容稳定窗口按住,迟迟下不来。面板上不是跟随负载的曲线,而是一上一下的震荡。这时调目标值没有用,应当先修指标时延与采样窗口,再谈目标定多少。 指标直接缺失是更危险的一种。拉不到指标时,控制器会拒绝动作并报出 ScalingActive=False,这是保护而不是缺陷。最典型的故障是把“没有数据”当成“没有负载”:有人看到利用率掉零就手动缩容,结果把正在服务的容量一起删掉。正确姿势是把缺失当成未知,保持上一次决策,同时去查指标管线与事件流,而不是继续动副本数。 上下限会从另一个方向卡死控制循环。maxReplicas 当初为了省钱设得太小,真尖峰到来时期望副本被死死夹在上限,ScalingLimited=True 一直亮着,排队与超时却在上涨;minReplicas 设得太大,则会掩盖目标值估错的问题,因为底座托住了副本,你永远看不到需求下降后控制器敢不敢缩。最后不要让两个控制器拧同一个旋钮:HPA 管个数、VPA 改 request,请求值一动,利用率分母就变,两边互相追打;滚动更新太激进时,新 Pod 尚未接流量,老 Pod 仍在承压,平均值被稀释后误判为可缩容,副本走出锯齿。看到这种抖动,先把控制面分开排查,再调目标。 快速验证 把下面代码保存到临时目录的 hpa_loop.py,运行 python3 hpa_loop.py。它只用标准库模拟一条控制循环:期望副本等于当前副本数乘以当前指标除以目标值再向上取整,再用上下限夹住;指标缺失时保持上次决策;缩容必须等稳定窗口过期,否则先按住不动。脚本断言:热指标扩容、冷却期内冷指标不缩、窗口过期后可缩、超限被上下限夹住。 ...