路线图

04 算力资源调度

星辉 2026-07-02 阅读 5 min 1,051 字 路线图
04 算力资源调度 封面

学习目标:能解释 AI 平台为什么需要队列、配额、优先级、抢占、GPU 共享和资源成本治理;能配置 Volcano/Kueue 队列和 Gang Scheduling;能选择合适的 GPU 共享方案。 重点度:需要会(15 分)


概述#

真实 AI Infra 场景中,GPU/NPU 不是给一个模型独占的——通常是多模型、多团队、多租户,训练、推理、评测任务共享同一批算力。

text
核心问题:如何让多模型、多任务、多团队高效共享 GPU/NPU 算力?

K8s 默认调度器(kube-scheduler)是为在线服务设计的——每个 Pod 独立决策,倾向于均衡分布。AI 批处理作业有三个默认调度器不擅长的特点:

  1. All-or-Nothing:分布式训练需要 N 个 Pod 同时就位,少一个都不行
  2. 资源拓扑敏感:GPU 间有 NVLink/RDMA 等拓扑要求
  3. 队列和配额:多团队共享需要 HPC 级别的调度能力

这三件事任何一个都能让 GPU 集群利用率从 80% 掉到 40%。


算力资源基础#

GPU / NPU / DCU#

算力类型代表产品生态
GPU(英伟达)A100、H100、H200、B200CUDA + NCCL
NPU(昇腾)Ascend 910B、950PRCANN + HCCL
DCU(海光)深算系列ROCm 兼容

单卡 / 多卡 / 多机#

  • 单卡:一张 GPU 跑一个模型,中小模型(7B-13B)的常见场景
  • 多卡:同机多张 GPU 通过 NVLink 互联,TP 切分大模型
  • 多机:跨节点通过网络(InfiniBand/RoCE)通信,PP 或超大训练

GPU 共享方案#

方案隔离性灵活性适用场景
整卡独占(默认)大模型训练和推理
MIG(Multi-Instance GPU)物理隔离固定规格A100/H100 多租户
时间片(Time-Slicing)弱(无显存隔离)灵活开发测试、小推理
MPS(Multi-Process Service)灵活同租户多推理进程
HAMi(GPU 共享)vGPU 隔离灵活K8s 原生 GPU 共享

MIG 切分规格(A100 80GB):

ProfileGPU 实例数单实例显存
1g.10gb710 GB
2g.20gb320 GB
3g.40gb240 GB
7g.80gb180 GB(整卡)

MIG vs Time-Slicing vs MPS 选型

  • 强隔离 + 固定规格 → MIG
  • 灵活 + 无显存隔离要求 → Time-Slicing
  • 同租户多进程并发 → MPS
  • K8s 原生 + vGPU 隔离 + 国产化 → HAMi

Volcano:K8s 原生批调度#

核心概念#

Volcano 把 HPC 几十年的调度经验搬到 K8s:

  • PodGroup:一组 Pod 的集合,Gang Scheduling 的基本单元
  • Queue:定义配额、权重、优先级
  • Job(vcjob):Volcano 自己的作业 CRD
  • Gang Scheduling:所有 Pod 一起调度或都不调度(all-or-nothing)
  • Session:调度器的一个调度周期(默认 1 秒)
  • Action:enqueue / allocate / preempt / backfill / reclaim

Gang Scheduling 解决什么问题#

text
默认调度器的死锁场景:
  作业A需要4个Worker,集群剩3个GPU空位
  作业B需要3个Worker
  → kube-scheduler 把A的3个Pod调上去,占住GPU干等
  → B一个都调不上去
  → A的第4个Worker永远等不到 → 死锁!

Gang Scheduling:
  A要4个但凑不齐 → 一个都不调
  B要3个正好有 → 全部调度
  A等B完成后释放资源再调

Volcano Job 定义#

yaml
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: llm-training-job
spec:
  minAvailable: 4              # Gang Scheduling:至少 4 个 Pod 就位才能开跑
  schedulerName: volcano        # 使用 Volcano 调度器
  queue: ai-team-1              # 提交到哪个队列
  policies:
    - event: PodEvicted
      action: RestartJob        # Pod 被驱逐时重启整个 Job
  tasks:
    - replicas: 4
      name: worker
      template:
        spec:
          containers:
            - name: pytorch
              image: pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime
              resources:
                limits:
                  nvidia.com/gpu: 8    # 每个 Pod 8 卡
                  cpu: "64"
                  memory: "512Gi"
          restartPolicy: OnFailure

队列和配额#

yaml
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
  name: ai-team-1
spec:
  weight: 2                   # 队列权重(proportion 算法用)
  reclaimable: true           # 资源可被抢占回收
  capability:
    nvidia.com/gpu: "64"      # 队列上限
  guarantee:
    resource:
      nvidia.com/gpu: "16"    # 队列保证资源(不会低于这个)

字段含义

  • weight:队列权重,proportion 算法按权重比例分配总资源
  • guarantee:保证资源(保底),即使其他队列抢也拿不走
  • capability:上限,这个队列最多用多少
  • reclaimable:空闲资源能否被其他队列借用

三种份额算法#

插件算法适用场景
proportion按 weight 比例分配多团队公平分配
drf主导资源公平(Dominant Resource Fairness)资源类型异构(GPU+CPU+内存混合)
fairshare经典 HPC fair share长时间公平性(考虑历史使用量)

抢占机制#

yaml
# 高优先级 Job 可以抢占低优先级 Job
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000
preemptionPolicy: PreemptLowerPriority
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority
value: 100
preemptionPolicy: Never

抢占注意事项

  • 能被打断的任务必须有 checkpoint 机制
  • 抢占是"杀 Pod"不是"暂停 Pod",被杀的进度丢失
  • Volcano 的 preempt action 按优先级和队列权重决定抢谁

Kueue:K8s SIG 官方方案#

四层对象模型#

text
ResourceFlavor(资源类别:A100/H100/spot/on-demand)
ClusterQueue(配额池:定义谁能用多少 + borrowingLimit)
LocalQueue(namespace 级入口:业务提交 Job 的界面)
Workload(实际执行的 Job/RayJob/PyTorchJob)

ResourceFlavor 定义

yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: h100
spec:
  nodeLabels:
    nvidia.com/gpu.product: "NVIDIA-H100-80GB-HBM3"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: a100
spec:
  nodeLabels:
    nvidia.com/gpu.product: "NVIDIA-A100-SXM4-80GB"

ClusterQueue 定义

yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: ai-team-1-cq
spec:
  cohort: ai-org              # 同 cohort 内可互借资源
  flavorFungibility:          # flavor 间能否互借
    whenCanBorrow: Borrow
    whenCanPreempt: Borrow
  resources:
    - name: nvidia.com/gpu
      flavors:
        - name: h100
          resources:
            nominalQuota: "4"       # 保证配额
            borrowingLimit: "8"     # 最多借到 8
        - name: a100
          resources:
            nominalQuota: "16"
            borrowingLimit: "16"
  preemption:
    reclaimWithinCohort: LowerPriority  # 高优先级可抢回借出的资源
    withinClusterQueue: LowerPriority

LocalQueue 定义(业务侧):

yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  name: team-nlp-queue
  namespace: team-nlp
spec:
  clusterQueue: ai-team-1-cq    # 指向 ClusterQueue

Kueue vs Volcano#

维度KueueVolcano
定位队列管理(admission gate)批调度器(替代 kube-scheduler)
API 风格贴近原生 K8s(用原生 Job)自有 CRD(Volcano Job)
Gang Schedulingall-or-nothing admission原生 PodGroup
生态集成RayJob/Kubeflow/PyTorchJob/JobSetKubeflow/MPI/Spark
跨集群MultiKueue
成熟度v1beta2(SIG 项目)稳定多年
学习曲线低(贴近 K8s 原生)

借用与抢占#

Kueue 的 cohort 机制让同一组里的队列可以互借资源:

yaml
# ClusterQueue 的 borrowingLimit
resources:
  - name: nvidia.com/gpu
    nominalQuota: "4"       # 保证配额
    borrowingLimit: "8"     # 最多借到 8

preemption:
  reclaimWithinCohort: LowerPriority  # 高优先级可抢回借出的资源

借用场景

text
cohort: ai-org
  ├─ team-nlp (nominal=16, 借用上限=8) → 最多用 24
  ├─ team-recommend (nominal=16, 借用上限=8) → 最多用 24
  └─ team-research (nominal=8, 借用上限=16) → 最多用 24

team-nlp 空闲 → team-research 可借 team-nlp 的资源
team-nlp 突然有任务 → 抢回借出的资源(LowerPriority 的被杀)

生产建议

  • reclaimWithinCohort: LowerPriority(不要用 Any,避免低优先级被反复抢占)
  • 能被打断的任务必须有 checkpoint 机制
  • 借用上限要设,否则一个队列能借光所有资源

HAMi:GPU 共享虚拟化#

HAMi(Heterogeneous AI Computing Virtualization Middleware)是在 K8s 中实现 GPU 显存和算力隔离的中间件,支持 vGPU 级别的资源切分。

HAMi 核心能力#

  • 显存隔离:每个 vGPU 限制独立显存,互不影响
  • 算力隔离:限制 vGPU 的 SM 利用率百分比
  • 国产化支持:支持英伟达、昇腾、海光等异构 GPU
  • K8s 原生:通过 Device Plugin 注入,业务无感知

HAMi Pod 配置#

yaml
apiVersion: v1
kind: Pod
metadata:
  name: vllm-shared
spec:
  containers:
    - name: vllm
      image: vllm/vllm-openai:latest
      resources:
        limits:
          nvidia.com/gpu: 1              # 请求 1 张 GPU
          nvidia.com/gpumem: "8000"      # 限制 8GB 显存
          nvidia.com/gpucores: "30"      # 限制 30% 算力
      command:
        - vllm
        - serve
        - Qwen/Qwen3-8B
        - --gpu-memory-utilization
        - "0.9"

适用场景

  • 一张 GPU 跑多个小推理服务(7B 模型 8GB 显存足够)
  • 开发测试环境多用户共享 GPU
  • Jupyter Notebook 多租户

HAMi vs MIG 的选择

  • 需要强隔离(生产多租户)+ A100/H100 → MIG
  • 需要灵活切分 + 任意 GPU → HAMi
  • 国产化场景 → HAMi(MIG 不支持国产芯片)

资源碎片治理#

碎片怎么产生#

小 Pod(Notebook、CI)被 binpack 到节点的角落,剩下的 GPU 资源无法组成整节点给大训练作业。

text
节点 A(8 卡):
  Pod 1 占 1 卡(Notebook)
  Pod 2 占 1 卡(CI)
  剩余 6 卡 → 整卡训练作业需要 8 卡 → 放不下

节点 B(8 卡):
  全空 → 能放 8 卡作业

治理手段#

手段思路实现
分离调度小任务走独立队列,大任务走专用队列Volcano Queue + ResourceFlavor
Binpack优先把小任务塞满已占用的节点Volcano binpack 插件
Anti-affinity小 Pod 分散到不同节点,保留整机可用性Pod anti-affinity
Preemption需要整机的大作业可以抢占碎片任务PriorityClass + 抢占
Topology-awareGPU 尽量调度到同一 NVSwitch 域拓扑感知调度

Volcano binpack 配置

yaml
# volcano-scheduler-configmap
scheduler_config: |
  actions: "enqueue, allocate, backfill"
  tiers:
    - plugins:
        - name: gang
        - name: binpack
          arguments:
            binpack.weight: 10
            binpack.cpu: 1
            binpack.memory: 1
            binpack.resources: nvidia.com/gpu
            binpack.resources.nvidia.com/gpu: 8

其他调度方案#

方案适用场景K8s 原生
Slurm传统 HPC 集群,非 K8s 环境
Ray JobRay 生态内的训练/调参任务半(KubeRay 注入)
KarpenterGPU 节点弹性伸缩

Slurm vs Volcano

  • 已有 HPC 集群 + Slurm 运维经验 → 继续用 Slurm
  • K8s 原生 + 容器化 → Volcano/Kueue
  • 混合栈:Slurm 管训练,K8s+Volcano 管推理

Karpenter + GPU

  • 按需拉起 GPU 节点(Spot Instance 降本)
  • 空闲时自动释放节点
  • 配合 Kueue 实现作业级弹性

关于 K8s GPU 节点管理(Device Plugin、GPU Operator、MIG 配置、DCGM 监控),参见 K8s 路线 Ch5"GPU 调度"DevOps 路线 Ch4"可观测"


实战要点#

  1. 队列设计跟组织架构对齐:一个团队一个 Queue,而不是一个模型一个 Queue。团队内部资源分配由团队自管,跨团队走 Queue 配额。

  2. Gang Scheduling 是必须的:没有它,高负载下 GPU 集群会陷入死锁——4 个 Worker 上去了等第 5 个,其他作业也起不来。Volcano 和 Kueue 都支持。

  3. 抢占必须配合 checkpoint:否则抢一次损失几小时训练进度。训练框架要支持周期性 checkpoint + 抢占前 preStop hook 保存。

  4. GPU 共享有取舍:MIG 隔离强但规格固定;时间片灵活但无显存隔离;HAMi 折中且支持国产化。生产多租户用 MIG,开发测试用时间片/HAMi。

  5. 监控指标进 Grafana:pending_jobs、queue_usage、session_duration 是排障关键。GPU 利用率 < 20% 持续 30min 要告警(可能碎片化或僵尸任务)。

  6. Kueue 的 cohort 借用要设上限borrowingLimit 不设的话一个队列能借光所有资源,导致其他队列饿死。

  7. Topology-aware 调度对 NVLink 敏感场景必备:8 卡 TP 训练如果 GPU 分布在不同 NVSwitch 域,跨域带宽会掉 50%+。

  8. 资源池分层:生产推理(高优先级,不可抢占)、训练(中优先级,可抢占)、实验(低优先级,可抢占)分队列管理。


小结#

AI 算力调度的核心是解决 K8s 默认调度器处理不了的三件事:

text
All-or-Nothing → Gang Scheduling(Volcano/Kueue)
队列和配额   → Queue + Quota + Priority
GPU 共享     → MIG / Time-Slicing / MPS / HAMi

选型建议:

  • 纯 AI 训练、K8s 原生 → Volcano
  • 想要 upstream 方案、API 贴近原生 → Kueue
  • GPU 共享 → HAMi(国产化友好)
  • 传统 HPC 集群 → Slurm
  • GPU 节点弹性 → Karpenter

核心工程决策:

  • 队列跟组织架构对齐(团队级,不是模型级)
  • Gang Scheduling 必须开
  • 抢占必须配 checkpoint
  • GPU 共享按隔离需求选(MIG 强隔离 / HAMi 灵活 / Time-Slicing 测试)
  • 资源碎片治理用 binpack + 分离调度

下一章进入 线上稳定性与可观测——AI Gateway、限流熔断、弹性伸缩、OpenTelemetry GenAI 语义约定。