首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
大模型推理集群化调度:负载均衡与异常容错体系实战
📅 2026/10/11 20:07:26
✍️ 爱科研究院
👁 阅读 3,247
商用大模型服务上线之前我一直觉得集群化调度是个“大厂才需要操心的奢侈品”。直到某次线上压测一台装了两张卡的推理节点在QPS冲到20以后直接OOM紧接着网关把所有重试流量打到了另一台节点不到两分钟整条链路雪崩我才意识到单机单卡扛不住商用流量这件事是绕不过去的坎。这篇文章把我从那次事故开始逐步补齐的集群化调度、负载均衡和异常容错体系做一个完整的梳理方案在多个商用推理场景里经过验证思路和坑都写出来希望能帮你少走点弯路。1. 为什么商用大模型服务必须走集群化路线1.1 单机单卡的天花板是物理级的上限大模型推理和传统Web服务最大的区别在于每个请求都会持续占用GPU资源而且是自回归式地占用。一个70B参数的模型按照FP16精度算光权重就要140GB左右显存单卡根本放不下就算用AWQ或GPTQ量化到4bit也要35GB以上再叠加KV Cache和运行时开销一张卡能支撑的并发请求数量非常有限。我的实测数据很直观一个量化到INT8的13B模型在单张A100上单请求延迟大约40ms但并发一上来因为GPU计算资源被瓜分每个请求的延迟会非线性上涨。跑到4并发时P99已经到300ms左右跑到8并发时经常出现超时。商用场景下API网关的QPS要求动辄几百甚至上千单机方案在物理上就不成立。更麻烦的是单台服务器是整个系统的“单点”。网卡故障、电源问题、GPU散热异常任何一个硬件闹脾气整个服务就断了。商用付费用户对可用性的预期是99.9%起步这意味着月停机时间不能超过43分钟单机架构无论如何都满足不了这个SLA。1.2 商用场景的SLA逼着你必须做冗余商用API服务的SLA不只是“能访问”还包括延迟分位数。我所在的团队当时负责一个面向B端客户的模型服务平台客户的调用量不大但对稳定性极其敏感。某客户的应用是实时客服场景要求P95延迟小于800ms。就这一个指标单机方案在并发稍微抖动时就轻易击穿。集群化调度的本质是用多台机器把“单点风险”摊薄。一台节点挂了另外几台还能兜住流量一个GPU显存撑不住了调度器可以把新请求打到其他节点。只有这样可用性和延迟指标才有保障。1.3 集群化要解决的三层问题我把集群化调度拆成了三个递进的目标这些目标贯穿了后面所有方案设计第一层是流量分发。请求进来后网关或入口负载均衡器要决定把它送到哪台节点。这一层解决的是“请求别都挤在一个节点上”。第二层是资源调度。大模型推理和普通请求不一样每个请求的后端资源消耗差异极大一个生成512个token的请求占用的计算量可能是一个生成16个token请求的30倍以上。调度器要根据每台节点的显存余量、GPU利用率、当前队列深度来决定把请求放到哪甚至要决定什么时候放这一层解决的是“节点资源被均匀且高效地利用”。第三层是故障透明。任何一个节点发生故障用户不能有感知。请求要么被重试到其他节点要么服务端通过容错机制快速恢复。这层解决的是“别让单点故障变成全局事故”。三层缺一不可。流量分发做不好资源调度再精细也没用容错不完善前面做得再好也可能在故障瞬间功亏一篑。2. 调度层设计请求从进来到落地的完整路径2.1 网关层的流量调度策略网关层是所有请求进入集群的第一道关卡也是负载均衡的主战场。我见过不少团队直接把Nginx默认的round-robin拿来用这在普通HTTP服务上问题不大但换到大模型推理场景就很容易翻车。原因在于大模型推理请求的动态性太强了。同样是“给我写一段文章”这类请求有的用户让它生成50个字有的让它生成2000个字两者在后端占据GPU的时间可能相差一个数量级。轮询算法不感知后端状态可能导致某个节点刚好接到几个长生成请求就被长时间占住而其他节点却在空转。我目前在网关层用的是“最少连接数 节点权重”的组合在此基础上做了两个增强第一个增强是节点权重动态化。每个推理节点启动时都有一个基础权重健康检查系统会根据节点的实时状态对权重做增减。比如某节点的最近错误率从0.1%涨到了2%就把它的权重从100下调到60这样新请求以更低概率被送过去给节点留出恢复时间。权重调整是平滑的避免因为瞬时抖动导致权重剧烈波动。第二个增强是P2C算法Power of Two Choices。每次来一个请求网关从可用节点列表中随机挑两个再对比这两个节点的当前负载指标我用的指标是“当前活跃请求数 × 平均预估执行时间”选负载更低的那个。P2C的核心价值在于避免了“羊群效应”——如果所有请求都用“找全局最低负载节点”的策略会出现大量请求同时涌向同一个节点的现象而P2C通过随机采样把这种竞争概率大幅降低。至于一致性哈希我把它用在有前缀缓存亲和性需求的场景。比如同一批请求的prompt前缀相似如果能让它们尽量落在同一个节点上KV Cache的命中率会显著提升。但代价是节点扩缩容时哈希环会变化导致缓存失效所以这块要谨慎使用不能一刀切全局启用。2.2 Engine层的请求队列调度流量到了推理节点之后还有一个更微观的调度问题节点上的推理引擎例如vLLM、TGI这类推理服务框架如何在多个等待中的请求之间分配GPU算力。对大模型推理来说最核心的技术是连续批处理Continuous Batching。传统批处理方式要等当前批次的所有请求都生成完毕才释放资源但连续批处理允许动态地往当前正在执行的批次里插入新的请求某个请求生成了终止符它占用的显存和算力就可以立刻出让给新请求。这个机制大大提高了GPU利用率但也给调度器带来了一个难题——什么时候插入新请求才不会影响正在运行的请求的延迟我在这块的实践经验是不完全依赖推理框架默认的调度策略而是给调度器增加“显存余量感知”和“队列优先级”两个维度。如果节点的可用KV Cache空间低于某个阈值即使请求队列很长也不要继续灌入新的请求否则会发生显存OOM如果两个请求的优先级相同优先执行预计耗时短的这样能提高系统的吞吐量。另外一个关键点是PD分离调度Prefill和Decode分离。Prefill阶段是并行计算算力密集Decode阶段是自回归串行访存密集。如果长prompt的Prefill请求和大量Decode请求挤在一起Prefill会霸占GPU导致Decode延迟暴涨。解决思路是把Prefill和Decode调度到不同的计算资源上或者至少在同一个节点内做时间片隔离保证Decode的延迟稳定性。2.3 显存感知的节点放置决策大模型推理集群里显存是最稀缺的资源比GPU算力还要稀缺。每个节点能支持多少并发请求取决于KV Cache还能不能挤出空间来。计算一下假设某模型单请求的最大上下文长度是4096个tokenKV Cache每个token大约占用1MB显存具体取决于层数、头数和精度那单请求满载时需要4GB左右的KV Cache空间。一张80GB显存的卡模型权重占40GB剩下的空间里只有大约30GB能分配给KV Cache大约能支持7个满载请求的并发。如果调度器不了解这个约束盲目把请求塞满整个节点结果就是OOM或者无限排队。我在做节点放置决策时依赖的是推理引擎暴露的实时指标包括已用显存量、KV Cache余量、当前排队请求数、平均decode速度。调度器根据这些指标来估计“如果这个请求进来预计会占用多少KV Cache预计需要多久执行完”。执行完的快慢我用简单公式估算预估执行时间 prompt_tokens / prefill_speed max_tokens / decode_speedprefill_speed 和 decode_speed 是引擎的实时统计值每个节点各不相同。调度器拿这个预估时间和显存占用来决定请求放在哪效果比单纯看“当前请求数”好得多。节点放置还要考虑“位置亲和性”。同一个模型服务的多个副本如果分布在同一个机柜/交换机下跨节点的张量并行通信会更快。商用集群如果网络架构允许尽量把同一模型的副本放在同一个TOR交换机下能有效减少跨机通信的延迟和带宽压力。3. 负载均衡的工程实现细节3.1 健康检查不能只看“活着”很多初做集群的人会把健康检查做成“端口通不通”。但大模型推理节点特别容易陷入“进程活着但实际已经不能稳定服务”的状态。最常见的就是显存碎片化进程没死端口也通但KV Cache被切得七零八落新请求进来后分配不出连续空间只能无限等待最终超时。我的健康检查体系分主动和被动两层。主动探活是每5秒向节点发送一个极小的推理请求比如让模型生成一个字的请求通过返回内容和耗时判断节点是否健康。被动监控是统计每30秒内节点的请求错误率、平均耗时、P99耗时任何一个指标连续3个周期超过阈值就认为节点处于亚健康状态。健康状态的判定结果会和调度器的权重联动。不健康的节点权重会线性下降直到完全摘除。摘除不等于丢弃系统会继续探活恢复后权重再平滑回升。这个“平滑”很关键如果权重一下子从0跳到100瞬间涌进来的流量可能又把刚恢复的节点打挂。3.2 长短请求混部下的均衡策略大模型场景里“连接数均衡”是个典型的误区。一个节点如果同时接到几个生成长度为2048 token的请求它的实际负载可能是只接了16个短请求节点的十倍以上。所以负载均衡必须从“基于连接数”升级到“基于预估负载”。具体做法是网关在转发请求前先解析请求里的参数比如prompt长度和max_tokens这两个信息从API协议里能读出来。用前面那个预估时间公式算出一个负载因子网关在多个候选节点之间对比时用的是“节点当前累计负载因子”。举个例子节点A当前有5个请求每个预估耗时500ms累计负载因子2500节点B当前有3个请求每个预估耗时1500ms累计负载因子4500。虽然A的连接数更多但调度器应该把新请求送到A。我还额外加了一个限制单个节点的预期并发水位不能超过Node的显存上限。即使某节点的累计负载因子很低但如果它的KV Cache余量已经不够承载一个新请求了也不会往里面派活。这个防止OOM的约束优先级最高。3.3 慢启动和预热机制新节点上线或者某个节点从故障中恢复时如果马上把大流量打进去大概率会出问题。原因有三点模型权重还在从磁盘/内存加载到显存的过程中CUDA context和推理引擎的算子需要初始化时间GPU的算力缓存是空的跑起来的速度比预热后慢不少。我给节点上线设计了“慢启动”流程新节点初始权重只有正常值的10%然后以每分钟20%的速率递增直到恢复正常。同时系统会对新节点做一次内部的预热请求用一个固定 prompt 连续打几次让显存分配和CUDA kernel都进入稳定状态然后再放开外部流量。这个机制在集群扩容的瞬间特别有用。有一次新加了两台节点没有慢启动就直接上线结果节点启动后的前两分钟超时率高达15%就是因为模型还没完全加载好就被压了近一半流量。后来加了这个机制扩容过程基本无感。以下是负载均衡用的几个关键参数表格供参考指标项采集方式阈值参考联动动作节点存活状态TCP心跳 应用层探活连续2次探活失败摘除节点错误率被动统计最近5分钟1% 持续3个周期权重降为60%P95延迟请求日志统计阈值 持续3个周期权重降为40%KV Cache余量引擎指标单请求预估占用暂停分配新请求GPU利用率指标采集95% 持续5分钟触发扩容评估4. 异常容错从故障识别到自愈闭环4.1 故障分类是容错设计的地基容错体系要覆盖哪些场景决定了整个系统的复杂度。我在设计阶段先做了一次故障分类把所有可能出现的异常分成四类每类的处理方式差异很大。第一类是硬件故障比如GPU掉卡、网卡中断、电源异常。这类故障通常很“硬”进程直接挂掉或者节点失联容错方式就是快速摘除节点并把请求重试到其他节点。第二类是资源过载比如显存OOM。OOM不像硬件故障那么直接进程不会立刻死但服务能力会急剧下降。容错方式是限制并发、触发节点重启、拒绝低优先级请求。第三类是长尾故障比如节点出现“慢但是不死”的状态。这种最坑人节点既不报错也不超时就是响应速度慢了一个量级。容错方式是基于延迟监控动态降权。第四类是逻辑/配置故障比如模型加载出错、推理参数配置错误、版本升级后行为异常。这类故障通过健康检查的返回内容来识别探活请求返回的结果如果和预期不符说明节点逻辑存在问题。4.2 检测机制的三个层级容错的前提是“发现得够快”。我按时间灵敏度把检测机制分成了三个层级。即时层TCP连接失败、连接超时、进程退出这类信号可以在毫秒级别发现。网关层对每个请求的转发结果做一个统计如果某个节点的连续3次连接都失败立即进入“疑似故障”状态不再分配新请求。亚健康层被动统计节点的错误率、P50/P95延迟、token生成速率。这些指标每10秒计算一次如果延迟或错误率持续3个周期超标触发熔断或降权。主动探测层除了被动的请求统计系统还定时发探活请求检测节点的深度健康状态。探活请求会执行一小段完整的推理过程和真实请求等价能发现“节点表面活着但实际推理链路异常”的问题。举个例子我们曾经遇到一个节点因为驱动问题推理时会偶发地返回NaN结果。普通的请求统计看不出延迟和错误率异常因为请求是成功返回的只是内容错了。加上主动探测后探活请求返回的结果里带校验字段发现内容不是预期格式才揪出了这个隐蔽问题。4.3 重试、熔断、降级的组合使用故障发现后不能让请求直接失败要有一套应对动作。重试是最直接的手段但大模型推理场景里重试要格外小心。生成类请求不是天然幂等的同一个请求重试两次可能产生两次计费和两份结果。我的做法是给每个请求生成唯一ID从网关发出去时带上重试时也带上同一个ID推理引擎在处理时先查ID如果已经处理过就返回缓存的句柄结果。这样既保证用户体验又避免重复计算。重试次数要严格控制。默认情况下每个请求最多重试2次重试间隔使用指数退避加随机抖动比如第一次重试等待200ms±50ms第二次重试等待1s±200ms。不控制重试次数的后果很严重一次短暂的节点故障可能让网关的重试请求把其他健康节点的队列打爆形成雪崩。熔断是防止雪崩的第二道防线。每个节点对应一个熔断器默认状态是“关闭”连续错误次数达到阈值比如5次后熔断器打开后续请求不再发给这个节点。熔断器打开后每隔10秒进入半开状态放少量请求探测节点是否恢复恢复则关闭熔断器未恢复则继续保持打开状态。这个机制和健康检查配合使用能有效保护后端节点不被持续涌来的流量拖垮。降级是容错的最后手段。当集群内所有节点都过载或部分故障时网关可以按策略把请求降级到轻量模型或者减少max_tokens。比如某些非核心的文本摘要请求可以把生成长度从500硬降到100实在不行就用规则模板兜底返回。降级不完美但比整个请求直接失败要好得多。4.4 推理侧的优雅退出与恢复节点自身在做故障恢复时也要讲究“优雅”。推理引擎的退出要等当前正在执行的批次处理完毕或达到最大等待时间不能直接kill。我们有一个节点因为显存碎片化需要重启如果强制kill正在推理的几十个请求全部超时客户端会感受一波明显的失败。加上graceful shutdown之后节点会先停止接收新请求然后等待已有的请求完成再退出这个过渡过程一般控制在10秒内。对于超长生成任务比如离线批量总结文档节点重启会导致任务中断从头再来成本太高。我做了KV Cache 的checkpoint机制推理引擎每处理一段时间就定期保存中间状态任务被中断后可以从最近的checkpoint恢复而不是从零开始。类似数据库的checkpoint和WAL思想。5. 落地中的常见坑与排查实录5.1 慢节点引发尾延迟现象整个集群的平均延迟正常P50和P90都在指标内但P99时不时飙高。排查时发现某台GPU因为散热问题长期处于高温度状态GPU频率被降了30%推理速度明显慢于其他节点。这类慢节点最坑人的地方在于它不报错健康检查探活也通过但就是贡献了大量高延迟请求。解决思路是靠“延迟基线的动态对比”。系统为每个节点记录最近10分钟的平均decode速度当某个节点的decode速度比其他节点慢超过30%且持续5分钟以上判定为慢节点自动降权。降到10%权重后保留探活流量继续观察直到恢复。慢节点不是马上摘除因为摘除会让它的存量请求全部超时降权加观察是更平滑的处理方式。5.2 显存碎片造成的“假饥饿”现象某节点显存明明还剩20GB但新请求进来后一直排队最终超时。查引擎日志发现显存虽然有余量但都是碎片无法分配出连续的KV Cache空间。这个问题的本质是KV Cache的分配和释放不够高效。推理请求反复创建释放显存块时间长了就会碎片化。解决手段有两个方向一是升级推理引擎的显存分配器使用更精细的内存池二是定期做节点整理当碎片率达到阈值时触发集群将存量请求平滑迁移然后重启节点清空显存。底层方案是显存整理调度。好在这个问题在较新版本的推理框架里已经大幅缓解但如果你所在团队用的是一个自研的推理引擎这个问题需要优先处理。5.3 重试风暴引发的雪崩现象某个下游依赖服务抖动导致组件的节点短暂超时网关层的重试逻辑触发每个超时请求被重试2-3次整体QPS直接翻倍。翻倍后的流量又让节点更忙产生更多超时陷入恶性循环集群全线崩溃。排查后发现网关层的重试没有全局限制。日后加了全局重试比例阀值每分钟重试请求不得超过总请求数的1%超过则暂停重试。同时重试请求和正常请求走不同的优先级队列重试请求的优先级降一级这样即使需要重试也不会挤压正常请求的资源。另外所有超时请求在重试前必须做一次“目标节点负载快照”查询只有当存在负载低于阈值的节点时才重试否则直接返回错误。这个策略避免了“没地方去还硬重试”的无效行为。重试相关的参数配置我给一个参考参数建议值说明最大重试次数2超过后直接返回错误首次重试等待200ms ± 50ms指数退避起始值重试间隔倍率2第二次等待约1s全局重试比例上限1%超限后暂停重试熔断器错误阈值连续5次打开熔断器半开状态探测间隔10s放少量请求探测5.4 前缀缓存热点导致的分配不均现象集群中部分节点GPU利用率极高部分节点很低但流量分发看起来是均匀的。后来分析请求分布发现某些客户端的prompt是动态变化的但前缀固定一致性哈希策略会让前缀相同的请求固定落在同一个节点上该节点因为缓存命中率高计算速度快但也因此承接了大量请求负载很高。解决办法不是取消一致性哈希而是给缓存命中率设置泳道当某个节点因为缓存命中率过高导致负载超过水位线时调度器会临时把新请求分散到其他节点。虽然这样会牺牲一部分缓存命中率但换来的是整体延迟更均衡。缓存命中和负载均衡之间的取舍需要在实际场景里找一个平衡点没有绝对最优解。实操中的最后几点心得这套集群化调度和容错方案是目前支撑我们商用模型服务的核心框架。从一开始“单机测试一切正常一上生产就挂”到现在几百个并发请求稳定运行最大的体会是故障发现的速度和恢复动作的克制比任何精妙的调度算法都重要。如果你的集群刚起步我建议先从最基础的做起——把健康检查做扎实把所有节点的延迟、错误率、KV Cache余量实时采集起来形成统一的负载视图再逐步加入动态权重、熔断、优雅退出这些机制。千万不要一上来就追求复杂的调度算法基础的可观测性和快速故障发现才是真正的底层能力。还有一个小技巧分享在投入生产之前给整个集群做一个故障注入演练随机杀死一个节点、随机让某个GPU温度升高、随机制造一次大规模超时看看调度系统能不能在规定时间内完成摘除、重试和恢复。这个演练会暴露很多理论上想不出来的问题非常值得做。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/11 20:07:26
5G接入时延优化:从随机接入到RRC建立的排查与调参实战
2026/10/11 20:07:26
仓库管理系统大作业指南:从ER模型到MySQL触发器与Flask演示
2026/10/11 20:07:26
新能源电驱多合一集成趋势:800V、碳化硅与油冷扁线技术解读
2026/10/11 21:12:34
易支付运营版源码部署与支付通道轮询、投诉进件实战解析
2026/10/11 21:12:34
基于调频能力裕度的风电场一次调频策略解析
2026/10/11 21:12:34
HDFS存储优化实战:纠删码、压缩与小文件治理策略
2026/10/11 21:12:34
Debian 12下FFmpeg安装全攻略:apt源、静态构建与源码编译
2026/10/11 21:12:33
Claude Code skill 方法论:把工作方法封装成 AI 技能包的四步框架与 TaoToken 接入实践
2026/10/11 21:07:33
安全帽检测数据集实战:从数据校验到YOLO模型训练与避坑指南
2026/10/11 0:00:10
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/11 0:00:10
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/11 0:00:10
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/11 0:00:10
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/11 0:00:10
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/11 0:00:10
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/11 19:13:46
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/10 3:41:54
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/9 11:36:17
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)