想象你开了一家奶茶店,生意时好时坏:工作日中午排起长队,凌晨却门可罗雀。如果固定雇佣 10 个店员,闲时发工资亏本,忙时招待不周;如果只雇 2 个,高峰期顾客直接流失。聪明的店长会怎么办?——看排队人数动态调班:人多了临时加人,人少了劝人早回。AI 推理服务正是这样一个“奶茶店”:用户请求忽多忽少,服务器(店员)每台都价格不菲。推理服务扩缩容,就是给这座“AI 奶茶店”装上自动排班系统:用指标(排队人数)感知流量,用规则自动增减机器(店员),同时想办法让新店员“秒上手”(冷启动优化)、让每个店员变成多面手(资源复用),最后把整套经验写成手册(MLOps 工程化),让系统自己运转。这篇内容,带你从零看懂这套“弹性应对流量波动”的完整方法。
掌握推理服务扩缩容,是你从“会部署模型”进阶到“能设计高可用、低成本推理系统”的关键一步,也是面试和工程实战中的高频考点。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
扩缩容本质:奶茶店的「编外军团」
小李的奶茶店开业第一天,他信心满满地雇了 6 个店员。可一周下来,账单让他傻眼:工作日下午三点,6 个人闲到比赛擦杯子;周五晚高峰,6 个人忙得团团转,顾客还是跑了一半。问题出在哪?他雇的人是“固定编制”,而客流是“流动的河”。推理服务也一样:用户请求像潮水忽涨忽落,服务器是昂贵的“店员”,全雇最稳妥但太烧钱,全退不现实。扩缩容的本质,就是让服务规模随着流量自动伸缩——人多了加人,人少了减人。
在技术世界里,这种伸缩有两种基本姿势。水平扩缩容指增加或减少服务器的实例数量,就像奶茶店多开几个收银窗口;垂直扩缩容指升级单台服务器的 CPU 或内存,就像让一个店员手脚更快。AI 推理服务偏爱水平扩缩容,因为 GPU 服务器贵到离谱,与其堆一台超级机器,不如按需拉来普通机器协同干活。而“按需”的前提,是能算清楚到底需要多少人:
回到奶茶店:小李现在每天打烊前看一眼预报的客流,把需要的店员数算个七七八八。固定编制是安心,弹性扩缩容是省钱,而省下来的每一分钱,都直接变成了利润。
- 读分子 QQ 是当前时段到达服务的每秒请求数(QPS,Queries Per Second)。它像奶茶店某一刻柜台前的排队人数,动态变化。
- 读分母 TT 是单个实例的吞吐能力,即一个服务器实例每秒能处理多少请求。它像每个店员每分钟能做几杯奶茶,由模型复杂度、显卡型号等决定。
- 向上取整⌈ ⌉ 符号表示“只要有剩余,就多算一个”。排队 91 人、单个店员每小时做 40 杯时,你需要 3 个人而不是 2.275 个人,因为人不能切半。
指标驱动策略:柜台前的「排队台账」
有了调班的想法,小李又要面临新问题:什么时候加人?凭感觉吗?当然不行——感觉会骗人,数据不会。他在柜台放了本“排队台账”,每五分钟记一次:队列长度、顾客等待时间、收银员空闲情况。这就是指标驱动:扩缩容不是拍脑袋,而是根据监控指标做决策。
推理服务常见的指标有四类。吞吐量:每秒处理请求数(QPS),相当于柜台每小时卖出多少杯。响应时延:一个请求从发出到返回的耗时,通常看 P99(99% 的请求都在这个时间以内),相当于顾客从排队到拿到奶茶的等待时间。队列深度:积压在服务入口处等待处理的请求数量,相当于柜台前排多长的队。资源利用率:GPU、CPU、内存的使用比例,相当于店员忙碌的百分比。
小李的“排队台账”越记越厚,他也越来越有底气:中午十二点一过,队列长度超过 15 人,他一个电话就叫来两个兼职;下午三点队列清零,他提前让兼职下班。指标不会说谎,而聪明的老板,只是比数据早一步行动。不过要注意:指标本身有滞后性,看到队伍排到门外才加人,那几杯被流失的顾客已经回不来了——这就要讲到下一节“自动扩缩容”如何提前行动。
- 吞吐量需求将当前 QPS 除以单实例吞吐量并向上取整,得到满足现有请求量所需的最少实例数。
- 时延需求将当前排队长度除以目标队列深度(比如“可接受的最长长度”),同样向上取整。排队 200 个请求、目标 100 个以内,就需要 2 倍的实例来消化。
- 取最大值实际需要的实例数取两者的较大者,因为短板效应——任何一个指标超标,服务质量都会出问题。
自动扩缩容机制:值班经理的「调度铁律」
小李不可能 24 小时盯着台账,于是他招了一位“值班经理”——一台监控电脑。规则很简单:队列超过 15 人,自动呼叫兼职;低于 5 人持续十分钟以上,自动让兼职下班。这就是自动扩缩容:把调班决策固化成规则,让系统自己执行。
工程上常见的实现是 Kubernetes 的 HPA(Horizontal Pod Autoscaler,水平 Pod 自动伸缩器)。它周期性检查指标,然后按公式计算目标副本数:
不过,光会算数还不够,自动扩缩容还有两个“防抖”法宝。冷却时间:每次扩缩容后暂停一段时间再判断,避免指标轻微抖动时系统来回折腾——就像值班经理刚叫来兼职,立刻又让人家回去,咖啡机都没热。伸缩步长:限制单次最多增减多少个实例,防止流量突刺时一下子把集群撑爆。更进一步,还有预测式扩缩容:基于历史流量曲线,在高峰到来前就提前加人,比如小李根据“周五晚上必爆单”的经验提前排班。冷却防抖、步长防爆、预测防迟,三件套齐活,这个“值班经理”才真正靠谱。
- 指标比值把当前观测到的指标(如平均 GPU 利用率、平均 QPS)除以你设定的目标值,算出“超标了多少倍”。
- 乘当前副本数用这个倍数乘上当前实例数量,得到期望的实例数量。指标超标 3 倍,当前 2 个实例,就需要 6 个。
- 向上取整并限幅结果向上取整,同时被配置的最小/最大实例数夹住,防止系统把实例加到失控。
冷启动优化:兼职店员的「闪电培训」
值班经理叫来了兼职,新问题又出现了:兼职小哥第一次来,找不到糖浆摆在哪,封口机不会用,磨磨蹭蹭五分钟才能上手。对推理服务来说,新实例刚启动时同样要经历一段“手忙脚乱”的冷启动:拉取容器镜像、加载模型权重到显存、分配 GPU 内存、初始化缓存……这段时间新实例无法服务任何请求,流量来了也接不住。冷启动慢,扩容就等于白扩——远水救不了近火。
冷启动为什么这么慢?主要瓶颈在模型加载:一个大模型的权重文件动辄几个 GB,从磁盘读进显存就需要几十秒甚至几分钟。此外还有缓存预热:如果在线服务依赖特征缓存、索引等,新实例的缓存是空的,前几百个请求都要“冷碰冷”,时延高得离谱。由此,冷启动耗时可以拆成三段:
针对这三段,业界有成熟的优化套路。常驻副本预热:至少保留 1-2 个最小副本常驻,让池子里永远有“已经热好的店员”;模型预加载:容器启动脚本里把模型提前加载进显存,而不是等第一个请求来了再加载;请求预热:新实例启动后先灌入少量模拟请求“暖机”,把缓存热起来再接真实流量。另外,把几个 10GB 的模型打包进镜像会导致启动极慢,用分层镜像/懒加载技术按需拉取数据块,也能把加载时间压到原先的十分之一。小李的启示是:真正的高手不等兼职来了才开始教,而是平时就把“SOP 卡片”贴在墙上——新店员 30 秒上岗,新实例 1 分钟接客。
- 加载耗时指拉取镜像和把模型权重读入显存/内存的耗时,通常是最大的时间开销。
- 预热耗时新实例处理第一批请求构建缓存、建立连接池的耗时,相当于新店员熟悉环境的过渡期。
- 就绪耗时Kubernetes 健康检查确认实例可以接受流量所需的时间,像店员穿好工服、站在柜台前喊“可以开工了”。
资源利用率管理:一杯原叶茶,三种卖法
扩缩容解决了“用多少人”,但另一个问题浮出水面:资源利用率太低。小李发现,下午三点店里 6 个店员,平均只有 2 个人在忙,剩下的人不是在擦桌子就是在发呆。服务器也一样:显卡贵得要命,推理时却往往只用了三成算力。资源利用率管理,就是让每个实例的算力尽量“物尽其用”。
第一个法宝是装箱调度。想象一个集装箱:里面要放尽可能多、形状各异的货物。调度器把多个小型推理任务塞进同一张 GPU 卡,让显存和算力都被填满,而不是每个任务独占一张卡还空着一大半。第二个法宝是超卖配置:给多个容器申请的内存总和超过物理资源,赌它们不会同时用满——就像一家奶茶店排班时,知道早班和晚班的人不会同时到店,共用一个储物柜也够用。不过超卖是把双刃剑,风险与收益并存,需要配合监控和驱逐策略。这两个手段共同追求一个指标:
那么,省下的钱到底有多少?假设每月推理成本 10 万元,平均利用率从 30% 提升到 70%,可近似认为节省约 40% 的重复成本——每月省下 4 万元。计算方法可以简化为:
其中 $U_{\text{new}} - U_{\text{old}}$ 是利用率提升量,$\text{Cost}_{\text{total}}$ 是当前总成本。这比喻的妙处在于:小李那杯原叶茶,正常卖 12 元一杯,他把茶叶做成茶包、把茶汤做成茶冻,一份原料卖了三种形态,单杯成本降了一半。资源管理的本质不是克扣“店员”,而是让每份算力都干满活、不摸鱼。
- 分子实际使用服务运行时真实占用的算力或显存,像店员真正做奶茶的工时。
- 分母分配资源你为服务预留的实例规格总和,像你为每个排班店员付的全天工资。
- 比值解读利用率接近 1 说明资源被高效使用;远小于 1 说明存在浪费,要么缩容,要么让多个任务共享。
MLOps工程化:从「嘴上经验」到《门店手册》
至此,小李的奶茶店已经实现了动态排班、监控指标、冷启动培训、资源复用,但他突然意识到一个大问题:这一切都装在自己脑子里。哪天他休假,店长会不会又把店开回“固定 6 人”的老路?于是,小李决定把这一年踩过的坑写成一本《门店弹性运营手册》,这就是MLOps 工程化——把扩缩容的经验、规则、流程沉淀成可重复执行、可验证的工程体系。
一本好的手册,至少包含四章。压测验证:大促前,小李提前三天用模拟客流压榨店铺,看它到底能扛多少单——对应工程上的压测工具(如 Locust、K6),找出系统的容量上限和拐点。容量规划:根据历史订单曲线和天气预报,算出高峰峰值和预留余量——对应容量规划中的“峰值 + 缓冲”策略。灰度发布:新排班规则先在某一家分店试运行,跑通了再推广到所有分店——对应扩缩容参数变更的灰度放量。故障演练:定期人为制造“客流爆掉”场景,验证值班经理是否真能扛住——对应混沌工程与故障注入。其中,“预留多少余量”可以用一个简单公式估算:
小李的手册很快被 10 家分店采用。分店店长不再依赖某个“天才店长”的感觉,而是看着手册、对着仪表盘做出同样高质量的决策——这正是 MLOps 的终极目标:把弹性能力从个人经验升级为组织能力。至此,从“为什么需要扩缩容”到“怎么落地成体系”,一整条链路就走通了。
- 峰值与均值峰值流量是一天中最高的一刻,平均流量是全天均值。两者差值代表波动幅度。
- 归一化差值除以均值,得到“波动有多剧烈”的相对值。波动越大,越需要扩缩容和余量。
- 工程含义若峰值是均值的 2 倍,预留余量至少 100%。这个数字直接决定你的扩容上限设置、成本预算和 SLA 承诺。
AI 应用场景
大模型 API 服务的流量洪峰
阶段:模型部署与在线推理
解决的问题:大模型 API 的用户请求昼夜波动极大——白天办公时间 QPS 是深夜的 20 倍。传统固定部署 100 个 GPU 实例,深夜 95% 空转,每月浪费数十万元;部署太少又导致白天响应超时。通过指标驱动 + 自动扩缩容,系统根据实时 QPS 和队列深度动态调整实例数,配合预测式扩缩容在早高峰前提前扩容。
具体案例:某 LLM API 服务商周一早晨 9 点流量突增,HPA 检测到平均时延从 300ms 升到 800ms,冷却期结束后自动从 20 个实例扩到 80 个;晚 10 点流量回落后缩回 15 个,全天成本降低约 45%。新扩实例通过模型预加载将冷启动时间从 90 秒压缩到 3 秒,用户几乎无感知。
电商大促推荐模型应急扩容
阶段:弹性伸缩策略设计与容量规划
解决的问题:双 11 零点流量瞬间冲高,反应式扩缩容“来不及”——等监控发现超标再扩容,第一批用户已经流失。通过容量规划预留余量 + 故障演练 + 压测验证的组合,提前确定扩容上限和步长。
具体案例:某电商平台在大促前两周用 K6 压测模型服务,发现 8 个实例能扛住 80% 峰值流量但 P99 超出 SLA。于是将 HPA 扩容上限设为 32 个实例、单次扩容步长 4 个,并在零点前 30 分钟用预测式扩缩容提前扩到 24 个实例。大促当天 P99 稳定在 180ms 以内,资源成本只比平时高 60%。
视频推荐服务的分时段调度
阶段:资源利用率管理与成本优化
解决的问题:视频推荐模型在晚间 8-11 点是高峰,其余时段利用率不足 20%。单独为高峰保留整张 GPU 卡太浪费,通过装箱调度将多个非峰值任务共享同一张卡,高峰时段再动态腾挪。
具体案例:某短视频平台把短视频推荐、直播审核、图文兴趣模型三路推理任务装箱到同一批 A10 GPU 上,利用超卖配置让三路任务错峰使用显存,整体 GPU 利用率从 28% 提升到 67%,月成本节省 38 万元。高峰时段通过优先级调度自动驱逐低优任务,保证核心推荐服务的 P99 不受影响。
Python 演示
class AutoScaler: def __init__(self, min_replicas=1, max_replicas=10, target_qps=50, cool_down=3): self.min_replicas = min_replicas self.max_replicas = max_replicas self.target_qps = target_qps self.cool_down = cool_down self.gc_count = 0 def decide(self, current_replicas, qps): # 向上取整计算期望实例数 desired = max(self.min_replicas, min( self.max_replicas, (qps + self.target_qps - 1) // self.target_qps )) if desired == current_replicas: return current_replicas, "保持" if self.gc_count > 0: self.gc_count -= 1 return current_replicas, "冷却中" self.gc_count = self.cool_down if desired > current_replicas: return desired, "扩容" return desired, "缩容" def simulate(): traffic = [40, 45, 60, 90, 120, 110, 80, 55, 35, 70, 130, 100] scaler = AutoScaler() replicas = 1 print("时间 QPS 实例 操作") for t, q in enumerate(traffic): replicas, action = scaler.decide(replicas, q) print(f"{t:>2} {q:>4} {replicas:>4} {action}") simulate()
AutoScaler(target_qps=50, cool_down=3)定义“目标单实例吞吐 50 QPS”和“冷却 3 轮”,相当于给值班经理定下调度铁律。(qps + self.target_qps - 1) // self.target_qps用整数运算实现“向上取整”,这是代码版的 ⌈Q/T⌉ 公式。max(self.min_replicas, min(self.max_replicas, ...))把期望实例数钳制在最小值和最大值之间,对应 HPA 副本数限幅。if self.gc_count > 0: ... 冷却中冷却时间内不重复扩缩,防止指标抖动造成“扩容-缩容”震荡。simulate()用一条模拟的 12 时段流量序列运行决策器,观察实例数如何跟随流量变化。
常见误区
误区扩容阈值越低越灵敏,流量稍微一涨就立刻扩容,这样永远不会超时,难道不好吗?
正解阈值过低会引发“扩容风暴”。柜台刚排到 3 个人就叫兼职,兼职到了队也散了,然后又要缩容——系统在扩缩之间反复横跳,调度开销和费用比省下来的还多。正确做法是设置合理的阈值带(如队列 15 人扩容、5 人缩容)并配合冷却时间,让扩缩容动作“稳准狠”而不是“快乱抖”。
为何常见:初学扩缩容时总觉得“快就是好”,忽略了扩缩容本身有成本和副作用,就像新手司机见缝就变道,反而更慢。
误区流量来了直接一次性扩到最大实例数不就行了?反正机器能扛住,还省心。
正解一次性拉满风险极大。集群资源有限,几十个实例同时启动会争抢带宽和显存,反而拖慢整个集群;而且如果流量是瞬时的,扩完就浪费。工程上通过伸缩步长控制单次扩容数量,比如每轮最多加 4 个实例,观察一轮指标再决定是否继续。这就像奶茶店高峰期不是一次性塞进 20 个新人,而是一批批让兼职上岗,边看边调。
为何常见:人们习惯用“最大保障”思维应对不确定性,但云计算资源的调度瓶颈和成本现实,决定了“渐进式扩容”才是更优解。
误区既然冷启动要几分钟,那干脆永远别缩容,多留几十个实例常驻,流量来了直接扛。
正解常驻实例等于把“弹性”变成了“固定部署”,流量低谷时成本依然居高不下,违背了扩缩容的初衷。正确思路是用更低成本的“兜底方案”缓解冷启动:保留少量常驻副本保证最低服务能力,配合模型预加载、请求预热把冷启动时间压缩到可接受范围,其余实例交给自动扩缩容动态调度。
为何常见:冷启动慢是痛点,很多人本能地用“保留备份”来对抗,却忽视了备份本身也要花钱。这是“局部最优”思维在成本维度上的盲区。
误区超卖就是把内存申请总和调成物理内存的两倍三倍,反正任务不会同时用满,赚翻了!
正解超卖的本质是“赌不同任务不会同时用满”。一旦赌输,触发 OOM 或被内核杀死进程,服务直接雪崩。安全做法是超卖率控制在 1.2-1.8 倍,并配合资源配额、驱逐优先级和监控预警。就像奶茶店储物柜可以两人共用一个,但如果十个人同时来取货,柜子就炸了。
为何常见:看到硬件闲置就本能想“挤一挤”,但没意识到共享资源在极端情况下的相互干扰。成本优化必须给风险留出缓冲垫。
误区把 CPU、内存、GPU、QPS、P99、队列深度、磁盘 IO……所有指标全部接入扩缩容规则,这样最科学。
正解指标过多会导致扩缩容决策逻辑混乱。比如 CPU 和 GPU 利用率同时走高时,该听谁的?多个指标互相矛盾时系统频繁震荡。工程实践是每个扩缩容目标选 1-2 个“最能反映瓶颈”的指标,其余指标只做监控看板不参与决策。奶茶店值班经理盯住排队人数这一个指标就能调好班,盯十个指标反而无从下手。
为何常见:技术人不自觉陷入“数据越多越安全”的焦虑,但决策系统的复杂度会随指标数量指数上升,最后变成一个谁都无法解释的黑盒。
面试题
Q1. 水平扩缩容(Horizontal Scaling)的核心做法是什么?
- A.提升单机 CPU 频率
- B.增加服务实例数量← 正确答案
- C.扩大磁盘存储容量
- D.优化业务代码逻辑
答案:B。水平扩缩容通过增减实例数量来应对流量变化,就像奶茶店多开几个收银窗口。A 是垂直扩缩容,C 是存储扩展,D 是性能优化,均不属于水平扩缩容。
Q2. 以下哪项不是常用的扩缩容观测指标?
- A.服务请求 QPS
- B.GPU 利用率
- C.响应时延 P99
- D.数据库表行数← 正确答案
答案:D。QPS、GPU 利用率、P99 时延直接反映推理服务的负载和质量,是扩缩容的常见指标。数据库表行数是业务数据指标,与扩缩容决策无关。
Q3. 自动扩缩容中的“冷却时间”机制主要解决什么问题?
- A.模型冷启动超时
- B.指标抖动导致震荡← 正确答案
- C.请求超时重试风暴
- D.实例内存持续泄漏
答案:B。冷却时间规定扩缩容动作后暂停一段时间再判断,防止指标在阈值附近轻微波动时反复扩缩。它不解决冷启动(A)、重试(C)或内存泄漏(D)。
Q4. 当前服务共 4 个实例,总 QPS 为 1600,单实例目标吞吐为 200 QPS,按容量公式需要多少实例?
- A.2 个
- B.4 个
- C.8 个← 正确答案
- D.16 个
答案:C。容量公式为 C = ⌈Q/T⌉ = ⌈1600/200⌉ = 8 个实例。当前仅 4 个实例但需要 8 个,因此需要扩容到 8 个,而不是保持 4 个或扩到 16 个。
Q5. 推理服务冷启动过程中,最耗时的环节通常是?
- A.拉取镜像和加载模型权重← 正确答案
- B.客户端建立 TCP 连接握手
- C.业务日志异步发送队列
- D.监控指标上报频率设置
答案:A。冷启动慢主要因为模型权重文件多达数 GB,从磁盘读取并加载进显存需要数十秒甚至几分钟。B、C、D 开销通常在毫秒级甚至异步完成,不是瓶颈。
Q6. 预测式扩缩容相比反应式扩缩容的核心优势是?
- A.完全消除冷启动耗时
- B.提前扩容以应对预期流量← 正确答案
- C.无需任何历史流量数据
- D.可以彻底取消冷却时间
答案:B。预测式扩缩容基于历史流量模式,在高峰到来前就扩容,避免“等指标超标再扩容”的滞后。它不能消除冷启动(A),仍依赖历史数据(C 错误),也仍需冷却机制防抖(D 错误)。
Q7. 将推理服务缩容到零副本的主要风险是?
- A.磁盘空间无法释放
- B.冷启动延迟不可接受← 正确答案
- C.网络带宽占用升高
- D.模型推理精度下降
答案:B。缩到零副本虽最省钱,但流量到来时需从零开始冷启动,模型加载可能耗时数分钟,用户请求等不起。B 正确;A、C、D 均与缩容到零没有直接因果关系。
Q8. 以下哪些属于推理服务冷启动优化的有效手段?
- A.常驻最小副本预热← 正确答案
- B.模型权重预加载← 正确答案
- C.请求失败立即重试
- D.流量预热脚本暖机← 正确答案
答案:ABD。常驻副本保证池子里始终有热实例;预加载把模型提前读进显存;预热脚本用模拟请求填充缓存。C 的“请求失败重试”是客户端容错机制,不仅不能加速冷启动,还可能增大瞬间压力。
Q9. 超卖(Overcommit)配置可能带来哪些风险?
- A.内存资源耗尽触发 OOM← 正确答案
- B.邻居实例互相性能干扰← 正确答案
- C.集群利用率必然下降
- D.请求时延必然降低
答案:AB。超卖依赖“任务不会同时用满”的假设,一旦多个任务同时达到峰值,内存耗尽触发 OOM,且同宿主机的任务会争抢 CPU 和内存互相干扰。超卖的目的是提升利用率(C 错误),也不能保证时延降低(D 错误)。
Q10. 某服务当前 P99 时延为 500ms,目标 P99 为 200ms,当前 8 个实例,按 HPA 公式计算期望副本数?
- A.16 个
- B.20 个← 正确答案
- C.24 个
- D.32 个
答案:B。HPA 公式为 N = ⌈(M_cur/M_desired) × N_current⌉ = ⌈(500/200) × 8⌉ = ⌈20⌉ = 20 个实例。这是“指标超标 2.5 倍,副本数也相应放大 2.5 倍”的线性扩展逻辑,因此选 20。