万卡GPU集群调度器深度解析:从排班设计到实操踩坑 先聊个有意思的现象。前阵子有个跑大模型训练的团队找到我说他们集群利用率总是上不去一万多张 GPU 卡在那里可任务就是排队等资源。我第一反应不是去看监控而是问了一句你们的调度策略是 binpack 还是 spread对方愣了一下说我们用的默认配置。这就对了问题八成出在调度器上。在大规模 AI 训练场里调度器往往是最容易被低估的组件。大家把精力都花在模型架构、数据 pipeline、并行策略上觉得调度不过是个“排队叫号”的活。但真正到了万卡规模你会发现调度器才是那个隐形老板——它决定了谁先用卡、用多久、能不能被抢、抢了之后怎么恢复、碎片资源怎么利用。这篇文章我就围绕“一万张 GPU 怎么排班”这个命题把调度器那些绕不开的设计思路、实操配置和踩坑经验一次讲透。1. 先把“排班”这件事拆清楚调度器到底在管什么1.1 为什么说调度器是训练场的隐形老板一万张 GPU 听起来很多但真正跑起来你会发现算力永远是稀缺的。这边 A 团队要起一个千卡规模的预训练任务那边 B 团队有一批微调任务等着跑C 团队还在做调试需要小规模环境。如果没有一个统一的大脑来分配资源结果就是要么有人抢不到卡要么有人占着卡不用要么任务之间互相干扰拖慢整体进度。调度器干的事情本质上就是在正确的时间、把正确的资源、分配给正确的任务。这句话说起来很轻巧但拆开看每一个环节都是坑。正确的时间指任务什么时候能获得资源能不能在承诺的时间内开始执行。正确的资源不仅仅是“有多少张卡”这么简单还涉及卡型、显存、网络拓扑位置、节点分布是否均匀甚至是否独占整机。正确的任务指任务的优先级、队列归属、是否可以被抢占、预期的训练时长等。在大规模训练场景下调度器扮演的角色更像一个“全局资源经纪人”而不是简单的排队系统。它需要平衡多个目标利用率要高、任务排队时间要短、公平性要有保障、关键任务要能优先执行。这多个目标本身是存在矛盾的比如追求极致利用率就可能牺牲任务排队体验追求公平又会拖慢重点任务进度。调度的艺术就在这里——怎么在矛盾的目标之间找到平衡点。1.2 传统调度和训练调度到底差在哪很多从大数据领域转过来的工程师对调度的第一印象是 YARN 或者 K8s 那套通用资源调度。但 AI 训练场景的调度需求跟这些传统模式有明显差异。最大的区别在于任务模型。大数据领域里的 MapReduce、Spark 任务天然是细粒度、可拆分、对任务连续性要求不高的。但深度学习的训练任务尤其是分布式训练是一个典型的长时间运行、不可中断的负载。一个千卡规模的预训练任务跑起来就是几周甚至几个月中间一旦被抢占前面积累的 checkpoint 成本和恢复时间都是巨大损失。第二个区别在于资源需求的整体性。普通的容器调度你申请 2 卡就给你 2 卡申请 8 卡就给你 8 卡资源是弹性的。但分布式训练任务不一样它往往要求所有参与训练的节点同时获得资源。比如你用 Megatron 做张量并行8 张卡必须同时就位少一张都没法干活。这也就是所谓的“Gang Scheduling”组调度需求。传统调度器在设计时很少考虑这种“全有或全无”的资源分配约束导致大规模训练集群如果直接用通用调度器很容易出现死锁——任务 A 占了 7 张卡在等第 8 张任务 B 占了另外的卡在等 A 释放结果谁也不让谁全都卡死。第三个区别是网络拓扑的敏感性。分布式训练对通信带宽和延迟的要求远高于大数据场景。数据并行任务如果能把 8 张卡分配在同一个节点内用 NVLink 通信跟跨节点走 RDMA 网络相比训练速度可能相差 30% 甚至更多。这就对调度器提出了“拓扑感知”的要求——在分配资源时要尽量考虑 GPU 之间的物理连接关系优先把任务聚拢在通信效率高的范围内。2. 万卡集群的调度设计核心方案与选型思路2.1 资源模型从“一张卡”到“多维立体”的抽象要给一万张 GPU 排班首先要回答一个问题资源怎么描述很多人简单地把一张 GPU 当成一个资源单位但实际生产环境中资源的维度远比这个复杂。一个合理的资源模型至少要包含以下几层基础资源维度GPU 型号A100、H800、L40S 等、显存大小、显卡算力TFLOPS、是否支持特定精度FP16/BF16/FP8。节点维度GPU 所在物理节点信息包括 CPU 型号、内存大小、GPU 数量、GPU 之间的互联方式NVLink、PCIe。拓扑维度节点所在机架、整个 Pod 内的网络架构两层 spine-leaf 还是三层架构、GPU 与其他组件之间的通信路径。可调度性维度这块卡当前是否健康、是否被预留、是否有排障任务占用等。有个实操细节值得注意初始建模时不要把维度分得太细否则调度器在做决策时计算量会爆炸直接拖慢调度响应时间。比较务实的做法是分两级——先做粗粒度的过滤比如只筛选符合条件的卡型和显存再做细粒度的打分比如综合拓扑位置、负载情况、亲和性要求算出每个候选节点的得分。这个“两阶段法”在工程实现上性价比非常高我后面会展开讲。2.2 调度策略binpack 还是 spread这不是一道单选题调度策略的选择会直接影响集群的整体利用率和任务稳定性这是整个调度器设计里最值得纠结的地方。Binpack紧凑分配策略调度器倾向于把任务尽量集中部署在少量节点上把空出来的节点留给未来的大任务。这种策略的好处是能降低集群资源碎片化提升整体利用率坏处是容易造成节点局部热点多个任务挤在一起可能互相抢 CPU、内存带宽、网络带宽导致训练性能反而下降。Spread扩散分配策略调度器把任务尽量打散到不同节点降低互相干扰的风险让每个任务独享尽可能多的节点资源。缺点是容易产生资源碎片——集群里每个节点都剩那么一两张卡单独凑不出一个大任务整体利用率就上不去了。在一万卡规模的生产环境里我的建议是不搞一刀切要做混合策略。比如按任务类型区分——超大训练任务用 binpack 保证整段资源可用性在线推理服务用 spread 降低故障爆炸半径短时任务则默认走 binpack 提升吞吐。还有一种更精细的做法是按队列维度配置策略不同队列服务于不同业务线各自设置自己的调度策略互不影响。在实际工程中很多调度器比如 Volcano、Kueue、Torque 等都支持在队列或任务模板级别配置调度策略不需要全局改配置。这个能力要充分利用起来否则你永远只能用一套参数服务所有业务很难兼顾。2.3 Gang Scheduling分布式训练的生命线Gang Scheduling 这个概念英文直译是“一伙人一起上”在分布式训练里是说一个训练任务的所有进程必须同时获得资源要么全部起来要么全部等着。为什么必须这样举例说明假设一个分布式训练任务需要 32 张卡分到 4 个节点上。如果调度器只成功分配到 3 个节点24 张卡第 4 个节点还在等待中。这时候如果前 3 个节点先启动训练会发生什么训练框架初始化时发现节点数量与预配置不一致直接报错退出。哪怕初始化侥幸通过后续的 all-reduce 通信也会因为缺一个节点而超时。最终结果就是任务白启动一次所有已经分配的资源全部被浪费。Gang Scheduling 的实现方式有很多种比较常见的是先整体匹配再统一启动。调度器在收到任务请求后会先按照资源的约束条件做整体匹配如果能找到满足全部资源需求的节点组合才一次性提交给底层运行时启动全部 Pod。如果匹配不上任务就保持在 Pending 状态继续排队等待。这块有几个工程细节要提醒你给 Gang Scheduling 的任务设置合理的超时阈值。如果长时间匹配不上任务会一直卡着占队列名额影响其他任务。一般建议设置 30 分钟到 1 小时超时后自动降级为等待或通知用户处理。大任务的 Gang 匹配要配合资源预留机制否则会出现“抢到一半”的尴尬状态。调度器要能对部分匹配的资源做预占防止这些资源被小任务抢走。在实际运营中万卡集群上跑的分布式训练任务很大一部分资源无法按时就位原因就出在 Gang 匹配失败后没有做降级处理任务一直等待白白消耗了排队配额。2.4 拓扑感知通信效率就是训练速度分布式训练的通信开销对整体训练性能的影响往往被严重低估。很多团队一开始觉得“多几毫秒通信没关系”等训练跑到大规模并行时才发现通信成了整个系统的天花板。以数据并行 张量并行混合的典型大模型训练为例张量并行的通信发生在每层内部通信频率极高对带宽和延迟极其敏感必须依赖节点内的 NVLink。跨节点的张量并行基本不可接受。数据并行的通信发生在每个 step 的梯度同步阶段通信量跟模型大小成正比。这部分可以走跨节点网络但要尽量利用高速网络RDMA/InfiniBand。流水线并行的通信发生在 stage 之间频率相对低一些但传输的数据量较大对带宽要求也不低。这也就要求调度器具备拓扑感知能力——在给任务分配资源时要能读懂“附近有哪些卡、它们之间怎么连接”这些信息。实操层面上常见的做法有两种节点内优先策略任务申请 8 卡时优先找单个节点内完整的 8 卡资源找不到再拆到多个节点。这种策略在中小规模任务里效果显著。节点集合匹配策略当任务规模大到单节点放不下时比如需要 64 卡调度器要尽量让这批节点在物理机架上相邻减少跨 Pod 的交换机跳数。这需要调度器维护完整的物理拓扑数据计算每一个候选节点集合的网络平均跳数。在社区方案中很多已落地实现这类能力。你在选型时要重点考察调度器是否支持自定义拓扑策略、能否感知网卡/RDMA 设备这类非 GPU 资源的分布这些细节直接影响最终的通信性能。3. 实操过程把一万张 GPU 的排班系统跑起来3.1 集群分级先弄清楚手上有哪些“员工”这里说一个我带队做集群规划设计时的通用思路先分级再分队列。手上的 GPU 资源往往不是“一万张一模一样的卡”而是各种型号、各种代际混杂在一起的。如果不对它们做分级处理后续所有调度策略、运维流程都会变得异常混乱。常见的分级方法一级资源池最新型号、性能最强的卡主要用于大规模预训练、核心业务研发网络配置完整支持 RDMA故障率低。二级资源池上一代主力卡型适合中小规模训练、普通微调和推理任务网络条件中等即可。三级资源池老旧卡或故障率偏高的卡适用于低优先级任务、实验性探索、内部工具开发等。分级之后配合队列做资源配额管理。比如一级池的配额只分配给核心算法团队二级池大家共享三级池按日回收。这样的好处有两个一是保证核心任务总是能拿到最好的资源二是让非核心任务在低优资源池里“废物利用”不占用关键资源。有些团队不重视分级把 A100 和旧卡混在同一个池子里跑任务结果模型训练性能波动极大——今天跑一个 step 只要 3 秒明天同样的代码就要 8 秒这种体验谁用谁知道。3.2 队列与优先级让“谁先上”这件事有章可循在资源池确定之后下一步就是设计队列和优先级机制。简单说队列是给任务划分“赛道”优先级是决定同一个赛道里谁先跑。我建议按业务重要程度 任务类型两个维度来设计队列队列名称适用任务优先级资源配额调度策略是否可抢占preemptible-main核心预训练任务高50%Binpack否regular-tuning常规微调任务中30%Spread是可被高优抢占experiment-dev实验开发和调试低20%Binpack是最短运行时长保护best-effort低优测试任务低弹性Binpack是随时可杀这里说一个很关键的经验低优先级队列不要直接没有保护地抢占否则你会被开发同学的投诉淹没。大家写完代码都想快速验证如果你把他的任务随时杀掉他连 debug 的机会都没有了。比较好的做法是给低优任务一个最短运行时长保护比如至少跑 30 分钟才允许被抢占这样既能保住灵活性又不至于让低优任务长期霸占资源。很多团队在设置队列时没有处理好配额和抢占的关系导致高优任务等资源时低优任务占着卡不动。我见过一个团队把 80% 的配额给了预训练队列结果预训练任务因为 Gang 匹配不上一直 Pending剩下 20% 的微调队列跑得飞起核心团队一天到晚催“我们的卡呢”实际上卡在物理上没有被浪费只是配额的静态划分把资源锁死了。这样的情况建议启用资源配额弹性机制——队列配额只作为上限任务调度时允许某个队列临时使用其他队列的空闲资源一旦原队列任务到来再触发抢占回收。这能把集群整体利用率拉高至少 15%。3.3 抢占与驱逐让“高优先级任务”能及时上位有了队列和优先级下一步就要实现抢占Preemption机制。在大型集群里抢占是保证核心任务时效性的关键能力。抢占分为两种类型可预期抢占高优任务到达后调度器提前发出预警给低优任务一段缓冲时间去保存 checkpoint 并主动退出。这种方式对训练任务相对友好但要求任务具备优雅退出能力。强杀抢占高优任务到达后立即释放资源低优任务直接终止。这种方式适用于对时延极其敏感的任务但代价是低优任务可能丢失进度。在生产环境中我的建议是优先使用优雅退出机制。分布式训练任务如果配置了自动 checkpoint比如每 10 分钟保存一次模型状态即使被驱逐最多损失 10 分钟的进度重跑代价完全可以接受。而强杀抢占在没有 checkpoint 的情况下可能让一个跑了 3 个小时的任务直接归零这对团队士气是毁灭性打击。还有一个被人忽略的细节抢占的顺序要讲究策略不能简单按“谁优先级低就杀谁”。比如低优任务是短任务预计 10 分钟跑完和高优任务冲突这时如果任务已经跑了 8 分钟不如让它跑完再释放资源等待成本很低。但如果低优任务已经跑了 2 小时且远未结束就应该果断抢占。这意味着调度器需要感知任务的“已运行时长”和“预计剩余时长”而不是只看优先级一个指标。3.4 容错与重调度GPU 故障了任务怎么办一万张 GPU 的集群故障是常态不是异常。平均下来一张 A100 一年的故障率大约在 1% 到 2% 之间听起来不高但放大到一万张卡那就是平均每天都会有卡出问题。如果调度器不具备完善的容错和重调度能力任何一个硬件故障都可能演变成大规模任务中断。容错设计有三个层级第一层任务自恢复。训练框架侧比如 PyTorch配置好自动重启和 checkpoint 恢复机制。任务进程崩溃后自动拉起从最近的 checkpoint 继续训练。这层是基础但很多团队在框架配置上没做完整。第二层调度器层面的故障感知。调度器要能够持续监听节点健康状态一旦发现某个节点不可用要能自动将该节点上的任务迁移到健康节点上。如果迁移涉及的是分布式训练任务调度器需要触发整个任务的重新调度——先让所有进程优雅退出再重新匹配资源最后统一拉起。第三层集群级故障域隔离。如果同一个任务的节点分散在多个机架上其中一个机架断电只会影响部分节点。调度器在初始化任务时就应尽量分散故障域避免“同任务同机架”的脆弱部署。这里有一个实际踩过的坑早期我们在做调度配置时只配置了任务失败自动重启的次数但没有配置“节点故障自动迁移”。结果有一次机器故障导致节点长时间不可用任务不断的重启、失败、重启、失败在同一个坏节点上反复折腾了半天才发现问题。后来我们把节点健康检测间隔从 5 分钟缩短到 30 秒并在检测到异常时立即触发调度器重新匹配资源问题才算真正解决。4. 常见问题与排查技巧实录4.1 任务长期 Pending资源却没满怎么排查这个问题在万卡集群上太常见了明明看监控 GPU 利用率不高但新提交的任务就是迟迟不能启动。排查思路如下看是否为 Gang 匹配失败在调度器 Web UI 或命令行工具里查看任务的调度状态确认当前匹配到的节点数和资源总数。如果匹配到的资源一直差一点点说明是资源碎片问题。看是否为资源预留冲突检查是否有其他高优任务做了资源预留但未启动导致新任务无法使用这部分资源。可以让运维同事查一下抢占状态和预留列表。看是否为节点污点/亲和性约束过强任务模板中如果配置了过强的节点亲和性比如必须落在指定标签的节点上这部分节点如果已经被占满任务就会一直无法调度。有一个比较隐蔽的案例某任务配置了“节点内 8 卡必须同机“的强约束但整个集群里 8 卡分布很散大部分节点只有 4 卡空闲。资源总量完全够但因为强约束无法满足任务一直 Pending。后来改成了软约束优先同机不行则跨机任务很快就跑起来了性能虽然有轻微下降但总比干等强。4.2 集群碎片化严重大量资源算得用不得碎片化是万卡集群运营的头号问题。表面上看集群利用率 70%但真正能用来跑大任务的有效容量可能不到一半。碎片化产生的原因主要有两个一是大量小任务把节点资源“啃”得七零八落二是调度策略长期不清理导致节点间资源分布严重不均。解法有两个层面日常运维层面设置定期碎片整理任务将小任务重新打散或合并释放出连续的整节点资源。这个操作需要调度器支持在线迁移任务或者至少支持在任务空闲窗口重启。调度策略层面对大任务做资源预订提前把整节点预留出来。这个方式效率最高但会牺牲一部分资源利用率两种策略需要平衡。有一次我们没有做碎片整理导致一个需要 64 卡的训练任务在集群里找遍了所有节点都对不上最后是人为干预、手动迁移了 5 个小任务才勉强凑齐 64 卡。从那以后我就养成了每周做一次碎片分析的习惯把资源分布图拉出来看一眼基本能做到心中有数。4.3 高优任务抢占低优任务后训练性能反而下降这个问题的典型场景是低优任务被驱逐后腾出来的资源零散分布在不同节点高优任务虽然拿到了足够数量的 GPU但这些 GPU 分布在多个节点上跨节点通信成了瓶颈反而比它等一等再运行更慢。这种情况的根源在于抢占只解决了资源数量问题没有解决资源质量问题。解决方案是在抢占时不仅要看数量还要评估资源组合的拓扑质量。如果腾出来的资源位置太差不如让高优任务继续等待等合适的节点集合空出来。对高优任务单独设置“拓扑质量阈值”低于阈值的资源宁可不要。这能避免“有卡就跑”的冲动型调度。4.4 团队自研调度器还是直接用开源方案每次聊到调度器都会有人问这个问题“我们要不要自己写一个调度器”我的态度一直很明确能用开源用开源只有被逼到墙角才自研。开源方案里Kubernetes Volcano/Kueue 的组合是目前生态最成熟的选择文档齐全、社区活跃能满足大部分训练场景的调度需求。如果对调度策略有更细粒度的要求可以基于 Volcano 做插件扩展这类成本远低于从零自研。自研调度器的合理场景只有几种对调度时延有极高要求ms 级别响应而开源方案达不到。集群规模大到开源方案出现性能瓶颈比如几万节点以上。有非常特殊的调度需求且无法通过扩展机制钩子实现。但即便是自研也建议复用以太坊社区或类似开源方案的成熟资源模型和调度框架不要从底层全部重写。调度器看起来简单真正做好极其复杂——坏节点管理、任务恢复、优先级抢占、公平性保障、资源碎片整理、拓扑感知每一个模块都够写半年的。5. 扩展思考调度器的未来与训练场景的演进聊完了当前的架构和实践再来说说这个领域接下来的方向。AI 训练的技术栈在快速演进调度器如果停留在“排班”这个角色上很快就不够用了。一个是异构计算的调度。现在很多团队已经不只用 GPU 做训练了加入了 NPU、TPU 甚至 FPGA 来做混合负载。怎么在一个集群里统一调度不同类型的加速卡并且让模型开发和底层硬件解耦这是调度器要面对的新课题。另一个是面向“持续训练”的调度模式。新一代大模型训练很多场景下不是“训练完就结束”而是持续在线上获取新数据做增量训练。这种任务与传统的批量训练任务差异巨大它要求调度器支持任务的动态伸缩、暂停恢复、甚至跨集群的实时迁移。目前这块的开源方案还不成熟更多是各家团队在各自封闭的体系里摸索。我个人判断未来两到三年AI Infra 领域里调度器的价值会进一步凸显。当模型训练的并行策略越来越复杂、训练任务对资源的约束条件越来越多、集群规模继续膨胀时调度器将是限制整个 AI 基础设施能力天花板的关键一环。谁能在调度层面做得更精细、更智能谁就能在算力成本上占据明显优势。再提一个小技巧收尾如果你现在还在用默认参数跑大规模集群建议先从调整队列和调度策略入手把任务的类型、优先级、资源约束梳理一遍制定合理的调度策略。这一项优化做完可能比升级一批新卡带来的收益更明显。一万张 GPU 能不能发挥出应有的价值很多时候不在硬件就在调度器那一条条看不见的规则里。