在前面的 20.1 和 20.2 节,我们完成了 GPU 驱动、CUDA、容器化环境的部署。此时,几十甚至上千张 GPU 已经就位,但它们还只是“散兵游勇”——如果没有调度系统,每一次训练任务都需要手工分配节点、手动启动进程、盯着日志看是否失败。这对于偶尔跑一次实验的小团队或许还能容忍,但对于需要同时支撑多个训练任务、多个团队共享集群的企业级算力中心来说,这简直是运维的噩梦。
集群调度系统的核心职责,就是把“用户提交的任务”和“集群的空闲资源”进行高效、公平且安全的匹配。在大模型场景下,这份工作远比传统互联网服务调度更复杂:一个训练任务可能独占数十甚至数百张 GPU,且动辄运行数周;一旦某个节点挂掉,整条流水线都可能瘫痪。因此,调度系统不仅决定“能不能跑”,更直接影响“跑得多稳”和“卡用得满不满”。
当前业界主流方案分为两条路线:一是继承自高性能计算(HPC)领域的 Slurm,二是云原生生态下的 Kubernetes + KubeRay。两者并非互斥,在真实算力中心中常以混合形态出现。
一、Slurm:高性能计算场景的“老牌劲旅”
Slurm(Simple Linux Utility for Resource Management)是 HPC 领域事实上的调度标准,全球 TOP500 超算中绝大多数都在使用它。它的设计哲学高度契合大模型训练对大规模、长时间、紧耦合并行的需求。
为什么大模型训练天然亲近 Slurm?
- 节点级独占分配:训练任务通常需要整机独占 GPU,避免与其他任务争抢计算核心和网络带宽。Slurm 可以轻松将一批节点“整机”分配给单个作业(Job),以
#SBATCH --nodes=4 --gpus-per-node=8这样的参数,就能把 4 台 8 卡机全部划给一个任务。 - 高可靠性守护:Slurm 的原生容错能力(如作业重排队、节点状态监控、Checkpoint 自动重新调度)能让长时训练在节点宕机后不至于从头开始。这对运行数周的千亿参数预训练是刚需。
- 亲和性与拓扑感知:在大模型分布式训练中,哪几张卡与哪个网卡近、哪些节点在同一个 InfiniBand 交换机下,对通信性能影响极大。Slurm 的拓扑感知调度可以根据用户指定的
--switches等参数,尽量把任务塞进同一交换机下,最大化 NVLink 和 InfiniBand 带宽利用率。 - 批作业脚本原生化:用户只需写一个 shell 脚本,头部注明资源需求(SBATCH 指令),
sbatch run_train.sh即可提交。这种“无侵入”风格非常适合研究型团队快速迭代。
实用部署要点:
- 控制节点高可用:Slurm 主控节点(Slurmctld)必须配置主备,否则主控宕机后所有队列调度都会停摆。通常使用
slurmctld双机配合虚拟 IP 做故障转移。 - GPU 资源识别:Slurm 自 20.02 版本后通过
gres.conf原生支持 GPU 调度。配置AutoDetect=nvml可自动识别节点上的 GPU 型号与数量,并在作业中自动设置CUDA_VISIBLE_DEVICES等环境变量。 - 与高速网络配合:对于 InfiniBand 网络,Slurm 可自动为作业分配网络隔离域,避免跨作业的通信干扰。需在
slurm.conf中声明SelectType=select/cons_tres和GresTypes=gpu等参数。 - 常见坑:Slurm 社区版默认对 Docker/容器化支持较弱(通常需额外配置
pam_slurm_adopt或使用 Pyxis 等插件)。如果你的训练环境已经全面容器化,单纯用 Slurm 会感觉“割裂”——这正是我们接下来要讨论 Kubernetes 路线入场的原因。
典型用户画像:以研究为核心、任务以大规模预训练为主、运维团队有 HPC 背景的算力中心,Slurm 是起步阻力最小的选择。
二、Kubernetes + KubeRay:云原生场景的调度方案
随着越来越多 AI 团队将工作流容器化,Kubernetes(K8s) 作为云原生编排的事实标准,自然也被引入了大模型训练与推理场景。但 Kubernetes 原生工作负载(Deployment、Job)都是为微服务或短任务设计的,根本无法直接承载“成百上千个 Pod 需要紧密协同通信”的分布式训练。于是,KubeRay 应运而生。
KubeRay 是 Ray 框架的 Kubernetes Operator,它专门为分布式计算任务(包括大模型训练)提供了“Kubernetes 上的集群管理器”。其核心组件 RayCluster 可以将大量计算 Pod 自动组成一个 Ray 集群,并在其上运行分布式训练代码。
为什么 K8s + KubeRay 是值得押注的方向?
- 统一基础设施:训练任务、推理服务、数据预处理、模型注册、监控组件全都可以跑在同一套 K8s 集群上,资源利用率更高,运维技术栈统一。
- 容器化原生:所有训练代码、依赖、环境变量全打包在镜像里,避免“在我机器上能跑”的环境一致性问题。Checkpoint 可以自动上传到对象存储。
- 弹性与混合调度:KubeRay 支持容错和动态扩缩(如 GCS 故障恢复),配合 K8s 的自动扩缩器,理论上可以实现训练任务的“弹性”调度(虽然实际中预训练很少弹性,但微调和推理任务大量受益)。
- 服务化友好:同一个模型训练完成后,直接通过 K8s Service 暴露推理 API,或转换为 Ray Serve 部署,省去从 HPC 集群向业务集群搬运权重的环节。
实用部署要点:
- 安装 KubeRay Operator:使用 Helm 一键部署
kuberay-operator,然后通过RayJob或RayServiceCRD 提交训练任务。一份典型的RayJobYAML 可指定所需 Ray Cluster 的头节点和工作节点配置,以及训练入口命令。 - GPU 分配:需安装 NVIDIA Device Plugin,并在 Pod spec 中声明
nvidia.com/gpu: 8来请求 GPU。同时建议配合gang-scheduling(如 Volcano 或 KubeRay 自身的调度特性)确保只有全部资源就绪时才启动 Pod,避免“部分卡到位部分卡阻塞”的死锁。 - 网络与通信:分布式训练要求 Pod 间的低时延高带宽通信。在 K8s 环境下,通常使用
hostNetwork: true或Macvlan等策略绕过 CNI 的性能损耗,直接暴露主机高速网卡(如 InfiniBand)。KubeRay 会为每个 Pod 注入 IP,并在 Ray 集群中配置好地址。 - 常见坑:K8s 的调度不如 Slurm 那样天然支持拓扑亲和。当任务需要“尽量将 Pod 调度到同一交换机下”时,需手动配置
topologySpreadConstraints或podAffinity,但这在大规模场景下容易与集群均衡调度策略冲突。许多团队会将 K8s 节点池按物理机架划 Zone,再通过节点选择器强制绑定。
典型用户画像:整个技术栈已容器化、拥有完善云原生基础设施、且需要频繁在“训练”和“推理服务”间切换的团队,KubeRay 是更现代的选择。
三、调度策略:资源隔离、优先级调度与队列管理
无论你选择 Slurm 还是 K8s,一个算力中心必须回答三个灵魂拷问:“谁可以先用?”“用多少?”“用不上怎么办?” 这对应着调度策略的三个核心模块。
1. 资源隔离
多团队共享集群时,必须避免“A 团队的训练任务占满了显存,导致 B 团队的推理服务 OOM”(Out of Memory)。大模型场景下的隔离分为三层:
- GPU 硬隔离:最好用的隔离就是物理独占。调度器应约束同一张 GPU 不能被两个算力密集型进程同时使用。Slurm 的
gres/gpu绑定和 K8s 的nvidia.com/gpu资源请求都能做到这一点。 - 网络隔离:对于 InfiniBand 网络,确保不同作业的流量在逻辑上分开(如使用不同 Partition Key),防止一个作业的密集通信塞满交换机。
- 存储 I/O 隔离:训练任务频繁读写 Checkpoint 会产生巨大 I/O 压力,可能拖慢同一存储上的推理或数据预处理任务。通过限定作业组的存储配额或单独挂载文件系统路径,可以在一定程度上缓解。
2. 优先级调度
不是所有任务都生而平等。预研实验和千亿参数的全量预训练如果同时卡在队列里,后者显然应该优先获得资源。常见的优先级策略包括:
- 静态优先级:为不同项目、不同团队预设优先级(Slurm 的
PriorityWeightAge和PriorityWeightQOS,K8s 配合 Volcano 等插件也可实现)。例如,给“生产级模型迭代”分配高权重,给“实习生实验”分配低权重。 - 动态优先级:根据任务运行时长、等待时间、资源利用率自动调整。一个通用的防饥饿策略是“配置公平份额(Fair Share)”:如果一个用户最近已经消耗了大量核心小时,其后续作业的优先级会被暂时降低,把资源让给排队较久的其他用户。
- 抢占式调度:在任务关键度差异极大的场景(如推理服务必须在线),低优先级训练任务可在高优推理负载请求资源时被“抢占”——即接收到 SIGTERM 信号后自行执行 Checkpoint 保存并退出,待资源闲时再重回队首重新启动。PyTorch 框架配合 Slurm 信号处理或 Ray 的弹性机制,可以基本实现无感抢占。
3. 队列管理
这是运维团队日常打交道最多的部分。典型设计模式如下:
- 多队列分级:
debug队列:限制最大运行时间 30 分钟,用于快速测试代码能否跑通,拥有最高调度优先级,但常备 2-4 张公用 GPU;normal队列:分配给常规微调、消融实验,单个作业最多申请 32 卡,时间上限 72 小时;large队列:分配给大模型全量预训练,无卡数上限但需经过审批,时间上限可设为 14 天;inference队列:分配给在线服务,优先保证资源,且禁用抢占。- 回填调度:在大型作业等待资源的同时,允许短小的任务“插队”运行,以提高集群总体利用率。前提是小任务必须在大型作业就绪前完成或主动退出,否则会阻碍大任务的启动。
- 资源预留:对需要定期运行的重要训练任务(如每周的数据清洗流水线),配置周期性资源预留窗口,确保到时一定有可用 GPU。
四、两种路线的协同共存
在实际中型以上算力中心,Slurm 与 KubeRay 往往同时存在,并通过策略进行“分工”:
- Slurm 管理裸金属节点:承接最核心的千卡级预训练任务,追求极致性能和确定性;
- K8s 管理容器化节点:承接微调实验、推理服务、数据预处理等更动态的工作负载,追求环境一致性和服务编排。
两者之间可通过对同一个物理 GPU 集群做“动态分区”实现:例如将 80% 的 GPU 节点物理划分为 Slurm 管理域,20% 加入 K8s 节点池;或使用 MIG(多实例 GPU) 技术将一张 A100 切分为多个小实例,分别供给两边。更高级的方案是引入 Run:ai、Volcano 等统一调度层,在 K8s 内部纳管 “Slurm 作业” 的提交与生命周期,但复杂度和成本也会明显上升。
最终的实用建议:
- 如果你的团队规模小于 20 人,GPU 总量不足 100 张,首选 Slurm。部署成本低,社区文档足,足够满足绝大多数训练场景。
- 如果你已经拥有成熟的 K8s 基础设施,且推理与训练混合频繁,直接上 KubeRay,可以复用现有运维能力和硬件。
- 无论选哪种,从第一天就建立好队列和资源限制。因为任何“先用起来以后再说”的宽容政策,都会在一两周后演变为“所有人都在抢卡,无人知道谁在算什么”的混乱。
在接下来的 20.4 节中,我们将讨论在调度系统之上,如何部署 PyTorch、DeepSpeed、Megatron-LM 等训练框架,让提交的作业真正跑成一次完整的模型训练。