4. 任务调度模型
大规模GPU集群的调度器本质上是一个有状态决策系统:它持续接收用户提交的任务,维护集群中每张GPU卡、每个节点的实时状态,并在每次调度事件触发时,从等待队列中选择一个或多个任务,为其分配满足约束的物理资源。理解这一模型,是后续优化调度策略的前提。
4.1 任务队列:从提交到运行的等待链
任务队列并非单一FIFO结构,而是由多个阶段组成的流水线。以Kubernetes + Volcano或Slurm为例,一个任务从提交到运行通常经历以下队列阶段:
- 提交队列(Submission Queue):用户通过CLI或API提交任务,调度器首先进行合法性校验(如镜像是否存在、配额是否充足),校验通过后进入等待队列。
- 调度等待队列(Pending Queue):这是调度器的主要决策对象。任务在此排队,等待满足资源条件且符合优先级策略的调度时机。
- 绑定队列(Binding Queue):调度器已为任务选定节点和GPU,但尚未完成实际资源预留。此阶段通常极短,但高并发下可能出现竞争冲突。
- 运行态(Running):任务已获得GPU资源,进入执行阶段。此时任务从队列中移除,但调度器仍需监控其状态(如异常退出、抢占、驱逐)。
实际系统中,队列往往按优先级和资源类型进一步细分。例如,Volcano支持多个Queue,每个Queue可配置不同的share(资源份额)和cap(上限),实现多租户间的资源隔离与公平调度。
4.2 资源模型:从单卡到多维拓扑
GPU集群的资源模型远比CPU内存复杂,调度器必须理解多维资源属性和拓扑关系。一个完整的资源模型包含以下层次:
| 资源层次 | 关键属性 | 调度决策影响 |
|---|---|---|
| GPU卡 | 显存容量、计算能力(如FP16 TFLOPS)、显存带宽、NVLink连接数 | 决定单任务能否放置,以及多卡通信效率 |
| 节点(Host) | GPU数量、CPU核数、内存、PCIe拓扑、GPU间互联(NVLink/PCIe) | 决定任务是否适合集中放置在同一节点 |
| 机架/集群 | 网络带宽(如RDMA 200Gbps)、交换机层级、故障域 | 影响分布式训练任务的通信开销和容灾能力 |
| 资源属性标签 | GPU型号(A100/H100)、显存大小、驱动版本、MIG配置 | 用于精确匹配任务需求,避免资源碎片 |
在调度器内部,资源通常以资源对象(Resource Object)的形式组织。以Kubernetes为例,每个节点的GPU资源通过Extended Resource(如nvidia.com/gpu)上报,调度器通过Node对象的allocatable和capacity字段感知可用资源。但仅凭数量是不够的——调度器还需要知道哪些GPU卡在物理上是相邻的,以便为需要多卡通信的任务分配同一NVLink域内的卡。
# 节点资源上报示例(Kubernetes Node Status)
status:
capacity:
nvidia.com/gpu: "8"
cpu: "128"
memory: "1024Gi"
allocatable:
nvidia.com/gpu: "8"
cpu: "120"
memory: "960Gi"
# 拓扑信息通过Node Label或CRD扩展
labels:
gpu-type: "A100-SXM4-40GB"
nvlink-domain: "domain-1"
对于更细粒度的拓扑感知,调度器需要维护一张GPU拓扑矩阵,记录每对GPU之间的通信带宽或跳数。例如,8卡A100节点内,GPU0-GPU3与GPU4-GPU7分别位于两个NVSwitch域,跨域通信带宽减半。调度器在分配4卡任务时,应优先选择同一域内的4张卡。
4.3 调度决策过程:从过滤到打分
调度决策过程通常分为两个阶段:过滤(Filtering)和打分(Scoring)。这一设计借鉴了Kubernetes调度框架,也被Volcano、YuniKorn等批量调度器采用。
4.3.1 过滤阶段(Predicates)
过滤阶段是硬约束检查,排除不满足条件的节点。常见过滤条件包括:
- 资源充足性:节点剩余GPU数、显存、CPU、内存是否满足任务请求。
- 标签匹配:任务要求的GPU型号、驱动版本、MIG配置是否与节点标签一致。
- 拓扑约束:任务请求的GPU数量是否能在同一NVLink域内满足;若任务要求全对等通信,则需检查节点GPU拓扑是否支持。
- 亲和/反亲和:任务是否要求与某些任务同节点(如PS架构的参数服务器与worker),或禁止与某些任务共存(如显存敏感型任务)。
- 污点/容忍:节点是否被标记为专用(如仅运行高优先级任务),任务是否容忍该污点。
过滤阶段的目标是快速缩小候选集,通常毫秒级完成。若所有节点均被过滤,则任务保持Pending状态,等待资源释放或新节点加入。
4.3.2 打分阶段(Priorities)
打分阶段对通过过滤的节点进行软性排序,选择最优节点。打分策略直接影响集群的碎片率和任务完成时间。常见打分项包括:
- 资源均衡:优先选择资源利用率更均衡的节点,避免某节点GPU满载而CPU空闲。
- 拓扑紧凑性:优先选择能将任务GPU分配在同一NVLink域或同一PCIe Switch下的节点,减少通信开销。
- 装箱(Bin-packing):优先选择剩余资源最少的节点,提高资源利用率,但可能增加热点风险。
- 反装箱(Spread):优先选择剩余资源最多的节点,降低故障影响面,但可能增加碎片。
- 任务亲和:若任务与已运行任务有数据交互需求,优先选择同节点或同机架。
打分阶段通常采用加权多目标方式,例如:最终得分 = 0.4×资源均衡 + 0.3×拓扑紧凑 + 0.3×装箱。权重需要根据集群实际负载特征动态调整。
4.3.3 决策执行与绑定
选定节点后,调度器执行绑定(Bind)操作:将任务与节点关联,更新节点资源余量,并通知节点上的Agent(如kubelet)启动容器。这一过程必须保证原子性——若两个调度器实例同时绑定同一资源,会导致资源超卖。因此,调度器通常采用乐观锁或分布式锁(如Etcd事务)确保一致性。
func Schedule(task):
candidates = []
for node in cluster.Nodes:
if Filter(task, node): # 硬约束
candidates.append(node)
if len(candidates) == 0:
return PENDING
bestNode = None
bestScore = -inf
for node in candidates:
score = Score(task, node) # 软约束加权
if score > bestScore:
bestScore = score
bestNode = node
Bind(task, bestNode) # 原子绑定
return RUNNING
4.4 案例:多租户场景下的调度决策
假设某集群有2个节点:Node-A(8×A100 40GB,NVLink全互联)和Node-B(8×A100 40GB,但GPU0-3与GPU4-7分属两个NVSwitch域)。当前状态:
- Node-A:已用4卡,剩余4卡(GPU4-7),显存余量160GB。
- Node-B:已用6卡,剩余2卡(GPU6-7),显存余量80GB。
此时两个任务同时到达:
- 任务X:需4卡,要求全对等通信(AllReduce),优先级高。
- 任务Y:需2卡,无特殊拓扑要求,优先级低。
调度过程分析:
- 过滤阶段:任务X要求4卡且全对等,Node-B剩余2卡不满足,被过滤;Node-A剩余4卡且在同一NVLink域内,通过。任务Y在Node-A和Node-B均通过。
- 打分阶段:任务X仅Node-A可选,直接绑定。任务Y打分时,Node-A剩余0卡(被X占用),Node-B剩余2卡,因此绑定Node-B。
- 结果:Node-A满载,Node-B满载,无碎片。若任务X先到,任务Y后到,则任务Y只能等待Node-B释放或Node-A新资源。
此案例展示了拓扑感知如何避免“4卡任务被拆到两个域”的性能灾难,也体现了优先级在资源竞争中的决定性作用。
4.5 常见误区与复盘
调度决策模型是集群资源管理的核心抽象。理解任务队列的分阶段流转、资源模型的多维拓扑属性、以及“过滤-打分-绑定”的决策流水线,是后续优化调度策略(如抢占、回填、弹性调度)的基础。实际生产环境中,调度器还需处理故障重试、任务依赖、数据本地性等复杂场景,但上述三要素构成了所有调度策略的公共骨架。