一、先分清三种突发形态
1. 脉冲型:几分钟内翻几倍
特征是来得快、去得也快,常见于消息推送、活动开场、定时任务集中触发。这类流量对扩容速度要求极高,而新实例从启动到接流量往往要几十秒到几分钟,等扩出来峰值已过。
对付脉冲,重点不在扩容,而在「留出热实例」。保持一两个常热副本,用少量闲置成本换掉一次拥塞。同时给入口侧加排队与限流,让请求有序等待而不是一起超时。
2. 阶梯型:涨上去就不下来
特征是某一时刻起台阶式上升,之后维持在新水位。常见于业务上线、新渠道接入、白天的作息曲线。这类流量适合正常扩容,因为扩容耗时相对整个高峰持续期很短。
关键是阈值要设在台阶前,不能等水位到顶才动。可预测的阶梯用定时策略提前扩,比等指标触发更稳。
3. 慢爬型:几小时慢慢抬升
特征是抬升慢、幅度大,常伴随缓存命中率下降、上下文变长。这类流量如果只看即时指标,容易一直「差一点」到阈值,扩得零零碎碎。
对付慢爬,要把统计窗口拉长,用一段时间的趋势判断,而不是瞬时值。同时把单实例处理能力的衰减纳入考虑——上下文变长会让单实例能扛的并发下降,这一点常被忽略。
二、预案第一层:指标怎么选
1. 别只看处理器占用
推理服务的瓶颈通常在加速卡与显存上,处理器占用常年不高。用处理器指标触发扩容,会出现「指标正常、用户超时」的局面。
判断指标是否合适,有个简单办法:在压测里观察哪一个数字先恶化。先恶化的那个才配当触发信号。
2. 三类可用的信号
一是排队长度:等待处理的请求数持续超过某个值,是直观的拥塞信号。二是延迟分位值:看高分位而不是均值,均值被大量快请求拉低,掩盖了慢请求。三是显存余量与批处理等待时间:显存贴顶说明装不下更多并发,批处理等待变长说明为了凑批牺牲了延迟。
三类组合使用,能减少误扩与滞后。单看任何一个都会偏。
3. 采样窗口要能压住毛刺
瞬时值抖动大,一个突发小尖峰就触发扩容,扩完又立刻缩,实例反复重建。做法是给指标加一个统计窗口,用一段时间的值代替瞬时值,窗口长度按流量形态定:脉冲型短一些,慢爬型长一些。
冷却窗口同样要设。扩容后一段时间内不再重复触发,缩容后一段时间内不急于再扩,防止来回震荡。
三、预案第二层:副本与水位
1. 副本下限决定底线体验
副本下限对应基础保障能力。设得太低,突发一来必然排队;设得太高,低谷期一直在为闲置付费。合理的确定办法是:用历史低谷期的峰值流量除以单实例能扛的并发,向上取整,再加一档余量。
2. 副本上限决定代价边界
副本上限要给一个明确数字,理由有三:资源池有容量边界,无上限的扩容请求会在关键时刻失败;成本要可控;上游依赖(数据库、缓存、下游接口)承受不了无限制的实例增长。
上限怎么定:按可接受的峰值成本倒推,同时确认资源池在那片区域是否有足够余量。余量这件事要提前问,不能等触发时才发现开不出来。
3. 水位线要留出扩容提前量
扩容有耗时,所以触发阈值不能设在「已经满了」的位置,而要设在「再涨就满」的位置。经验做法是把触发水位设在单实例能力的一定比例处,留出的余量正好覆盖一次扩容的耗时。
这个余量要按实测的扩容耗时调,不按感觉调。扩容从触发到能接流量需要多久,是这套预案里必须实测的一个数字。
四、预案第三层:冷启动与预热
1. 冷启动慢在哪几段
新副本从创建到能接流量,通常要经过:调度到节点、拉取镜像、读入模型权重、初始化运行时、分配显存、探针就绪。其中模型读入往往占大头,大模型动辄几十秒到几分钟。
2. 三处可做的优化
一是镜像与模型提前落到节点,防止每次冷启动都从远端拉。二是把权重文件放在读取更快的存储层,或做本地缓存。三是把探针分开:启动探针、存活探针、就绪探针各管一件事,未就绪的实例不接流量,防止请求打到还在读入的副本上。
第三点尤其重要。没有就绪探针时,流量会涌入还在读入的实例,这些请求全部超时,用户看到的就是一片失败。
3. 预热比事后扩容更有效
对流量形态可预测的业务,定时预热往往比指标触发更划算:在固定高峰前十几分钟把副本数抬上去,高峰过后再降。做法简单,效果稳定。
对不可预测的流量,就只能接受一部分冷启动成本,用排队与超时保护体验。此时要做的是让等待可见——告诉调用方当前在排队,而不是让它一直转圈。
五、预案第四层:扩不动时怎么办
1. 限流要分优先级
扩容失败或赶不上时,入口侧必须有保护。做法是按调用方或接口分优先级:核心交易链路优先放行,后台批处理与试运行请求排队或延后。没有优先级区分的限流,会在关键时刻把核心请求一起拦掉。
2. 降级要提前定好动作
降级不是临时想出来的。要提前定三件事:降级触发条件、降级后提供什么、怎么恢复。常见动作有:缩短输出长度上限、关闭非必要的后处理、切换到更轻的模型版本、暂时关闭某些非核心接口。
每一项都要在预案里写清由谁决定、怎么通知调用方。临场讨论降级的代价,往往比降级本身更大。
3. 超时与重试要成对设
上游超时设得太长,请求堆在入口;设得太短,正常请求也被判失败。重试要有上限与退避,无上限的重试在拥塞时会把请求量放大数倍,等于自己制造雪崩。
六、预案要演练,不能只配置
1. 压测要看三个数字
压测不是把流量打满看会不会崩。要记录三个数字:单实例稳定承担的并发、扩容一次的实际耗时、扩到上限后的排队与超时表现。三个数字直接决定阈值与副本上下限怎么设。
压测要在接近真实的条件下做:同样长度的上下文、同样的模型版本、同样的入口链路。用短请求压出来的数字,套到长上下文场景上会差很远。
2. 故障演练要专门制造失败
只演练「顺利扩容」没有意义。要专门制造几种失败:扩容请求因资源不足被拒、新实例读入失败、某个上游变慢。看的是三种能力:失败能否被及时发现、有没有自动兜底、值班的人知不知道该做什么。
失败演练暴露的问题,往往比正常演练多得多。
3. 回退要能在分钟级完成
预案里必须有一条回退路径:改回上一版配置、切回上一版模型、把流量导到备用入口。回退动作要演练过一次,知道大概花多久。
没演练过的回退,真用时总比预计慢一倍。
七、把预案写成一张可执行的卡片
1. 卡片上写六行
第一行:三种突发形态各自的判定特征。第二行:触发指标与统计窗口。第三行:副本下限与上限、触发水位。第四行:扩容耗时实测值。第五行:扩不动时的限流与降级动作。第六行:回退路径与联系人。
六行写在一页里,贴在值班能看到的地方。预案写成几十页文档的,真出事时没人翻。
2. 每季度复看一次
模型换了、上下文长度变了、调用方增加了,都会让原来的阈值失效。复看时对三件事:卡片的数字与当前配置是否一致、扩容耗时是否变长、降级动作是否还适用。
3. 让调用方也知道
限流与降级会影响到调用方,所以预案里要有一段是对外的:什么情况下会限流、调用方应该怎么退避、有没有备用入口。把这段提前告知,比事发时逐个解释有效。
结语
应对突发流量,配自动扩容只是其中一步。真正起作用的是四层准备:认清流量属于哪一种形态、选对触发指标、把副本与水位算准、把冷启动和扩不动时的动作提前定好。四层都到位,扩容才是保护;缺一层,扩容反而可能变成负担——扩出来的实例接不上流量,或者扩完又立刻缩回去。
落地顺序建议这样:先做一次压测,拿到单实例能力与扩容耗时两个数字;再按这两个数字设副本下限、副本上限与触发水位;然后把限流与降级写进卡片;末了做一次专门的失败演练。做完这一轮,判断预案是否有效有个很实际的标准:某个工作日的中午流量突然翻三倍,值班的人不需要打电话找人,服务在可接受的时间内自己稳住了。