
开局先讲个真实场景。前段时间帮团队搭一套大模型推理集群模型是72B的MoE量化后单卡A100勉强能塞下但一压测就露馅单卡每秒只能处理两三个请求排队一长首字延迟直接飙到十几秒。业务方当场就问了句——“你们这集群和单卡跑有什么区别”这个问题其实问到了点子上。很多人一说“大模型推理集群”就觉得是“多买几张卡起多个服务前面挂个Nginx”。真这么干卡越多越乱钱花了不少延迟反而更难看。千卡负载均衡这事难的不是“把请求分到不同卡上”而是让每一张卡都在干它最擅长的事让请求在集群里的等待时间和排队时间都能算得清清楚楚。这篇就沿着“单卡到底缺什么→多卡怎么扩→千卡怎么组织→均衡怎么落地”这条线把推理集群从设计到排障的完整逻辑讲清楚。适合正在做大模型服务化、或者准备从单机部署往多机集群走的同学参考。1. 单卡推理的真实边界先搞懂瓶颈在哪1.1 一次大模型推理请求的本质拆解别一上来就聊集群先回到单卡。一次完整的大模型推理请求在引擎内部实际上是两段Prefill预填充和Decode解码。Prefill阶段模型要把你输入的Prompt完整计算一遍生成每个Token对应的中间状态K和V存进KV Cache。这个过程是密集的矩阵乘法GPU算力利用率能拉到很高但一次请求如果输入很长这步就会非常耗时。Decode阶段才是真正“一个词一个词往外吐”的过程。每生成一个Token只需要基于已有的KV Cache做一次前向计算。这个阶段的特点是算力消耗小但内存带宽消耗大——因为每次要读取完整的KV Cache。这两个阶段叠加起来单卡推理的“容量”就被锁死了。显存决定你能不能装下模型和KV Cache算力和内存带宽决定你每秒能生成多少Token而推理引擎能不能把多请求的Prefill和Decode混在一起调度决定了你这张卡在并发上来时会不会被拖死。1.2 单卡部署的几个硬性天花板很多人本地跑大模型玩得很开心——用Ollama或者vLLM起个单卡服务自己问几个问题觉得挺快。但一旦作为后端服务暴露给真实用户三个瓶颈立刻显现显存天花板。模型权重吃掉一部分显存剩下的空间才是KV Cache的配额。7B模型在FP16下权重约14GBA100 80G还能留出大量空间做并发但如果换成72B模型就算用量化塞进单卡KV Cache的空间就被压缩到很小并发一高直接OOM或者疯狂换入换出。算力天花板。单卡算力是固定的Decode速度就有物理上限。以7B模型为例单张A100实测大约能跑到每秒800~1000个Token的总吞吐看起来不少但一个长回答就要出500个Token换算下来单卡同时只能支撑几十个活跃用户超过就得排队。单点故障风险。单卡服务挂了等于整个服务挂了没有降级方案。这在生产环境里是没法接受的。结论很直接单卡只适合个人玩、小流量内部工具、或者开发联调。真要做面向多用户的推理服务多卡、多机是必然选择。而“多卡”怎么组合直接决定了你后面集群的上限。2. 从单卡到多卡三类并行方式怎么选2.1 张量并行把一个大模型“拆”到多张卡上张量并行Tensor Parallelism解决的问题是“单卡放不下”。思路很直观把Transformer里某一层的权重矩阵按行或按列切开分到多张GPU上计算时通过卡间通信把部分结果拼起来。举个例子一个线性层权重是 4096x4096切成4份每张卡各存 4096x1024 的部分。前向计算时每张卡算出自己那部分结果再用AllReduce操作汇总。这里面有个关键约束每做一次张量并行都需要跨卡同步一次。Transformer层数深一次请求要经历几十上百次同步通信开销非常可观。所以张量并行一般只用在单机多卡内——靠NVLink的高速互联带宽足够大通信损耗才压得住。跨机跨交换机做张量并行性能会惨不忍睹。实际工程里TP张量并行的规模通常取4或者8对应一台机器上的卡数。超过8基本不划算。2.2 流水线并行把模型的“层”切成几段接力跑流水线并行Pipeline Parallelism是另一种切法不切权重矩阵而是按Transformer层数切。第1~16层放在GPU0第17~32层放GPU1数据像流水线一样从GPU0流到GPU1。PP的好处是通信量小——只在层与层的边界传输一次中间激活值不像TP那样每层都要同步。但它的缺点是容易出现GPU空转GPU1要等GPU0算完第一批数据才开始干活早期阶段会有一批GPU在摸鱼。工程上解决这个问题的手段是micro-batch切分把一个batch拆成更小的batch交错地灌进流水线让各卡尽量并行干活。但就算这样PP的GPU利用率也普遍低于TP。所以我的建议是能用TP不用PPPP只在模型大到单机放不下时才考虑。比如千亿参数模型必须跨多台机器部署TPPP混合是无奈也最合理的选择。2.3 数据并行最朴素的扩容手段如果模型单卡能放下只是算力不够那就用数据并行Data Parallelism每张卡存一份完整的模型权重各跑各的请求互不干扰。这就是典型的“横向扩容”。DP是最容易理解的扩容方式但工程上它有一个隐含要求前面必须有一个负载均衡器把请求分配开来否则几份副本之间会出现严重的冷热不均。这里要特别提醒一个误区很多人做了数据并行每张卡各自挂一个服务进程然后让负载均衡器做轮询——这能跑但效果很粗糙。为什么因为各个请求的输入长度、生成长度差异巨大轮询分发会让某些卡碰上“长文本大户”而忙死其他卡却空着。更合理的做法是“全对全”的一体化调度这个后面展开讲大模型推理集群的调度层时再细化。2.4 并行方式的组合艺术主流大模型推理集群里的并行配置通常是这么组合的并行方式解决问题代价推荐规模张量并行TP单卡放不下模型高频通信开销4~8卡需NVLink流水线并行PP跨机部署超大模型流水线空泡尽量少用数据并行DP并发不够用需依赖负载均衡调度按需扩专家并行EPMoE模型专家分布复杂调度仅MoE场景对于现在主流的MoE模型比如Mixtral、DeepSeek系列还会引入专家并行Expert Parallelism把不同的专家网络分散到不同卡上路由时做All-to-All通信。这个技术非常高效但调优门槛也高普通场景不做强制要求。一句话总结模型放不下先加TPTP扩不动了考虑PP并发不够DP加节点。这个优先级顺序值得刻在脑子里。3. 千卡推理集群的整体架构设计3.1 先想清楚集群的四个分层千卡集群不是几百张卡堆一起那么简单的。我习惯把架构分成四层接入层、调度层、推理Worker层、存储与权重分发层。每层各司其职彼此通过接口解耦。3.2 接入层流量的第一道关卡接入层是外部请求进入集群的入口。它不关心你是哪个模型、跑在哪个Worker上只负责两件事安全和转发。安全层面要做认证鉴权、限流。转发层面把请求转发给调度层。这里有一个常见的架构坑有人图省事接入层直接把请求发到某个Worker——这样调度层就形同虚设了无法做到全局最优也会让后续扩缩容变得很难操作。所以接入层必须只与调度层通信不应该知道Worker的存在。所有从外部进来的请求一律先进全局队列。3.3 调度层集群的真正大脑调度层是整个千卡集群最核心的部分也是和普通Web集群差异最大的地方。它做的事叫作“请求级调度”每一个推理请求进入后调度器决定它去哪一组GPU、什么时候开始执行。为什么不直接发到Worker就行因为GPU执行任务是“排队式”的不是像Web服务器那样接一个请求算一个请求。想象一下一组GPU上有4个Worker每个Worker当前都在处理一个长回答Decode可能要跑几十秒。如果调度器愣头青一样把新请求直接扔给某个Worker这个请求就得在Worker内部排队——而调度器完全不知道它在等还会继续往这个Worker塞更多请求最后导致延迟雪崩。好的调度器必须掌握每个Worker的实时负载状态当前正在处理的请求数、预估剩余时间、排队长度、KV Cache余量。综合这些信息才能做出最优决策。调度策略上业界主流做法是最少请求数优先哪个Worker在跑请求最少优先排给它基于预估完成时间优先结合请求长度估算剩余处理时间亲和性调度同一个会话或同一用户的请求尽量分到同一个Worker这样能复用其已生成的KV Cache大幅降低延迟其中第三点特别重要。我见过一个真实案例因为没做亲和性调度同一用户连续追问时每次请求被分发到不同WorkerKV Cache全部作废相当于每次追问都要重新把之前的对话历史从头算一遍。对话一长延迟高到用户直接放弃。做了亲和性调度后这个场景的首Token延迟降低了60%以上。3.4 推理Worker层无状态化的关键设计推理Worker是实际执行GPU计算的进程一般来说一个进程占一张或多张GPU跑着vLLM/SGLang这类推理引擎。这里有一个架构层面的关键决策Worker必须无状态化。什么叫无状态就是它只负责执行推理不存储用户的会话记录、不维护用户状态的上下文。所有的会话状态都交给调度层管理——调度层知道某个用户上次的KV Cache在哪个Worker上下次请求就优先调度到那个Worker。但无状态化之后引出另一个技术问题调度层是怎么做到“还记得上次在哪”的答案是调度层维护了一张映射表记录“用户ID/会话ID → 最近使用的Worker实例”。这张表在调度器进程内可以直接存内存里最简单也最可靠。如果做亲和性调度之后同一个Worker都负载过高怎么办折中方案是只要那个Worker还能扛得住就硬塞给它如果扛不住就牺牲亲和性换整体稳定允许调度到别的Worker重新计算前缀。什么都不如“服务不挂”重要。3.5 存储层与权重分发容易忽略的第四层千卡集群里还有一个容易被忽略的环节模型权重从哪来、怎么更新的。模型权重少则几十GB多则几百GB。集群启动时不能每个节点都从对象存储慢慢下载——那要等好几十分钟。业界通常有这样的方法首启时把权重文件从对象存储拉下来写入节点的本地NVMe或SATA盘。之后每次启动直接读本地盘中间只做增量更新。若权重文件在某次更新后损坏了可以通过校验和去对象存储做校验修复。还有一个更高级的方案是page-based权重分发把权重文件切成固定大小的分片通过P2P的方式在节点之间互相分发类似BT下载的原理一台机器拉完其他机器从它那里拿。几千张卡的集群几分钟内就能把几百GB的权重传完比传统的主从分发快一个数量级。4. 千卡负载均衡的落地细节4.1 别把Web负载均衡直接照搬过来很多人一听“负载均衡”第一反应是Nginx、LVS、F5。但这些传统LB只处理连接和网络包分发不了解上层推理服务的特性在千卡推理集群里其实很容易出问题。问题出在请求重量极度不均匀。同样是“你好你是谁”跟“请帮我解析这篇三万字的论文并提炼结论”计算量完全不是一个量级。轮询和最小连接数这类经典算法在这种场景下都会失效。正确的做法是把负载均衡“上移”不让LB直接去分流量而是让它把请求交给调度器的全局队列再由调度器基于各Worker的真实负载状态决定派发到哪张卡。负载均衡的本质不是“均匀分发”而是“避免堆积”。4.2 排队长度的隐喻最好用的调度信号在大量实践中我发现一个非常可靠的经验法则用“Worker排队长度”作为调度信号几乎总是能超过复杂的预测算法。原理很好理解GPU推理任务天然是排队式的队列越短说明这张卡越空闲把请求发给它等待时间最短队列越长说明这张卡已经忙得快转不动了再往里面塞请求就是火上浇油。调度器可以定期获取各Worker的“当前请求数排队的请求数”给每个Worker算一个负载评分综合评分最低的Worker优先接收新请求。4.3 会话保持与亲和性一个被低估的性能利器前面提过亲和性调度这里再展开讲一下。对话类大模型的推理有一个独特特性多次请求之间存在前缀复用。用户发“帮我总结一下这篇文章”紧接着又追问“再补充一下细节”后一个请求的Prompt包含前面所有对话历史但前一轮的KV Cache已经算好了。如果能把第二次请求发到同一个Worker就可以直接复用缓存省掉一大段重复计算。工程实现的要领是请求进来时带上会话ID或用户ID调度器根据会话ID查表找到上次使用的Worker如果该Worker健康且负载可接受就发送给它如果Worker异常或过载则放回全局队列让负载最低的Worker接单这种策略对多轮对话场景简直是性能“外挂”。我们实测过长对话场景下做了亲和性调度单请求的首Token延迟能下降50%以上整机吞吐也能提升30%。4.4 预热、慢启动与优雅下线千卡集群的负载均衡还有一个经常被忽略的细节新加入的Worker需要预热退出服务的Worker需要优雅排空。新Worker启动后如果负载均衡器立刻把大量请求转给它会出现一个尴尬的现象模型权重虽然加载到显存了但CUDA Kernel和KaCache还处于冷状态第一个请求会异常地慢。你想想新来的请求排队到它的头上结果一看半天没出字用户体验直接拉闸。所以正规做法是新Worker刚注册时标记为“预热中”调度器只给它分配少量请求随着处理量逐渐提升权重直到完全放开。优雅下线方向也一样。如果把一个还在处理请求的Worker直接kill掉正在排队的请求全部失败。正确的下线流程是先通知调度器“我要下线了”调度器立刻停止分发新请求给它但保留队列中已有的请求等这些请求全部处理完Worker再自杀退出。这个细节生产环境中真的能帮你少收很多客服投诉。5. 容量规划与动态伸缩5.1 从QPS反推GPU数量来感受一下如何计算选机器之前必须先把容量算清楚。推理集群的容量不是看“峰值QPS”而是看两个更接地气的指标总生成速率Token/s和平均首Token延迟TTFT。举个例子业务想要支撑100路并发每路平均每秒生成20个Token那集群的Total throughput至少要做到100×20 2000 Token/s。假设单张A100跑7B模型实测能到800 Token/s那至少需要3张卡才够。再预留30%的冗余取4张。很多人会问这怎么听着这么简陋但现实就是这样很多时候决定你GPU数量的不是并发数而是总Token吞吐。这也是为什么很多看起来并发不高的小团队一算发现要买几十张卡——模型大、单卡吞吐低潮水一来就承压。5.2 自动伸缩按队列深度而不是按CPU负载集群的自动伸缩逻辑和传统微服务有个显著区别——参考指标应该是调度层的全局队列长度而不是CPU使用率。GPU推理的CPU占用通常很低真正繁忙的是算力和显存而这俩在系统指标里并不直观。队列长度才是实打实的信号队列越来越长说明需求超过了集群的消化能力需要扩容队列长期空着说明资源闲置可以缩容。伸缩动作也不能一梭子全扩。我习惯分两步先小步扩容比如每次加2台观察队列长度是否下降如果下降不明显再继续扩。这样能避免“一下子扩了几十台结果用户量根本没那么大”的浪费。5.3 冷热分层把预算花在刀刃上一个很现实的问题千卡集群的GPU费用是非常高昂的。我见过不少团队把整个集群都配置成性能最强的卡比如H100结果日常利用率只有20%纯属浪费。分层方案值得借鉴用少量H100等高算力卡处理高价值、超低延迟需求比如实时对话、付费产品用A100/A800处理常规流量把低优先级的离线批量推理比如数据清洗、定时生成安排到降配节点上慢一点没关系只要便宜就好。再进一步还能按时间段做缩容夜间流量低时把大部分机器缩容回收只保留少量兜底。这些措施单独看没啥叠加起来能把集群成本压低30%以上。6. 常见问题与排查实录千卡集群的坑坑坑不一样。我把过去几个月折腾下来的高发问题整理成速查表后面再挑典型的展开聊。典型症状可能原因排查建议集群吞吐上不去但GPU利用率很高TP配置过大通信开销吃掉收益检查AllReduce占比降低TP规模重测某几张卡总有OOM其他卡很空闲调度器未感知显存余量让Worker定期上报KV Cache余量调度器考虑显存维度新启动的节点处理后延迟高未做预热Kernel和缓存未就绪注册时标记预热中渐进分配流量长对话场景延迟恶化明显亲和性调度未生效缓存重复计算核查会话ID传递、调度表TTL扩缩容后集群抖动、请求大面积超时下线时未优雅排空强制排空队列后才能摘除Worker权重更新后部分节点仍跑旧版本版本校验没做或分发失败启动时校验权重文件hash版本不匹配拒绝对外服务6.1 案例一显存明明够为什么会OOM有次压测时发现个诡异问题集群总显存才用了60%但某个Worker频繁OOM。查了很久才发现原因——并不是真的显存爆了而是单卡上KV Cache预留空间过小。相同显存空间下KV Cache分配策略是贪心的长请求会一次性申请一大块连续显存但分配后释放不彻底碎片化严重后面的大请求无连续空间可分配。解决方案是给推理引擎显式限制单请求的max_seq_len并开启连续批处理中的显存池化让KV Cache以小块为单位分配、减少碎片。这属于那种“症状在外面病根在内部”的经典问题。6.2 案例二首Token延迟突然飙高问题出在慢启动压测环境刚搭好时首Token延迟从200ms一下跳到3秒——彼时我一开始以为是模型推理慢后来发现是当天有台新Worker刚加入集群它一启动就被调度器平摊到了大量流量冷启动状态下的性能叠加导致那一整批请求全被拖累。做了预热机制之后问题就消失了。这个案例也说明千卡集群里很多“性能劣化”并不是硬件问题而是调度策略的缺陷——尤其是对“新节点”的管理不当。6.3 案例三NCCL通信超时的背后有次我们跨机扩展TP规模想试一下TP16跨两台机器结果一上线就大面积报NCCL超时错误。排查发现问题出在这里——跨机通信走的是RoCE网卡但系统默认用的是内核态IPoIB性能和稳定性都跟不上。这个坑提醒我们TP规模超过单机后网络栈必须认真配置RDMA/RoCE必须显式指定使用用户态驱动否则结果就是延迟爆炸。跨机TP配置启动前建议先跑NCCL all_reduce的benchmark确认带宽在可接受范围内再加流量。7. 从单卡到千卡真正难的不是卡的数量把整个架构梳理完你会发现一个有点反常识的结论从单卡到千卡真正难的从来不是GPU的数量而是“状态管理”。单卡上跑模型KV Cache、并发上下文都在一个进程里天然一致一旦拆到千卡KV Cache散落各处、请求要跨机器调度流转、Worker有生有死整个系统的复杂度一下就上来了。这时真正决定集群好坏的不是单卡跑得多快而是调度器能不能准确掌握全集群的状态、做出合理的分配决策。在我做过的几次集群改造里最深的一条体会是先保证调度层拥有足够准确的实时状态再谈优化策略。很多团队上来就研究复杂的预测算法、强化学习调度结果连Worker上报负载的链路都不稳定收上来的数据是脏的——再好的算法也是白搭。如果你正打算从单卡往集群迁移我建议的路径是先花一周把可观测性做好每个Worker的负载、队列、延迟指标都清晰可见再动手实现调度层。数据健全之后很多看似复杂的问题用最简单的策略就能解决。这个内容后续还可以扩展的方向不少比如多模型混部时的KV Cache隔离、基于强化学习的动态调度、离在线混合部署等。但万变不离其宗状态感知是根策略优化是叶。根扎稳了叶子才能长好。