想象你是一家热门餐厅的经理。客流量一天起伏不定——午饭高峰排队到门外,下午两三点又冷冷清清。如果雇固定数量的服务员,高峰期忙不过来、顾客投诉;淡季又闲得发慌、工资照付。聪明的经理会怎么做?盯着门口排队的人群:队伍排到两条街外就打电话叫休班员工来帮忙,队伍稀疏了就安排一些人先回去。这就是“动态排班”。 在 Kubernetes 的世界里,Pod 就是餐厅的服务员,用户请求就是来来往往的客流。Kubernetes 的 HPA(Horizontal Pod Autoscaler,水平 Pod 自动伸缩器) 就是那位聪明的餐厅经理——它盯着 Pod 的繁忙程度(CPU 利用率、内存消耗、甚至 GPU 利用率),繁忙就自动增加 Pod 副本,空闲就自动减少副本。下面,我会带你从零理解 HPA 的指标来源、伸缩算法、冷却调优,并给出 LLM 推理服务弹性伸缩的完整配置方案。
掌握 HPA 配置,是 AI 工程化落地的基础功:它能让你部署的推理服务像潮汐般随流量自动涨落,既守住服务质量,又最大限度节省 GPU/CPU 算力成本。
目录
可视化演示
展示Kubernetes HPA控制器的指标采集、期望副本数计算和Pod伸缩调整全流程,以及GPU自定义指标与伸缩策略调优。切换模式查看不同环节。
本节配有交互动效(进入视口后自动加载,约 3KB)。
HPA控制循环 每15秒从指标API采集Pod资源使用率 根据目标利用率计算期望副本数,调用scale接口调整 desired = current × (metric / target) 控制循环持续运行使Pod数量匹配负载
1. HPA基础概念:餐厅经理的“动态排班”
想象你是一家热门餐厅的经理。你手下有一队服务员,客流高峰时忙得脚不沾地,客流低谷时闲得刷手机。你的直觉告诉你:不能固定排班,得看客流动态调人。但具体怎么调?你需要一个稳定的规则:门口排队超过 20 人就加一个人,排队少于 5 人就让一个人先下班。这个规则,就是 HPA 的雏形。
在 Kubernetes 中,运行 AI 推理服务的应用通常打包成一个或多个 Pod。Pod 就像餐厅的服务员,同一个 Deployment 里可以有多个完全一致的 Pod 副本,共同分担用户请求。而 HPA(Horizontal Pod Autoscaler,水平 Pod 自动伸缩器) 就是那位聪明的餐厅经理——它周期性检查 Pod 的运行指标,发现“忙不过来”就自动增加副本,“太清闲”就自动减少副本。整个过程全自动,无需人工干预。
HPA 的核心是一个内置控制器(kube-controller-manager 的一部分),它通过 Kubernetes 的 Metrics API 读取指标数据,再用一个简单的公式算出期望副本数。它管理的对象非常明确:Deployment、StatefulSet、ReplicaSet 这类带有“副本”概念的控制器。与之相对的是 VPA(Vertical Pod Autoscaler)——如果说 HPA 是“加人或减人”,VPA 就是“给每个人发更大份的活儿”(调大 CPU/内存资源配额)。
你看,HPA 的工作方式并不神秘——就是经理盯着客流(指标),按照既定规则算人头,然后淡定地调整排班表。但这位经理从不休假,7×24 小时盯着,一分钟都不松懈。
- desiredReplicas(期望副本数)这是 HPA 计算出的最终目标 Pod 数量,表示系统应该运行多少个副本才能满足当前负载。
- currentReplicas(当前副本数)现在正在运行的 Pod 副本数量,是计算的基准。
- currentMetric(当前指标值)最近采集到的指标值,如所有 Pod 的平均 CPU 利用率。
- desiredMetric(目标指标值)HPA 配置中设定的目标值,比如 50% CPU。
2. 指标来源:经理的“客流情报网”
餐厅经理要准确排班,光靠感觉不行,得有数据。看什么数据?门口排队人数是最直观的,但还不够——还要看入座率、翻台率、外卖订单量。不同的数据源从不同角度反映客流态势。HPA 也一样,它有一张“指标情报网”。
CPU 利用率是 HPA 最常用的指标,也是默认指标。它计算的是 Pod 实际使用的 CPU 相对于 requests(资源请求量)的百分比。为什么强调 requests?因为 Kubernetes 的调度器以 requests 为 Pod 分配节点资源,HPA 以它为分母计算利用率,两者口径一致才公平。
内存消耗是第二个内置指标。内存不像 CPU 那样可以被“压缩”,用满了就可能触发 OOM 被杀,所以内存指标对某些应用至关重要。不过 HPA 对内存偏高的响应是“扩容”,它无法通过限制来压低内存占用,使用时需要格外注意。
自定义指标是 HPA 的“万能接口”。如果 CPU 和内存都无法反映业务真实负载(比如队列积压、响应延迟、GPU 利用率),你可以通过 Kubernetes 的 custom.metrics.k8s.io API 暴露任意指标。对 LLM 推理服务来说,GPU 利用率就是典型场景——GPU 是稀缺且昂贵的资源,只有 GPU 利用率能真实反映推理服务的繁忙程度。
有了这些“情报”,HPA 就能像经理一样做到心中有数。但情报再多,最终都要落到排班计算上——我们下一节看看经理怎么用公式决定人手。
- metricUsed(实际使用量)容器当前实际消耗的资源量,如 0.8 个 CPU 核心。
- metricRequest(资源请求量)Pod 声明的基础资源需求,是 Kubernetes 调度的依据。
- × 100%(百分比换算)把比值换算成直观的百分比,便于和目标值比较。
3. 伸缩算法:从客流到人手的“排班公式”
经理拿到客流数据后,开始心算:现在有 20 桌客人在等位,每位服务员最多同时照顾 5 桌,那至少需要 4 个人。HPA 的伸缩算法也是这个道理,只不过它算得更精细,还有上下限保护,避免忙时无人可用、闲时人浮于事。
HPA 的完整算法公式融合了上下限约束:
其中 avgMetric 是所有 Pod 的平均指标值。公式的核心思想是比例缩放:平均负载越高,副本数越多,但结果必须落在最小副本数和最大副本数之间。
实际配置中,minReplicas 通常设为 1 或 2(保证基础可用性),maxReplicas 根据集群资源估算。比如你的 GPU 集群最多能同时跑 8 个推理实例,max 就设为 8。
除了上下限,HPA 还有单次调整限制,防止副本数“大起大落”。默认策略下,扩容一轮最多翻倍(每 15 秒评估一次),缩容一轮最多减半(每 5 分钟评估一次)。这就像经理不会因为瞬间涌入 100 个客人就立刻招 50 个新人——先让在班的人顶上,确认客流持续增长再逐步加人。
- avgMetric(平均指标值)总负载除以当前副本数,表示每个 Pod 的平均繁忙程度。
- 比例缩放用平均负载和目标负载的比值,按比例调整当前副本数。
- ceil 向上取整Pod 数量必须是整数,只要有小尾巴就多算一个。
- max(·, minReplicas) 下限保护负载再低,也不能低于配置的最小副本数,保证基础服务能力。
- min(·, maxReplicas) 上限保护负载再高,也不能超过配置的最大副本数,防止集群资源被耗尽。
4. 自定义指标适配器:给餐厅装“外卖订单统计器”
普通餐厅看排队和入座就够了,但如果你开的是“外卖为主”的网红店,线下客流根本不能反映真实订单量。你得装一个外卖平台的数据统计器。对 LLM 推理服务来说,CPU 和内存常常不能反映 GPU 的繁忙程度——GPU 利用率才是决定服务质量的关键指标,而它需要一个专门的“统计器”。
这个统计器就是 prometheus-adapter(Prometheus 适配器)。它的工作流程是:先由 Prometheus 监控系统采集集群中的各种指标(包括 GPU 利用率),再由 prometheus-adapter 把这些指标通过 Kubernetes 的 custom.metrics.k8s.io API 暴露给 HPA。有了这个适配器,HPA 就能读取“自定义指标”并做出伸缩决策了。
在 GPU 场景中,通常还需要 DCGM exporter(NVIDIA 官方 GPU 监控工具)来采集 GPU 利用率,把数据喂给 Prometheus。整个链路是:GPU → DCGM exporter → Prometheus → prometheus-adapter → HPA → Deployment。这条链上的每一环都缺一不可。
prometheus-adapter 的核心是配置发现规则(discovery rules),告诉它“哪些指标可以用、怎么把 Prometheus 的查询结果转换成 HPA 能读的格式”。常用的 GPU 指标表达式类似这样:
装了这套“外卖统计器”,HPA 就能在 GPU 跑满时及时扩容、GPU 空闲时及时缩容。对于大模型推理服务来说,这一步是成本优化的关键——毕竟 GPU 按小时计费,空转一秒都是钱。
- sum(rate(……))对 1 分钟窗口内的 GPU 利用率采样值求和,平滑瞬时波动。
- number of GPUs(GPU 总数)当前集群或命名空间中 GPU 卡的总数量,用于计算平均值。
- 整体平均将总利用率平均化,得到 HPA 可直接比较的指标值。
5. 冷却与调优:经理的“稳一稳”决策
新人经理最常犯的错是什么?看到门口排队人数刚多起来,立刻抓起电话叫所有休班员工回来;十分钟后队伍短了,又让刚到的员工回去。结果员工来回折腾,怨声载道,排班表乱成一团。成熟经理的做法是:先观察几分钟,确认客流是持续上升还是偶然波动,再动手。
HPA 也有同样的“冷静机制”,称为冷却时间(cooldown)。核心参数是 stabilizationWindowSeconds(稳定窗口期)——它决定 HPA 在改变副本数之前,需要持续观察多长时间。默认缩容的稳定窗口是 300 秒(5 分钟),意味着“负载偏低”这个信号要持续 5 分钟才会触发缩容;扩容的窗口通常较短或为 0,因为缺人是紧急事件,越快响应越好。
除了稳定窗口,Kubernetes 从 v1.18+ 还引入了速率限制,进一步防止副本数剧烈波动。默认的扩容策略是“每 15 秒最多翻倍”,缩容策略是“每 5 分钟最多减半”。也就是说,即使负载暴增,HPA 也不会一次从 2 个 Pod 跳到 100 个——它会先到 4,再到 8,每步留出缓冲,避免压垮集群调度器。
这些参数在 HPA 的 behavior 字段中可以灵活配置。对于 LLM 推理服务,建议扩容窗口短(快速响应突增请求)、缩容窗口长(避免 GPU 实例频繁启停)。调优的核心思想是:响应要快,变化要稳——像经验丰富的经理一样,看到趋势再行动。
- currentReplicas × 2(翻倍扩容)每轮评估最多把副本数翻一倍,避免瞬间创建大量 Pod 导致调度风暴。
- min(·, maxReplicas)翻倍后的结果不能超过配置的上限。
- scaleUpLimit(扩容上限)本轮扩容后允许的最大副本数。
6. 实战配置:把排班制度写成正式手册
餐厅经理有了成熟的排班规则,会把它写成正式的员工手册,让每个值班经理都能按章执行。HPA 也一样,Kubernetes 用声明式配置(YAML 文件)来定义伸缩规则——写清楚指标、目标值、副本上下限、冷却行为,然后提交给集群,控制器自动执行。
一个完整的 HPA 配置(基于 autoscaling/v2 API)长这样:
这个配置表示:管理 llm-inference Deployment,副本数保持在 2 到 8 之间;当 CPU 平均利用率超过 60% 或 GPU 利用率超过 70 时扩容;扩容观察 30 秒,缩容观察 300 秒。
配置好之后,用这些命令验证:
get 看当前状态(副本数、目标/当前指标),describe 看详细事件和条件。排查问题时,重点关注 Events 段——HPA 会把“无法读取指标”“超出最大副本数”等异常记录在这里。另外,用 kubectl top pods 确认指标采集是否正常,一旦发现 unknown 状态,多半是 metrics-server 或 Prometheus 适配器没就绪。
YAML 手册写得越清晰,HPA 执行得越稳定。把排班规则固化下来之后,你的推理服务就真正实现了“随客流自动调人”的弹性能力。
apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: llm-inference-hpaspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-inference minReplicas: 2 maxReplicas: 8 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 - type: Pods pods: metric: name: gpu_utilization target: type: AverageValue averageValue: "70" behavior: scaleUp: stabilizationWindowSeconds: 30 scaleDown: stabilizationWindowSeconds: 300
kubectl get hpakubectl describe hpa llm-inference-hpa
AI 应用场景
LLM 推理服务的 GPU 弹性伸缩
阶段:模型部署与运维
解决的问题:LLM 推理服务对 GPU 资源依赖极大,手动扩缩容无法应对突发的用户请求波峰。传统做法是固定数量的 GPU 副本,导致低峰期 GPU 闲置、高峰期请求排队超时,用户体验差且成本高昂。
具体案例:某 AI 公司部署了基于 vLLM 的 Llama-3 推理服务。白天办公时段,员工频繁调用代码补全和文档摘要功能,GPU 利用率飙升到 90%+;夜间和周末几乎无人使用。通过配置 HPA + DCGM exporter + prometheus-adapter,以 GPU 利用率 70% 为伸缩目标,副本数在 2-8 之间自动调整。高峰期自动扩容保证响应时间,低峰期自动缩容节省 60% 以上的 GPU 成本。
电商大促的 Web 服务自动伸缩
阶段:服务部署与弹性运维
解决的问题:大促期间流量是平时的 10 倍,固定副本数要么浪费资源,要么扛不住流量导致系统崩溃。
具体案例:电商平台在大促前配置 HPA,以 CPU 利用率 50% 和自定义的“订单队列长度”为指标,min=10、max=100,扩容窗口 15 秒、缩容窗口 600 秒。大促开始时订单量暴涨,HPA 在几分钟内自动扩容到 80+ 副本;大促结束流量回落,副本数缓慢缩回 10,避免了人工盯屏和半夜起床扩容的痛苦。
Python 演示
import mathimport randomimport time def hpa_desired_replicas(current_replicas, total_load, target_load): """HPA 核心算法:计算期望副本数""" if current_replicas == 0: return 1 avg_load = total_load / current_replicas ratio = avg_load / target_load return math.ceil(current_replicas * ratio) def clamp_replicas(desired, min_replicas, max_replicas): """将期望副本数限制在 min/max 范围内""" return max(min_replicas, min(max_replicas, desired)) def simulate_hpa(seconds=60, min_replicas=2, max_replicas=8, target_load=50): """模拟负载变化下 HPA 的扩缩容全过程""" replicas = min_replicas cooldown = 0 print("时间(s) | 总负载 | 平均负载 | 期望副本 | 实际副本") print("-" * 60) for t in range(seconds): total_load = 30 + 25 * math.sin(t * 0.1) + random.uniform(-5, 5) total_load = max(5, total_load) if cooldown > 0: cooldown -= 1 desired = replicas else: desired = hpa_desired_replicas(replicas, total_load, target_load) desired = clamp_replicas(desired, min_replicas, max_replicas) if desired != replicas: cooldown = 5 avg_load = total_load / replicas if t % 5 == 0: print(f"{t:7d} | {total_load:6.1f} | {avg_load:8.1f} | {desired:6d} | {replicas:6d}") replicas = desired time.sleep(0.05) return replicas if __name__ == "__main__": simulate_hpa(seconds=80)
avg_load = total_load / current_replicas计算每个 Pod 的平均负载,这是 HPA 判断“忙不忙”的依据。ratio = avg_load / target_load平均负载与目标值的比值,大于 1 说明需要扩容,小于 1 说明可以缩容。return math.ceil(current_replicas * ratio)按比例缩放并向上取整,得到期望副本数。clamp_replicas(desired, min_replicas, max_replicas)把期望副本数限制在 min 和 max 之间,对应 HPA 的上下限保护。cooldown = 5每次调整副本数后进入 5 个时间步的冷却期,模拟 HPA 的稳定窗口。if t % 5 == 0:每 5 个时间步打印一次状态,方便观察整个过程。
常见误区
误区我把 HPA 的 target 设成 50,那 HPA 会保证 CPU 永远在 50% 以下,超过就会立刻扩容。
正解target 是“期望平均值”,不是硬性上限。HPA 周期性检查,发现平均负载超过目标就按比例扩容,但扩容需要时间,Pod 启动也需要时间,这期间 CPU 可能持续高负载。更准确的理解是:target 是“希望维持的平均水平”,而不是“防火墙”。
为何常见:把“目标值”误当成“阈值阈值”,就像把空调 26 度理解成“室温永远不会超过 26 度”一样。
误区HPA 配置里写了 GPU 指标,集群就能直接采集,kubectl get hpa 就能看到利用率。
正解HPA 读取自定义指标前,必须先部署 Prometheus 监控系统采集指标,再安装 prometheus-adapter 把指标通过 custom.metrics.k8s.io API 暴露给 HPA。缺少任何一环,HPA 都会显示 unknown 状态。
为何常见:HPA 的 YAML 配置看起来是“声明式”的,让人觉得写进去就能生效,忽略了底层复杂的数据链路。
误区冷却时间太长导致响应慢,我把它设成 0 或几秒,这样扩容更快,服务更稳。
正解冷却时间过短会导致“抖动”——负载轻微波动就触发扩容/缩容,Pod 频繁创建删除,不仅浪费资源,还可能影响服务稳定性。缩容窗口尤其不宜过短,建议 300 秒起步;扩容窗口可以短一些,但要留有缓冲。
为何常见:直觉上“越快越好”,但忽略了频繁伸缩带来的稳定性代价和调度开销。
误区流量低的时候让 HPA 缩容到 0,彻底不占资源,省钱省心。
正解默认情况下 HPA 的 minReplicas 最小只能设为 1,不支持缩到 0。要支持缩到 0,需要配合 KEDA(Kubernetes Event-driven Autoscaling)等工具。即使技术上可行,Pod 冷启动也有延迟,缩到 0 会导致请求来临时长时间等待。
为何常见:看到“弹性伸缩”就想到“用多少启动多少”,忽略了 Pod 启动时间和 Kubernetes 调度器的实际机制。
误区我在 Pod 里设置了 CPU limits,HPA 的利用率 = 实际使用量 / limits。
正解HPA 计算 CPU 利用率使用的是 requests 而不是 limits。比如 Pod 请求 500m CPU,实际用了 300m,利用率是 60%。limits 是强制上限,用于限制容器不能超过某个用量,与 HPA 的伸缩决策无关。
为何常见:limits 和 requests 在 YAML 里是紧挨着的字段,初学者容易混淆它们各自的作用。
面试题
Q1. HPA 的中文全称是?
- A.水平Pod自动伸缩← 正确答案
- B.垂直Pod自动伸缩
- C.水平Pod自动修复
- D.集群节点自动伸缩
答案:A。HPA 全称 Horizontal Pod Autoscaler,即水平 Pod 自动伸缩器。“水平”指增加/减少 Pod 副本数量;“垂直”(VPA)才是调整单个 Pod 的资源配额。自动修复是控制器和探针的工作,集群节点伸缩属于 Cluster Autoscaler 的范畴。
Q2. HPA 计算期望副本数使用哪个公式?
- A.向上取整:当前副本×负载/目标← 正确答案
- B.向下取整:当前副本×负载/目标
- C.向上取整:当前副本×目标/负载
- D.四舍五入:当前副本×负载/目标
答案:A。HPA 的核心公式是 desiredReplicas = ceil(currentReplicas × currentMetric / desiredMetric)。负载超过目标时,比例大于 1,副本数增加;负载低于目标时,比例小于 1,副本数减少。向上取整是为了保证有小数余量时多预备一个 Pod,避免反复伸缩。
Q3. 以下哪个是 HPA 管理的主要对象?
- A.Deployment 控制器← 正确答案
- B.Node 节点资源
- C.Service 服务对象
- D.Namespace 命名空间
答案:A。HPA 通过 scaleTargetRef 指定管理对象,通常是 Deployment 或 StatefulSet 这类拥有副本概念的控制器。Node 资源由 Cluster Autoscaler 管理,Service 负责网络负载均衡,Namespace 用于资源隔离,它们都不是 HPA 的伸缩目标。
Q4. HPA 计算 CPU 利用率时的分母是什么?
- A.Pod 的 requests 资源← 正确答案
- B.Pod 的 limits 资源
- C.Pod 的运行时长
- D.Pod 的镜像大小
答案:A。HPA 的 CPU 利用率 = 实际使用量 / requests。Kubernetes 调度器以 requests 为 Pod 分配节点资源,HPA 以它作分母口径一致。limits 是强制上限,不影响伸缩决策。运行时长和镜像大小与资源利用率无关。
Q5. stabilizationWindowSeconds 的主要作用是?
- A.控制扩容单次最大幅度
- B.控制指标采集间隔时长
- C.控制缩容前的观察期← 正确答案
- D.控制 Pod 启动的超时时间
答案:C。stabilizationWindowSeconds 是“稳定窗口期”,它要求指标信号持续一段时间后才执行伸缩。默认缩容窗口 300 秒,避免瞬时负载回落触发缩容。扩容窗口通常较短,因为缺人是紧急事件。它控制的是“观察多久”,而不是“一次变多少”或“多久采一次”。
Q6. HPA 的 minReplicas=1、maxReplicas=8,当负载持续为 0 时副本数会?
- A.副本数自动降为 0
- B.副本数最低保持一← 正确答案
- C.副本数降到负一
- D.副本数持续振荡
答案:B。HPA 的公式结果受 minReplicas 下限保护,即使负载为 0,副本数也不会低于 minReplicas(默认最小为 1)。要支持缩到 0 需要 KEDA 等工具。持续为 0 的负载是稳定信号,不会引发振荡,所以 D 也不对。
Q7. 以下哪个指标不需要自定义适配器就能被 HPA 读取?
- A.GPU 利用率指标
- B.内存利用率指标← 正确答案
- C.请求延迟指标
- D.队列长度指标
答案:B。CPU 和内存利用率是 HPA 的内置指标,通过 metrics-server 即可获取,无需额外适配器。GPU 利用率、请求延迟、队列长度都属于自定义指标,需要通过 prometheus-adapter 等组件暴露给 HPA。
Q8. 关于 GPU 指标用于 HPA 伸缩,以下哪些说法正确?
- A.需要 DCGM exporter 采集← 正确答案
- B.需要 prometheus-adapter 适配← 正确答案
- C.依赖自定义指标 API← 正确答案
- D.无需额外组件直接可用
答案:ABC。GPU 利用率不是 Kubernetes 内置指标。完整链路是 DCGM exporter 采集 GPU 数据 → Prometheus 存储 → prometheus-adapter 通过 custom.metrics.k8s.io API 暴露给 HPA。D 说法错误,没有适配器 HPA 无法读取 GPU 指标。
Q9. 关于 HPA 冷却调优,以下哪些说法正确?
- A.缩容窗口默认约 5 分钟← 正确答案
- B.扩容窗口可以配置较短← 正确答案
- C.冷却过短容易导致抖动← 正确答案
- D.冷却越长响应速度越快
答案:ABC。默认缩容稳定窗口是 300 秒(5 分钟),扩容窗口通常较短,这是为了快速响应负载上升、谨慎处理负载下降。冷却过短会导致 Pod 频繁创建删除的抖动现象。冷却越长,HPA 对负载变化的响应越慢,D 说反了。
Q10. HPA 相比手动伸缩的优势有哪些?
- A.自动响应负载波动← 正确答案
- B.降低运维人工成本← 正确答案
- C.提升资源利用率← 正确答案
- D.无需配置任何监控
答案:ABC。HPA 自动感知负载并调整副本数,省去了人工盯屏和手动 kubectl scale 的运维负担,让资源随流量动态匹配,提升利用率。但 D 错误——HPA 依赖监控指标,需要配置 metrics-server 或自定义指标链路才能工作。