HPA 自定义指标:请求数不变,为什么副本会减半?
Last modified:
一个上传服务始终收到约 35 RPS。只把每个请求体从 1 MiB 改成 512 KiB,HPA 却把副本从 4 个缩到 2 个。这正常吗?
请求数没有减少,副本为什么会减半?
答案取决于 HPA 实际读取的指标。本次实验不用 CPU,而是让应用导出请求数和已处理字节数,再分别用 RPS 与 B/s 控制副本。
本文是 HPA 实验系列的第二篇。第一篇先解释了一个 CPU 指标如何变成 Ready Pod;本文只关注应用吞吐指标。
自定义指标从哪里来
|
|
HPA 不会调用应用业务接口,也不会自动数 HTTP 请求。应用先定义指标口径,Prometheus 采集累计值,Adapter 把它转换并映射为 Kubernetes 指标。
本例的两个 Counter:
| Counter | 何时增加 | HPA 使用的速率 |
|---|---|---|
demo_http_requests_total |
/request 或 /upload 成功处理一次 |
http_requests_per_second |
demo_processed_bytes_total |
/upload 完整读取请求体后 |
processed_bytes_per_second |
Counter 只增不减,进程重启时可能归零。直接把累计值交给 HPA,会让历史流量一直影响副本数。因此先用 rate() 计算每秒速率:
|
|
较长窗口更平滑,但降载反映更慢;较短窗口更敏感,新 Pod 却可能还没有足够样本。Prometheus 的 rate 还会处理 Counter 重置和窗口边界外推。
为什么这里用 Pods,而不是 Object
Prometheus 通过 Pod 服务发现逐个抓取 app=web 的实例,并保留 namespace、pod 标签。Adapter 因此能返回每个 Pod 的独立指标:
|
|
HPA 使用 Pods + AverageValue:
|
|
Mi 只是 Kubernetes Quantity 的数值倍率;B/s 来自指标定义。10Mi 在这里表示 10485760 B/s,约等于 83.89 Mbit/s。
自定义指标不能直接使用 CPU 那样的 Utilization,因为它没有 requests.cpu 这样的分母。不同指标来源支持的目标类型如下:
metrics[].type |
可用的 target.type |
|---|---|
Resource / ContainerResource |
Utilization、AverageValue |
Pods |
AverageValue |
Object / External |
Value、AverageValue |
如果业务要表达「处理能力使用率 70%」,应先在应用或 Prometheus 中定义分母和比例,再把结果作为一个数值指标交给 HPA;HPA 不会替自定义指标寻找容量分母。
两个 HPA 不能同时控制一个 Deployment
请求速率实验使用的指标配置只有名称和目标不同:
|
|
实验顺序替换同一个 web HPA。不要创建两个 HPA 同时写同一个 Deployment:它们会相互覆盖期望副本数。如果确实要同时考虑 RPS 和带宽,应把两项写进同一个 metrics 数组,HPA 分别计算后选择较大的副本建议。
动手验证:保持 RPS,只改变请求体大小
实验文件固定在 k8sdev c48f140。先按第一篇的环境步骤创建集群,再执行:
|
|
每个场景同时检查 HPA 建议、Deployment 期望副本、Ready 副本、Prometheus 查询和 custom metrics API。首次带宽运行被 macOS 休眠打断,采样出现长时间空档;失败记录被保留,重跑使用 caffeinate -i 并加入连续采样检查。
结果:指标选择改变了答案
请求速率 HPA:
| 实测 RPS | Ready 副本 | 估算 |
|---|---|---|
| 35.08 | 4 | ceil(35.08 / 10) |
| 15.12 | 2 | ceil(15.12 / 10) |
| 0 | 1 | 受 minReplicas 限制 |
处理带宽 HPA:
| 请求条件 | 实测 RPS | 实测带宽 | Ready 副本 |
|---|---|---|---|
| 1 MiB × 35 RPS | 35.16 | 35.16 MiB/s | 4 |
| 512 KiB × 35 RPS | 34.88 | 17.44 MiB/s | 2 |
| 停止上传 | 0 | 0 | 1 |
第二阶段的计算是:
|
|
此时 RPS 仍约为 35。如果 HPA 依据请求速率,它会继续建议 4 个副本;实际缩到 2,证明本阶段参与决策的是处理带宽,而请求数只用于核对实验条件。
同一轮 custom metrics API 的返回值也保留了 Pod 维度。高带宽阶段的四个值为:
|
|
Kubernetes Quantity 中的 m 表示千分之一,所以第一项是 8640957.516 B/s,不是 86 亿 B/s。半请求体阶段只剩两个 Pod,API 返回 8682209280m 与 9604956160m,Prometheus 总值为 18287165.44 B/s。
原始证据:
这个实验没有证明什么
processed_bytes_per_second 统计的是程序完整读取的请求体,不是节点网卡吞吐,也不含 HTTP 头和 TCP 重传。程序读取后直接丢弃数据,没有写盘和业务计算。
生产指标必须回答两个问题:
- 增加 Pod 后,这个负载能否被更多实例分担?
- 服务饱和时,指标是否仍能表达尚未处理的压力?
共享网卡、数据库或下游已经饱和时,增加 Pod 可能没有收益。完成请求数也可能在过载时不再增长,这时应结合排队、在途请求或延迟指标。
回答开头的问题
请求数不变而副本减半,是因为 HPA 当时没有按请求数决策。请求体减半让应用实际处理的字节速率从 35.16 MiB/s 降到 17.44 MiB/s,每 Pod 目标仍是 10 MiB/s,所以建议从 4 变成 2。
导出一个指标,不等于它会参与扩缩容;只有写入当前 HPA spec.metrics 的指标才参与计算。 下一篇将把指标从「每 Pod 吞吐」换成「一个队列还有多少任务」,并处理缩容时的在途任务。