任务调度基础:调度器角色、调度策略分类、常见调度算法
在多租户AI训练平台中,任务调度器是连接“资源供给”与“训练需求”的核心枢纽。它不直接参与模型计算,却决定了每一块GPU、每一GB显存何时、被谁、以何种方式使用。理解调度器的角色、策略分类与算法原理,是后续进行资源配额设计、优先级调优和故障恢复的前提。
4.1 调度器的角色与职责边界
调度器在系统中的位置介于“任务提交层”与“资源管理层”之间。其核心职责可以归纳为三个动作:感知、决策、绑定。
- 感知(Perception):持续收集集群中所有节点的实时状态,包括GPU型号、显存占用、算力负载、网络拓扑、节点健康状态等。同时维护一个等待队列,记录所有已提交但尚未运行的任务及其资源请求(如GPU数量、显存大小、是否需要RDMA)。
- 决策(Decision):根据预设的调度策略(如公平性、优先级、吞吐优先),从等待队列中挑选下一个可运行的任务,并为其选定一组具体的物理资源(例如:节点A的GPU 0、1,节点B的GPU 3)。
- 绑定(Binding):将决策结果下发给资源管理器(如Kubernetes的kubelet或YARN的NodeManager),完成容器或Pod的创建与启动,并更新集群资源视图。
需要特别强调的是,调度器不负责任务执行过程中的细粒度监控(如loss曲线、吞吐波动),也不负责GPU显存内的数据搬运。它只做“资源分配”这一件事,但这件事的成败直接决定了集群的整体利用率与任务完成时间。
4.2 调度策略分类
调度策略决定了“下一个任务选谁”。根据多租户场景下的不同诉求,策略可分为三大类:资源效率优先型、公平保障型、业务优先级型。
| 策略类别 | 核心目标 | 典型适用场景 | 潜在代价 |
|---|---|---|---|
| 资源效率优先 | 最大化集群吞吐量,减少资源碎片 | 离线批量训练、实验网格搜索 | 低优先级任务可能被长时间饥饿 |
| 公平保障 | 各租户按权重获得资源,避免垄断 | 多个业务部门共享集群 | 整体吞吐可能略低于贪心策略 |
| 业务优先级 | 保证核心在线训练任务的SLA | 核心推荐模型每日定时更新 | 需要配合抢占与驱逐机制 |
在实际系统中,单一策略往往不够用。生产级调度器通常采用多级策略组合:先按优先级队列粗筛,再在队列内部按公平性排序,最后通过资源适配算法完成节点选择。
4.3 常见调度算法原理与对比
算法是调度策略的具体实现。以下三种算法在多租户AI平台中最常见,需要深入理解其优缺点。
4.3.1 先来先服务(FCFS)与短作业优先(SJF)
FCFS 按任务提交时间排序,实现简单,但容易导致“大任务阻塞小任务”。SJF 按预估执行时间排序,能降低平均等待时间,但AI训练任务的执行时间极难预估(受数据量、模型收敛速度影响),因此SJF在AI场景中很少直接使用,更多是作为一种理论参照。
4.3.2 容量公平调度(Dominant Resource Fairness, DRF)
DRF 是Mesos和YARN中广泛采用的算法。其核心思想是:看每个任务对多种资源(CPU、GPU、内存)的“主导份额”。例如,租户A的任务需要2个GPU和8GB内存,租户B的任务需要1个GPU和16GB内存。假设集群共有10个GPU和80GB内存,则A的主导资源是GPU(占20%),B的主导资源是内存(占20%)。DRF会动态调整分配,使得所有租户的主导资源份额尽可能相等。
// 伪代码示意:DRF计算主导份额
for each tenant t:
share[t] = max( used_gpu[t]/total_gpu, used_mem[t]/total_mem )
select tenant with minimal share[t] to schedule next
DRF的优点是公平性较好,且能适应多维资源请求。缺点是计算复杂度略高,且在小规模集群(如8卡机)上,由于资源种类少,其优势不明显。
4.3.3 Gang 调度(All-or-Nothing)
分布式训练(如TensorFlow PS架构或PyTorch DDP)要求所有worker必须同时启动,否则任何一个worker单独运行都会导致训练阻塞或超时。Gang调度正是为此设计:只有当任务所需的全部资源都可用时,才一次性分配并启动所有Pod。
实现Gang调度通常采用“资源预留+超时回退”机制:
- 任务提交时声明需要的Pod数量与每Pod资源。
- 调度器尝试为所有Pod寻找可用节点,若部分Pod无法满足,则整个任务进入等待状态,不分配任何资源。
- 为避免死锁(多个大任务互相等待资源),需设置等待超时,超时后按优先级或提交时间进行“抢占式”资源回收。
4.4 案例:多租户集群中的调度策略配置
假设某公司有一个32卡GPU集群(4台8卡A100节点),两个租户:算法部(权重3)和数据平台部(权重1)。算法部提交了一个需要16卡(2个节点)的分布式训练任务,数据平台部提交了4个需要4卡的单机任务。
如果采用纯FCFS,算法部的16卡任务先提交,则它会占用2个节点,剩余2个节点供数据平台部使用,资源利用率高,但数据平台部的任务必须等待算法部释放资源后才能扩容。如果采用DRF,调度器会计算:算法部主导资源为GPU(16/32=50%),数据平台部若先调度4个4卡任务,其GPU份额为16/32=50%,两者相等,因此可以同时调度,但算法部的任务需要等待数据平台部的部分任务完成才能凑齐16卡。
最终推荐配置为“优先级队列+DRF权重”:
# Volcano调度器配置示意
queue: algorithm
weight: 3
capability: 24 # 最多占用24卡
queue: data-platform
weight: 1
capability: 16
这样,算法部的任务在提交后,由于权重高,会优先获得资源;但受capability限制,不会完全饿死数据平台部。同时,算法部的16卡任务通过Gang调度保证所有worker同时启动。
4.5 误区与复盘
- 误区一:调度策略越复杂越好。实际上,复杂的调度算法(如基于预测的强化学习调度)在工程落地时,其收益往往被调度延迟和状态同步开销抵消。建议从DRF或加权公平队列开始,逐步优化。
- 误区二:只关注调度器本身,忽略资源回收。调度不只是“分配”,还包括“回收”。如果任务结束后资源释放不及时(如容器残留、显存未归还),再好的调度算法也会失效。务必监控资源回收链路。
- 误区三:所有任务都要求Gang调度。对于单机训练或容错性强的任务(如支持弹性训练的框架),Gang调度反而会降低资源利用率。应根据任务类型动态选择调度策略。
复盘一次真实故障:某团队启用了Gang调度,但未设置超时回退。当两个16卡任务同时提交时,每个任务都等待对方释放资源,形成死锁,集群利用率降至0%。解决方案是设置gang-schedule-timeout=30s,超时后低优先级任务被回退,高优先级任务获得资源。