HPA 到底怎么决定副本数?为什么负载一下去它不立刻缩?高一点点就不动是 bug 吗?这篇用 busybox 死循环和 Python 占空比负载在真集群里把公式、稳定窗口、容忍区和缺少 requests 的报错都实际走一遍。
HPA app(目标利用率 50%,min 1、max 6)创建后,(当前副本→期望副本,利用率)的变化:(副本 1→0, 利用率 None%)@0s → (副本 1→4, 利用率 200%)@31s → (副本 4→4, 利用率 201%)@46s → (副本 4→6, 利用率 200%)@77s → (副本 6→6, 利用率 200%)@92s
事件 SuccessfulRescale:New size: 4; reason: cpu resource utilization (percentage of request) above target
事件 SuccessfulRescale:New size: 6; reason: cpu resource utilization (percentage of request) above target
算式:第一次决定:当前 1 个副本、利用率 200%、目标 50% → ceil(1 × 200 ÷ 50) = ceil(4.00) = 4(实测期望副本 4);第二次:当前 4 个、利用率仍是 200% → ceil(4 × 200 ÷ 50) = 16,超过 maxReplicas=6,所以只到 6HPA 每 15 秒从 metrics API 取一次 Pod 的 CPU 用量,算 desired = ceil(当前副本数 × 当前值 ÷ 目标值)。每个 Pod 总是用满 200m 的 limit、requests 是 100m,所以利用率恒为 200%、目标 50%:第一次有数据(创建后 31 秒)是 ceil(1×200÷50)=4,副本数 1→4。新副本利用率还是 200%,下一次 ceil(4×200÷50)=16,超过 maxReplicas=6,只到 6。
注意“利用率”是用量占 requests 的百分比,不是占 limits 的。
给所有 Pod 创建 /tmp/stop(死循环改成 sleep,CPU 降到接近 0)之后,(当前副本→期望副本,利用率)的变化:(副本 6→6, 利用率 200%)@0s → (副本 6→6, 利用率 195%)@12s → (副本 6→6, 利用率 80%)@28s → (副本 6→6, 利用率 12%)@43s → (副本 6→2, 利用率 1%)@58s → (副本 2→1, 利用率 1%)@73s → (副本 1→1, 利用率 1%)@89s
事件 SuccessfulRescale:New size: 2; reason: All metrics below target
事件 SuccessfulRescale:New size: 1; reason: All metrics below target
从负载消失到缩到 1 个副本用了约 89 秒(指标采集周期 15 秒 + HPA 同步周期 15 秒 + 稳定窗口 30 秒)给所有 Pod 创建 /tmp/stop 让负载消失,利用率 200% → 195% → 80% → 12% → 1% 逐步落下(metrics 本身有采集周期,不会瞬间反映)。副本数 6 一直不动到 58 秒才降到 2,73 秒才到 1,整个过程约 89 秒。
利用率 12% 时的期望副本数是 ceil(6×12÷50)=2,与 6→2 一致:缩容取稳定窗口(实验里设成 30 秒;默认是 300 秒)内最大的建议,窗口里还留着之前较高的建议,所以先落到 2,窗口滑过之后才到 1。扩容没有这种等待,所以扩得快、缩得慢。
tol-in(利用率约 52%,比目标 50% 高 4%(比值 1.04,在默认 ±10% 的容忍区内)):100 秒后 当前副本 2 期望 2 利用率 52%;SuccessfulRescale 事件:(没有) tol-out(利用率约 70%,比目标高 40%(比值 1.4)):100 秒后 当前副本 5 期望 5 利用率 70%;SuccessfulRescale 事件:['New size: 3; reason: cpu resource utilization (percentage of request) above target', 'New size: 5; reason: cpu resource utilization (percentage of request) above target']
两个 2 副本、requests 100m 的 Deployment,占空比负载把利用率控制在 52% 和 70%。tol-in 的比值是 1.04:按公式 ceil(2×52÷50)=3 本应扩容,100 秒里却一直是 2 个副本、没有任何 SuccessfulRescale 事件——比值落在默认 ±10% 的容忍区(0.9~1.1)内,HPA 不动作。tol-out 的比值是 1.4:先 2→3,再 ceil(3×70÷50)=5 到 5。
所以“利用率比目标高一点点却没扩”不是故障,是容忍区在避免抖动。
Deployment 没有 requests.cpu:HPA conditions:{"AbleToScale": ["True", "SucceededGetScale", "the HPA controller was able to get the target's current scale"], "ScalingActive": ["False", "FailedGetResourceMetric", "the HPA was unable to compute the replica count: failed to get cpu utilization: missing request for cpu in container c of Pod noreq-75647b8894-pjwf6"]}
kubectl get hpa:noreq Deployment/noreq cpu: <unknown>/50% 1 4 1 31sDeployment 没有 requests.cpu 时,HPA 的 ScalingActive 条件是 False,reason 是 FailedGetResourceMetric,message 是 failed to get cpu utilization: missing request for cpu in container c of Pod …;kubectl get hpa 的 TARGETS 一列显示 cpu: <unknown>/50%,副本数停在 1。
排查 HPA 不工作,先看 TARGETS 是不是 <unknown>,再看 kubectl describe hpa 里的 Conditions。