ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型推理集群从单卡到千卡:负载均衡与架构设计实战

大模型推理集群从单卡到千卡:负载均衡与架构设计实战 1. 从单卡到千卡先搞清楚我们要解决什么问题先说个真实场景。很多人第一次接触大模型推理是从单卡跑Qwen、Llama这类开源模型开始的。一张卡装个vLLM或者TGI起个服务接口调通感觉“推理也没多难嘛”。但一旦业务量上来并发请求一多单卡立刻就被打穿。这时候你自然想到加机器于是从1张卡变成4张卡、8张卡再往后就是几十台机器、上百张卡。问题就从“怎么跑通模型”变成了“怎么让这一堆卡协同工作还能稳定扛住流量”。这篇内容就是围绕这个演进过程来写的先拆解单卡推理的瓶颈再讲多卡、多机的集群架构设计最后落到千卡规模下的负载均衡和调度策略。适合三类人看刚入门想做推理服务优化的工程师、已经在用多卡但遇到性能瓶颈的团队、以及准备设计大规模推理平台的后端同学。我会尽量用实际踩过的坑来讲不堆概念。这里先给一个整体认知大模型推理集群和训练集群的设计逻辑完全不同。训练集群追求的是“把算力榨干”batch size可以怼得很大容忍一定的延迟推理集群追求的是“在延迟稳定的前提下把吞吐拉满”而且要处理动态变化的请求。这个本质差异决定了后面所有架构选择。2. 单卡推理的瓶颈拆解一张卡到底能扛多少2.1 单卡推理的极限在哪里先拿最常见的配置说一张NVIDIA H10080GB显存跑Qwen3 8B或者27B这种规模的模型。很多人以为瓶颈在GPU算力实际测下来会发现算力往往不是最先触顶的。负载均衡的关键往往在于推理引擎的调度能力和显存管理。以H100跑Qwen3 8B为例实测下来在vLLM默认配置下单卡能做到的吞吐大概是指标参考值输入长度平均512 tokens输出长度平均256 tokens并发请求数16-32吞吐量800-1500 tokens/s单请求延迟P951.5-3秒这组数据会随模型、量化方式、显存大小浮动但规律是一致的显存容量决定了你能同时塞多少请求计算吞吐决定了你每秒能吐多少token两者必须匹配。有个很容易忽略的点是KV Cache。以8B模型为例模型权重本身可能只需要16GB显存FP16但KV Cache会随着并发数线性增长。假设每个请求预留4096 tokens的KV Cache空间一个请求大约吃掉几十MB显存并发32路时就要额外占用好几个GB。如果并发继续往上加显存先爆服务直接OOM算力再强也没用。2.2 单卡推理的几个典型瓶颈点实际压测下来单卡推理最先出现的瓶颈按出现频率排序是第一显存容量触顶。模型权重 KV Cache 激活值三者加起来超过显存总量服务直接崩。这个最无解只能换更大显存卡或者做量化。第二调度开销。默认的连续批处理continuous batching如果实现得不好请求排队时间会很长。实测下来vLLM的调度器在并发超过某个阈值后调度延迟会明显上升表现为P95延迟突然拉高。第三GPU利用率不均衡。这个在单卡上不太明显在多卡并行时才是大问题——后面我会专门讲。第四输入输出长度波动。真实业务里有的请求输入只有几十个token有的输入是几千token的长文档。如果按最大长度预留资源显存浪费严重如果不预留又可能触发OOM。单卡阶段能做的事其实有限调大batch size、开continuous batching、做模型量化FP8甚至INT4、用paged attention减少碎片。这些优化做完单卡Qwen3 8B大概能从几百tokens/s提升到1500 tokens/s左右。但再往上就真到头了。2.3 单卡到多卡直觉方案为什么不对很多人第一步想到的是一张卡跑不完那就把模型切到多张卡上用张量并行tensor parallelism。这个思路本身没错但要注意张量并行是“用通信换显存”GPU之间的通信开销会吃掉一部分算力。模型越小、单卡显存越够用张量并行带来的收益就越低甚至可能变负。举例说明。Qwen3 8B在单张H100上本来跑得好好的你非要用2卡张量并行结果每卡只占一半显存但每次前向计算都要跨卡同步两次通信延迟直接拖慢整体速度。实测下来8B模型2卡张量并行的吞吐反而不如单卡延迟还会高一截。这时候正确的选择是数据并行把请求分发到多张卡上每张卡独立跑完整模型而不是张量并行。所以单卡到多卡的第一个决策点在于模型适合用张量并行切分还是适合用数据并行扩展判断标准很简单——单卡显存能不能装下模型权重加上合理规模的KV Cache。装得下就优先数据并行装不下才考虑张量并行。这也是为什么很多生产环境里7B-14B级别的模型用数据并行70B级别的模型才必须上张量并行。3. 多卡推理的集群架构两种扩展方式的取舍3.1 数据并行 vs. 张量并行什么时候选哪个数据并行DP的思路是每个GPU上都放一份完整模型请求按一定策略分发到不同的GPU上各跑各的。它的好处是扩展性好、没有通信瓶颈坏处是显存浪费——每张卡都要放一份完整的模型权重。张量并行TP的思路是一个模型切成多份分别放在多张卡上一次前向计算需要多卡协同完成。它的好处是能跑单卡装不下的超大模型坏处是通信开销随并行度增长通常建议TP大小不超过8NVLink带宽范围内。我的建议是能DP就别TP除非模型真的放不下。原因很直接——DP架构下加机器就是线性扩展每加一张卡就多一份吞吐TP架构下加卡性能增长是边际递减的而且对网络带宽的要求极高。实际项目中7B、8B、13B级别的模型在A100/H100上DP就够了。70B级别至少需要2卡或4卡TP。而如果做MoE模型比如Qwen3 30B-A3B这种因为激活参数少TP的收益会被通信开销抵消一部分需要实测调TP大小。3.2 多机部署的组网需求多卡部署还只是“单机多卡”一旦规模到几十张卡就要跨机器组网了。推理集群对网络的要求比训练集群宽松一些但也不能随便弄。关键判断是机器之间的通信频率。DP模式下的多机通信主要是请求分发和结果汇总频率低、数据量小万兆以太网基本够用TP模式下的多机通信是每层都要做AllReduce频率极高必须走InfiniBand或者至少RoCERDMA over Converged Ethernet否则通信延迟会直接吃掉模型并行带来的收益。具体的组网方案可以这样参考纯DP集群单机8卡机器间万兆网即可但要注意前端负载均衡的带宽。2机TP16的70B推理机器间必须上IB网络否则性能约等于不可用。混合方案单机内部用NVLink做TP机器之间做DP这是目前最主流的千卡集群布局方式。这里有个经验值TP通信的数据量大约等于激活值大小乘以TP并行度。70B模型hidden size通常是8192一层Transformer的激活值就有几十MB每层两次AllReduce几十层下来通信量非常可观。所以跨机TP一定要谨慎能塞进单机8卡的就不要跨机。3.3 推理引擎选型vLLM、TGI、TensorRT-LLM怎么选集群架构定下来之后推理引擎就是承载一切的基础。现在主流就三个vLLM、HuggingFace TGI、TensorRT-LLM以及基于它做的Triton Inference Server。我的选择逻辑是这样先用vLLM做默认选项。理由很简单生态最成熟、支持模型最多、社区活跃、API兼容OpenAI格式落地最快。我实测vLLM的continuous batching和paged attention在长上下文章节里的表现是三个引擎里最稳的。TGI的优势是在HuggingFace生态内集成度最高部署最简单的模型做一些diffusers类的多模态推理更方便。但性能上限和vLLM相比没有明显优势并发控制不如vLLM灵活。TensorRT-LLM是性能天花板。同样模型、同样硬件TensorRT-LLM的吞吐通常比vLLM再高20%-40%。代价是编译时间长、模型切换成本高、动态shape处理麻烦。如果你做的是“固定几个模型长期服务”的生产环境值得上如果模型迭代频繁建议先用vLLM扛着。还有个容易被忽略的点引擎和集群调度器的配合。vLLM原生支持张量并行和流水线并行但数据并行场景下多个vLLM实例之间没有自动负载均衡需要自己在前面挂一层路由。这点后面讲千卡调度时会详细说。4. 千卡规模的负载均衡从“能用”到“好用”的关键设计4.1 千卡集群的基本拓扑到了千卡规模集群就不再是“一堆机器”了而是要有清晰的逻辑分层。我常用的一套分层是接入层负责统一入口处理认证、限流、路由。调度层负责把请求分配到具体的推理实例核心是负载均衡策略。推理层实际跑模型的GPU实例通常以“实例组”每组若干张卡为单位暴露服务。存储层模型权重、KV Cache的offload、日志和监控数据。千卡部署时最忌讳的是把集群当成一个大的“GPU池子”所有请求随机扔给任何一台机器。正确的做法是把实例按照模型、规格、性能分成不同的池子每个池子独立扩缩容。举个例子。你有1000张H100可能这样划分池A200张卡跑Qwen3 8BTP1DP200承载大部分在线对话流量。池B400张卡跑70B模型TP4组内100个实例承载高复杂度推理。池C200张卡跑多模态模型TP2承载图片输入类请求。池D200张卡作为弹性缓冲池任何池子流量突增时都能临时接管。这种池化设计的核心好处是隔离故障域。池A出了问题不会拖垮池B模型升级也只是在池内滚动发布不影响其他池子。4.2 负载均衡策略不只是“轮询”那么简单很多人以为负载均衡就是请求轮流分发实际在千卡推理场景里简单的轮询Round Robin是不够的因为每个推理实例的“负载”不是一个简单的计数而是动态变化的。推理实例的负载取决于三个因素当前正在处理的请求数、每个请求的剩余输出长度、以及GPU的实际利用率。同样一个实例可能刚接了一个输出1000 tokens的长请求也可能刚完成一批短请求。按请求数轮询会把长请求集中打到同一个实例上造成局部热点。在实践中我推荐两层的负载均衡设计第一层全局调度器根据“实例当前活跃请求数 预估剩余token数”做加权路由。这个信息可以从vLLM暴露的/metrics接口实时获取不需要额外的agent。第二层实例内部靠vLLM自己的continuous batching调度保证同一实例上的请求尽可能均匀地被GPU处理。两层之间有一个配合点全局调度器下发请求时要“慢半拍”不要一次性把队列塞满。给推理实例预留一点缓冲空间让它在处理完当前批量时能立刻接上新的请求而不是一直在排队。关于“等开销负载均衡”简单解释一下不是让每个实例处理的请求数完全一样而是让每个实例的GPU忙闲程度尽量一致。忙闲不均是推理集群最大的性能杀手——同一个集群里一张卡被打满、另一张卡在空转整集群的吞吐上不去还可能因为热点实例超时造成连锁失败。4.3 请求级调度策略排队、优先级和抢占千卡规模的推理集群流量绝不是均匀的。高峰期可能几十倍于低谷期。这时候调度策略要从“分配”转向“排队和优先级管理”。我对生产系统的建议是给每个请求打上优先级标签在线交互 异步任务 批量评测。优先级高的请求可以“插队”但要有超时保护——不能无限抢占资源否则低优先级任务会饿死。队列长度要设上限超过上限直接拒绝返回503不要无限排队拖垮系统。这里有一个关键的工程细节在线推理和离线批处理的资源池要分开。把离线任务比如批量评测、数据标注和在线对话放在同一个池子里离线任务虽然优先级低但它也会占用显存和算力一旦并发量大在线请求的延迟就会飙高。我的做法是在线池和离线池物理隔离离线池有空闲时让在线池的请求“借用”资源但高峰期禁止借用反向。请求级调度还要注意超时设置。推理请求和普通HTTP请求不同一个大模型生成几百个token可能需要好几秒网关的超时时间不能设得太短。一般我会把超时设成“模型配置的最大生成时间 一定余量”而不是传统的3秒5秒。否则你会发现长输出请求全被网关掐断报错率看起来很高但实际上是超时造成的假象。4.4 实例生命周期管理扩容、缩容和滚动更新千卡集群的另一个核心问题是实例管理。推理实例和传统无状态服务不一样——加载一个70B模型进显存可能需要几十秒甚至几分钟。如果频繁扩缩容资源浪费和启动时延会把整个集群拖垮。所以生产环境的推理实例管理我强烈建议“池子预热 慢扩快缩”的策略每个模型池维护一个最小实例数保证任何时刻都有“热的”实例在跑新扩容的实例在后台预热。扩容时新实例先把模型加载好、健康检查通过再开始接流量不要一加入集群就立刻分发请求给它。缩容时先把实例置为“排空”状态不再接收新请求等已有请求都结束后再释放资源。滚动更新同理。更新模型版本或者引擎版本时一次只替换一个实例并且分批次验证。不要同时更新超过池子25%的实例否则一旦新版本有问题整个池子的可用容量暴跌。这些经验看着朴素但真到千卡规模哪天不小心把全部实例同时重启那画面太美不敢看。集群故障多半不是设备问题而是管理操作失误。5. 核心环节实操从部署到压测的完整流程5.1 环境准备与配置清单先列一份我常用的配置清单基于NVIDIA H100集群每台机器8卡项目配置GPUNVIDIA H100 80GB x8CPU64核以上建议96核内存512GB以上系统盘1TB NVMe SSD数据盘4TB NVMe SSD用于模型权重缓存机间网络InfiniBand 200GbpsTP跨机必需机内网络NVLink PCIe Gen5推理引擎vLLM 0.6调度器自研路由层 Kubernetes 自定义调度器模型权重建议预先下载到本地数据盘不要每次启动都从对象存储拉——70B模型权重有140GB左右从网络拉一次可能好几分钟这时间够加载好几次了。5.2 单卡部署vLLM的完整步骤先以单卡部署Qwen3 8B为例把基线流程跑通再扩展。首先安装vLLMpip install vllm然后写一个启动脚本核心参数如下python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen3-8B \ --served-model-name qwen3-8b \ --tensor-parallel-size 1 \ --max-num-seqs 32 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --port 8000这里的几个参数值得解释一下max-num-seqs同时最多处理多少个请求。设太大容易显存OOM设太小浪费算力。8B模型在H100上32左右是比较安全的起步值可以逐步往上调。max-model-len最大上下文长度影响KV Cache预留。设8192不会让模型真的用到8192但会按8192预留显存预算。gpu-memory-utilizationGPU显存利用率上限默认0.9即预留10%给CUDA context和碎片。实测0.92-0.95也能稳定运行但0.9最保险。启动后先用简单的curl验证curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d {model: qwen3-8b, messages: [{role: user, content: 你好}], max_tokens: 100}能返回结果说明服务起来了。这时候可以再用压测工具测单卡吞吐推荐用vllm-benchmarks或者简单的并发脚本。5.3 多机部署与Kubernetes配置示例单机部署跑通后多机集群我推荐基于Kubernetes管理。原因很简单千卡规模的手动管理不现实而K8s即便有各种槽点它的声明式管理、滚动更新、故障自愈能力在推理场景里够用。先写一个Deployment配置示例省略了namespace等常规字段apiVersion: apps/v1 kind: Deployment metadata: name: qwen3-8b-dp labels: app: qwen3-8b spec: replicas: 8 template: metadata: labels: app: qwen3-8b spec: containers: - name: vllm image: vllm/vllm-openai:latest command: [python, -m, vllm.entrypoints.openai.api_server] args: - --model/data/models/Qwen3-8B - --served-model-nameqwen3-8b - --tensor-parallel-size1 - --max-num-seqs32 - --max-model-len8192 - --gpu-memory-utilization0.9 - --port8000 env: - name: CUDA_VISIBLE_DEVICES value: 0,1,2,3,4,5,6,7 resources: limits: nvidia.com/gpu: 8 volumeMounts: - name: model-storage mountPath: /data/models volumes: - name: model-storage hostPath: path: /mnt/models这个配置跑起来后每个Pod会占用一张节点上的8张卡各自独立跑数据并行。然后需要给Pod加一个Service做集群内负载均衡apiVersion: v1 kind: Service metadata: name: qwen3-8b-service spec: selector: app: qwen3-8b ports: - port: 8000 targetPort: 8000 type: ClusterIP这个Service默认是轮询负载均衡。之前说过单纯的轮询不够所以生产环境我会再加一层自定义调度器根据/metrics的数据做加权分发而不是直接用Service自带的负载均衡。5.4 压测方法与调优记录部署完成必须压测否则集群到底能扛多少负载完全是玄学。压测我用的是Locust写并发脚本模拟真实请求序列import random import time from locust import HttpUser, task, between class InferenceUser(HttpUser): wait_time between(0.5, 2) task def chat_completion(self): prompt_len random.randint(100, 2000) prompt 测试文本 * prompt_len max_tokens random.randint(50, 500) self.client.post(/v1/chat/completions, json{ model: qwen3-8b, messages: [{role: user, content: prompt}], max_tokens: max_tokens })压测时我重点看四个指标吞吐量tokens/s整体每秒生成的token数。P90/P95延迟大多数请求的完成时间比平均延迟更有参考意义。拒绝率因为超时或OOM被掐断的请求比例。GPU利用率分布是否所有卡都忙得均匀。第一轮压测通常会发现几个问题某个节点网络带宽成了瓶颈、某个实例GPU利用率偏低、或者某个pod的内存被打满。这时候就要微调参数最常用的调优手段是调整max-num-seqs观察显存余量变化。调整负载均衡权重把请求倾斜到GPU用量低的实例。对超长请求做超时保护防止个别慢请求拖死整个实例。实测下来一个8卡节点跑Qwen3 8B单实例压测到40并发左右P95延迟开始明显上升。这时候可以判断当前配置下的合理负载水位并在前端限流器上按这个水位设置阈值别让流量无限制地涌进来。6. 常见问题与排查技巧实录6.1 典型故障现象与解决思路千卡推理集群的故障最常见的有这么几类**故障一个别GPU利用率100%其他GPU只有30%。**这个大概率是请求分配不均匀热点请求集中在某几个实例上。排查思路是看调度器的路由日志确认是不是轮询策略把长请求连续打到了同一个实例。解决办法是把负载均衡策略从“按请求数”改成“按活跃token数”。**故障二压测时吞吐上不去GPU利用率整体不到50%。**这种情况多半是瓶颈在CPU或网络不在GPU。先看CPU占用如果CPU吃满说明tokenize/调度开销太大需要加大max-num-seqs让GPU更忙如果CPU正常再看网络流量跨机TP场景下通信量过大会让GPU花时间等数据。**故障三服务正常但P95延迟突然飙到10秒以上。**这个最常见的原因是长尾请求——某个请求的输出长度特别长一直占着batch slot导致其他请求排队时间变长。解决思路是给max_tokens设上限或者干脆把超长请求路由到专门的长文本实例组。这几个问题看着简单但千卡规模下的排查比单机复杂得多。单机可以nvidia-smi盯着看千卡规模必须依赖监控系统。我的配置是Grafana Prometheus采集每个实例的GPU利用率、显存、队列长度、请求延迟告警阈值设在利用率不均超过30%或者P95延迟超过目标值的时候触发报警。没有这套监控千卡规模下的运维就是盲人摸象。6.2 负载均衡的隐藏坑实例容量差异这是我在实际运维中踩过最深的一个坑。千卡集群里的实例即使型号完全一样实际性能也有差异。因素很多H100的芯片体质差异、散热条件不同导致的降频、同一台机器上不同PCIe槽位的带宽差异、甚至同一张卡上不同NVLink端口的速率差异。如果负载均衡不考虑这些按“理论容量”均摊流量实际表现就是配置完全相同的两个实例一个P95延迟400ms另一个P95延迟900ms。表面上看是负载均衡的锅其实是硬件差异叠加了负载分配偏差。我的解决办法是在调度层引入“动态权重”机制。每个实例定期上报自己的“最近N分钟的P95延迟”和“GPU平均利用率”调度器根据这些指标实时调整权重。延迟高的实例分到的请求自然少一些延迟低的实例多吃一点流量。这比静态权重靠谱得多因为它是根据真实表现自适应调整的。6.3 推理集群的常见误区速查最后整理一个我常跟团队强调的误区清单误区实际情况总觉得GPU算力不够猛加卡很多时候瓶颈在调度、网络或显存加卡不解决根本问题用轮询做负载均衡就够了推理请求的动态性决定了必须考虑tokens分布不是简单计数一张卡OOM就加张量并行小模型先考虑数据并行TP未必带来收益长请求和短请求混在一起跑会导致长尾延迟最好做队列分离或池子分离压测只看平均延迟推理场景必须看P95/P99平均延迟掩盖问题模型的batch size和训练一样能怼大推理受延迟约束batch不是越大越好这些误区的根源是把训练集群的经验直接套到推理集群上。两者的目标和约束完全不同架构设计也必须分开考虑。7. 写在最后我踩过坑后的几点体会这个项目做下来我最深的体会是大模型推理集群的架构设计本质上是个资源管理问题——管理GPU算力、显存、网络带宽、请求队列这四类资源让它们在一个动态变化的流量环境下保持平衡。具体到落地有几点想分享给你的经验。第一不要一上来就追求“千卡”。先从单卡把模型跑顺、把延迟和吞吐摸清楚再扩展到4卡、8卡每一步都要有压测数据支撑。直接拍脑袋上1000张卡出了问题连排查的方向都没有。第二负载均衡是大集群的灵魂但要分层做全局调度器管实例间分配推理引擎管实例内调度两层配合才能既稳又高效。很多时候你以为是引擎不行其实是上层分发出了问题。第三监控和可观测性在集群规模面前不是“加分项”而是“必需品”。在单卡时代你用nvidia-smi就能排查但千卡规模下没有监控数据一个故障可能要人肉排查几个小时。我知道搭建监控系统比较枯燥但它会在故障时救你于水火。第四也是最重要的一条测试环境一定要模拟生产流量。很多集群故障不是硬件问题而是“压测环境是压测环境、生产环境是生产环境”造成的。请求长度分布、并发模型、超时配置都必须在真正上线前用真实流量回放验证一遍。我见过太多团队在压测环境跑得好好的一上生产就崩根源就在这。这整套架构不是一成不变的模型在变、推理引擎在变、硬件也在变。但核心的资源分层和管理思路是稳定的——把算力、显存、网络这些资源管好把请求合理地分配上去无论底层怎么换这个框架都能用。最后祝你们的集群上线顺利少踩我踩过的那些坑。
返回列表