3、调度系统概述:调度器作用、常见调度器(如Kubernetes、Slurm)简介
在GPU集群中,调度器是连接“用户提交的计算需求”与“底层硬件资源”之间的核心枢纽。没有调度器,集群中的GPU、CPU、内存和网络资源将陷入无序竞争:部分节点过载排队,而另一些节点闲置浪费。调度器的本质是一个资源分配决策引擎,它接收来自多个用户或应用的任务请求,依据预设策略,决定“哪个任务在何时、哪台机器的哪块GPU上运行”,并在运行过程中持续管理任务生命周期。
3.1 调度器的核心作用
调度器并非简单地“先来先服务”,它在集群中承担着四类关键职责:
- 资源抽象与视图维护:调度器持续跟踪集群中每台节点的可分配资源(GPU型号、显存、CUDA核心数、CPU核数、内存),并维护一个全局的资源拓扑视图。例如,某节点有8张A100,其中3张已被占用,调度器需精确记录剩余5张的显存分配状态。
- 任务排队与优先级裁决:当任务请求量超过可用资源时,调度器将任务放入队列,并根据优先级策略(如FIFO、公平共享、抢占式优先级)决定下一个被调度运行的任务。这是本课程后续章节的核心主题。
- 放置决策(Placement):为选中的任务选择最合适的节点和GPU设备。决策需考虑数据 locality(如训练数据是否已缓存在该节点)、GPU间互联拓扑(NVLink/PCIe)、避免碎片化等约束。
- 生命周期管理与故障处理:任务启动后,调度器监控其运行状态;若节点故障或GPU异常,调度器负责将任务迁移或重新排队,并释放已失效的资源占用。
3.2 常见调度器:Kubernetes 与 Slurm
当前GPU集群环境中最常遇到的两类调度器分别来自云原生生态和传统HPC生态,它们的设计哲学与适用场景有显著差异。
3.2.1 Kubernetes(K8s)调度器
Kubernetes 是容器编排的事实标准,其默认调度器 kube-scheduler 负责将 Pod(容器组)分配到合适的节点。对于GPU资源,K8s 通常结合 Device Plugin 机制将GPU作为可扩展资源(如 nvidia.com/gpu)进行声明和分配。
K8s 调度流程分为两个阶段:过滤(Filtering) 和 打分(Scoring)。过滤阶段排除不满足条件的节点(如GPU数量不足、显存不匹配、端口冲突);打分阶段对剩余节点按优先级函数排序,选出最优节点。GPU调度通常通过自定义调度器扩展(Scheduler Extender)或调度框架(Scheduling Framework)来增强,例如支持GPU显存精确匹配、MIG(多实例GPU)切分、拓扑感知分配(确保同一Pod内的多卡通过NVLink互联)。
# 在K8s中声明GPU资源请求示例
apiVersion: v1
kind: Pod
metadata:
name: gpu-training-pod
spec:
containers:
- name: trainer
image: pytorch/pytorch:2.1.0-cuda12.1
resources:
limits:
nvidia.com/gpu: 2 # 请求2张GPU
3.2.2 Slurm 调度器
Slurm(Simple Linux Utility for Resource Management)是传统高性能计算(HPC)和学术超算中心使用最广泛的作业调度器。它面向批处理作业和MPI并行任务设计,以分区(Partition)和节点(Node)为管理单元。用户通过 sbatch 提交作业脚本,通过 squeue 查看队列,通过 scancel 取消作业。
Slurm 的GPU管理通过 Gres(Generic Resource)插件实现,支持细粒度的GPU绑定和任务独占。其调度算法支持先进先出(FIFO)、公平树(Fair Tree)、抢占(Preemption)和回填(Backfill)——回填机制允许低优先级作业利用高优先级作业等待时留下的资源空洞运行,显著提升集群利用率。
# Slurm 提交GPU作业示例
#!/bin/bash
#SBATCH --job-name=deep_learning
#SBATCH --partition=gpu
#SBATCH --gres=gpu:4 # 申请4张GPU
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=4
#SBATCH --time=24:00:00
module load cuda/12.1
srun python train.py --config=config.yaml
3.3 两种调度器的对比与选型
| 维度 | Kubernetes | Slurm |
|---|---|---|
| 设计定位 | 云原生容器编排,微服务与AI训练统一调度 | HPC批处理作业,MPI/GPU密集型科学计算 |
| 任务单元 | Pod(一组容器) | 作业(Job),包含多个Step |
| GPU资源抽象 | Device Plugin + 扩展资源(nvidia.com/gpu) | Gres 通用资源(gres/gpu) |
| 优先级策略 | PriorityClass + 抢占(Preemption) | Priority 多队列 + 抢占 + 回填 |
| 典型场景 | 在线推理、微服务、CI/CD、混合负载 | 大规模分布式训练、分子动力学、气象模拟 |
| 调度决策粒度 | 节点级过滤+打分,支持自定义扩展 | 分区+节点+Socket级绑定 |
| 状态管理 | etcd 存储,控制器模式,声明式API | 集中式控制器,配置简单,无状态服务 |
3.4 调度器使用中的常见误区
- 误区一:认为调度器只是“排队器”。实际上,调度器的放置决策直接影响训练性能——例如将需要8卡NVLink通信的任务分散到不同物理机,会导致通信效率骤降。调度器必须感知GPU拓扑。
- 误区二:忽略调度器自身的可用性。K8s 的 kube-scheduler 和 Slurm 的 slurmctld 都是单点服务,需要高可用部署。若调度器宕机,整个集群将无法接收新任务,但已运行任务不受影响。
- 误区三:将优先级策略与资源配额混为一谈。优先级决定“谁先获得资源”,配额(Quota)决定“一个用户/团队最多能占用多少资源”。两者需要配合使用,否则高优先级用户可能无限挤占资源。
- 误区四:不监控调度延迟。当集群规模超过数百节点时,调度器本身的决策耗时可能成为瓶颈。K8s 默认调度器在Pod数量大时可能出现毫秒级到秒级的延迟,需通过性能调优或引入调度缓存解决。