训练作业排队:优先级、多队列与资源预留

FreeGuideOnline 最新 2026-06-28

训练作业排队:优先级、多队列与资源预留

在深度学习团队或研究机构中,GPU 资源总是稀缺的。当多个用户同时提交训练任务时,如果没有合理的调度机制,会导致资源争抢、任务饿死或利用率低下。训练作业排队 正是为解决这类问题而设计的一整套策略,其核心组件包括:优先级定义、多队列划分与资源预留。本文将带你从零理解这些概念,并给出可落地的配置思路。


为什么需要排队系统

裸用 GPU 服务器时,常见走两个极端:要么完全靠人工协商,要么任由任务争抢。前者效率低下,后者容易出现:

  • 一个用户抢占全部 GPU,其他人长期无法运行。
  • 小任务被大任务阻塞,实验迭代速度变慢。
  • 紧急训练无法及时获得资源。

一个良好的排队系统必须能够:

  1. 公平分配:防止资源被少数人垄断。
  2. 区分轻重缓急:让重要任务优先执行。
  3. 提高整体吞吐量:通过填满 GPU 空闲时间,减少闲置。
  4. 提供可预期的等待时间:让使用者知道大概多久能开始运行。

核心概念一:优先级

优先级决定了在资源紧张时,哪个作业可以先被调度。通常用一个数值或级别表示,例如 P0 > P1 > P2,或者 HIGH / NORMAL / LOW

如何定义优先级

不同组织有不同规则,常见维度:

  • 任务紧急性:例如线上模型热修复、赶论文截止日期、客户演示等,可设为最高级。
  • 资源体量:短时小任务(几分钟到半小时)可以给予更高优先级,快速轮转,提升体感。
  • 用户角色:管理员 > 核心研发 > 实习生,但容易引起争议,需与团队协商。
  • 项目重要性:公司战略项目可配置固定高优先级。

优先级的实现方式

调度器会维护一个优先级队列,高优先级作业总是在低优先级之前被考虑。但要注意:

  • 饥饿预防:如果一直有高优先级任务提交,低优先级可能永远得不到执行。需要搭配 “老化” 机制,等待时间越长,优先级动态提升。
  • 抢占:是否允许高优先级任务抢占正在运行的低优先级任务?
    • 非抢占式:简单,但对紧急任务不够友好。
    • 抢占式:需要保存检查点,优雅终止,稍后恢复或重新排队。该功能对训练作业尤其重要,因为训练往往耗时数天,抢占代价高。

示例配置(伪代码)

priority:
  levels: [urgent, high, normal, low]
  default: normal
  preemption: true           # 允许抢占
  aging:
    max_wait: 7200           # 最大等待秒数
    boost_factor: 1          # 每等待3600秒优先级提升一级

核心概念二:多队列

将所有任务丢进同一个队列是混乱的根源。多队列允许按组织、项目、资源类型或任务性质将提交入口隔离开来。

为什么需要多队列

  • 资源隔离:为不同团队或项目划分固定资源比例,互不干扰。
  • 策略差异:每个队列可绑定不同的优先级策略、资源限制和使用配额。
  • 管理清晰:运维能直观看到各队列的负载和排队情况,便于扩缩容决策。

常见队列设计模式

队列类型 说明 适用场景
部门队列 按算法组、工程组、数据组划分 中型团队,需独立核算资源
项目队列 项目 A、项目 B 各有一个队列 每个项目有固定预算
任务类型队列 交互式调试队列、训练队列、推理队列 隔离不同生命周期任务,避免调试占用训练资源
混合队列 组合以上维度 大型组织,资源精细化运营

队列配置要素

一个队列通常包含:

  • 资源配额:最多能用多少卡、多少内存。
  • 优先级范围:该队列允许设置的最高优先级。
  • 提交权限:谁可以向该队列提交作业。
  • 排队深度限制:避免过多任务堆积使调度器压力过大。

示例配置片段:

queues:
  - name: team-nlp-high
    resources:
      max_gpu: 16
      min_gpu: 4           # 预留最低保证
    priority: {max: urgent, default: high}
    acl: [nlp-core-members]
    max_pending_jobs: 20
  - name: debug
    resources:
      max_gpu: 4
      max_walltime: 3600  # 最大运行时间1小时
    priority: {max: normal, default: low}
    acl: [all_users]

通过将调试任务限制在独立队列并设置最大运行时间,可以防止调试 session 长时间占用 GPU。


核心概念三:资源预留

资源预留是指在调度系统中事先为特定队列或作业保留一定数量的 GPU,即便这些 GPU 当前空闲,其他作业也不能占用。这通常用于保障关键业务的资源可用性。

预留的类型

  1. 静态预留:在集群配置中硬性划分,例如总是为 NLP 团队保留 8 张 A100。优点是确定性高;缺点是当 NLP 团队不用时,资源白白浪费。
  2. 动态预留:仅在特定时间窗口或满足条件时生效。例如:每天 20:00-08:00 为特定项目预留资源,或者当队列深度达到一定阈值时启用预留。
  3. 弹性预留:允许低优先级任务使用,但一旦预留方提交任务,可抢占回去。结合抢占与优先级,是一种资源利用率的折中。

预留的表示形式

在配置系统中,通常用 reservationmin_guaranteed 标识:

queue:
  - name: production-training
    resources:
      guaranteed_gpu: 32    # 32卡保证可用,其他队列不可占用
      max_gpu: 64
    priority: urgent

同时,可以在集群层面设置一个 “资源共享池”,未被预留且未被使用的 GPU 供任何队列按优先级竞争。

预留的代价与策略

  • 预留过多会导致集群整体利用率偏低。
  • 建议先观察历史使用情况,然后逐步调整。
  • 结合超发策略:允许借用预留资源,但有明确的归还机制。

实际调度流程(以 Kubernetes + Volcano 为例)

许多团队使用 Kubernetes 搭配 Volcano、Yunikorn 或 Armada 来管理训练作业。一个典型的调度周期如下:

  1. 用户提交 Job,携带队列名称与优先级。
  2. 准入控制检查权限、配额与队列容量。
  3. 调度器将所有未调度作业按 队列优先级 → 作业优先级 → 提交时间 排序。
  4. 先尝试满足带有资源预留的队列需求,从总资源池扣除预留量。
  5. 按排序结果,将作业派发到满足其节点亲和性、资源要求的 GPU 节点。
  6. 若开启抢占且资源不足,按规则选择受害者,保存检查点,驱逐并重新调度。
  7. 闲置预留资源可被低优先级作业短暂使用(需开启借用)。

设计最佳实践

1. 优先级的颗粒度不宜过细

3-4 个级别(如 P0-P3)即可满足绝大多数场景。级别太多导致使用者困惑,且调度决策变复杂。

2. 强制执行最大运行时间

对每个队列设置 max_walltime,防止任务霸占资源。超时可自动终止或转为挂起,并通知用户保存检查点。

3. 可视化排队状态

为用户提供仪表板,展示:

  • 我所提交的任务在队列中的位置
  • 预计启动时间
  • 当前队列负载与已用/总量 GPU

透明性能极大缓解焦虑。

4. 定期审计与调整

资源分配不是一劳永逸的。每季度根据实际使用数据调整队列配额与预留比例,剔除僵尸队列,回收闲置资源。

5. 定义清晰的抢占策略

若允许抢占,要规定:

  • 只允许高优先级抢占低优先级正在运行的任务。
  • 只能抢占使用“借用”资源的任务。
  • 抢占比前发送宽限期通知(例如 15 分钟),让作业有机会保存进度。
  • 记录抢占事件,便于回溯。

6. 自动化回收

结合任务监控,当检测到 GPU 利用率持续偏低时,自动降级其优先级、发送报警或将其挂起,把资源让给真正需要的任务。


总结

  • 优先级解决了谁先运行的问题,防止资源被非关键任务挤占。
  • 多队列从架构层面实现资源隔离与策略划分,让管理更有序。
  • 资源预留为关键业务提供了兜底能力,避免因突发大流量导致重要训练延迟。

三者结合,便能构建一个既公平又高效、同时兼顾组织治理需求的训练作业排队系统。初学者在搭建时,建议先从简单的单队列+固定优先级开始,逐步引入多队列和预留,并在使用中依据反馈数据持续迭代优化。