跳转到内容

Slurm 与 Ray

上一章讲了 K8s 上的 GPU 调度,这一章讲两条替代路线:Slurm(HPC 世界三十年传统的王者)与 Ray(Python 原生的新贵)。很多大模型训练集群(包括多数超算与 Meta 级别的集群)跑在 Slurm 上,而不是 K8s——路线选择没有对错,只有场景匹配。

Slurm 是超算世界的标准调度器(Top500 超算的绝对主流),设计哲学与 K8s 完全不同:为批处理的大规模科学计算而生——作业是"申请 N 个节点跑一个脚本",整组分配、拓扑感知、公平份额都是原生的。

终端窗口
# 一个 8 节点训练作业
sbatch --nodes=8 --ntasks-per-node=8 --gpus-per-task=1 \
--cpus-per-task=16 train.sh
squeue -u $USER # 看队列
srun --jobid=... bash # 进去调试

Slurm 的原生优势(对比 K8s 的三层补丁):

能力SlurmK8s
整组分配(gang)原生(作业 = N 节点整组)要 Volcano/Kueue 补丁
拓扑感知原生(topology.conf 描述,块分配)要插件补丁
公平份额原生(fair-share 多级队列)要 Kueue 配
抢占/回填原生(preemption + backfill)要插件配
生态HPC 工具链(sacct 计费、pam 等)云原生全家桶(监控、网络、存储)

为什么大模型团队选 Slurm:训练集群的形态(大作业、整组、长时、公平共享)与 HPC 完全同构——Slurm 三十年积累的调度策略(公平、回填、抢占)K8s 还在补课。

Ray 的路线完全不同:不是"调度器",是"分布式计算框架 + 附带的调度"——Python 原生的任务/actor 模型,训练(Ray Train)、调参(Ray Tune)、推理(Ray Serve)、数据(Ray Data)一套全家桶。

import ray
ray.init(address="ray://head:10001")
@ray.remote(num_gpus=8) # actor 申请 8 卡
class Trainer: ...
# 整组申请:placement group
pg = ray.util.placement_group([{"GPU": 8}] * 8) # 8 节点 × 8 卡

Ray 的定位:

  • 强在 Python 生态的一体化:同一个集群跑训练、调参、推理、数据处理,API 全是 Python
  • 强在异构工作流:数据预处理 → 训练 → 评测的 DAG 编排天然支持
  • 弱在大规模作业调度:万卡级的 gang 公平、抢占、回填不如 Slurm 成熟;资源碎片与调度效率在超大规模下是已知挑战
场景推荐
大规模训练集群(万卡级、多团队共享、公平性要求高)Slurm(调度策略最成熟)
云原生环境、已有 K8s 运维体系K8s + Volcano/Kueue
研发迭代为主(调参、多实验、一体化工作流)Ray
推理服务为主K8s(服务形态天然匹配)
超算环境Slurm(没得选)

混合是常态:很多团队"训练用 Slurm(或裸机 + 简单调度)、推理与研发用 K8s/Ray"——按工作负载形态分流,而不是一刀切。

不管哪条路线,Gang Scheduling、抢占与回填与多租户配额是同一组问题:

  • gang:整组分配,凑不齐就等
  • 公平:多团队共享时的份额与优先级
  • 回填(backfill):大作业等待时,小作业填空闲——利用率与公平的核心手段

Slurm 原生都有;K8s 靠 Volcano/Kueue;Ray 在补。这些策略的原理比工具重要——下一章展开。

  • 调度器分配的资源要传给训练框架(节点列表、rank 编排、GPU 绑定):torchrun/DeepSpeed 的启动脚本由调度器生成
  • 训练容错的"踢节点、缩容重启"需要调度器配合(Slurm 的作业重排、K8s 的 elastic)
  • 故障检测(网络、GPU XID)上报给调度器,形成闭环
  • Slurm 为大规模批处理而生:gang、拓扑、公平份额、回填全部原生,是大模型训练集群的主流选择之一。
  • Ray 是 Python 原生计算框架:一体化生态(训练/调参/推理/数据)是强项,超大规模调度是弱项。
  • K8s 适合服务形态与云原生环境,训练调度靠 Volcano/Kueue 补课。
  • 混合是常态:训练 Slurm/裸机,服务 K8s,研发 Ray——按负载形态分流。
  • gang/公平/回填是共同问题,原理比工具重要。