04 算力资源调度
学习目标:能解释 AI 平台为什么需要队列、配额、优先级、抢占、GPU 共享和资源成本治理;能配置 Volcano/Kueue 队列和 Gang Scheduling;能选择合适的 GPU 共享方案。 重点度:需要会(15 分)
概述#
真实 AI Infra 场景中,GPU/NPU 不是给一个模型独占的——通常是多模型、多团队、多租户,训练、推理、评测任务共享同一批算力。
核心问题:如何让多模型、多任务、多团队高效共享 GPU/NPU 算力?K8s 默认调度器(kube-scheduler)是为在线服务设计的——每个 Pod 独立决策,倾向于均衡分布。AI 批处理作业有三个默认调度器不擅长的特点:
- All-or-Nothing:分布式训练需要 N 个 Pod 同时就位,少一个都不行
- 资源拓扑敏感:GPU 间有 NVLink/RDMA 等拓扑要求
- 队列和配额:多团队共享需要 HPC 级别的调度能力
这三件事任何一个都能让 GPU 集群利用率从 80% 掉到 40%。
算力资源基础#
GPU / NPU / DCU#
| 算力类型 | 代表产品 | 生态 |
|---|---|---|
| GPU(英伟达) | A100、H100、H200、B200 | CUDA + NCCL |
| NPU(昇腾) | Ascend 910B、950PR | CANN + 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):
| Profile | GPU 实例数 | 单实例显存 |
|---|---|---|
| 1g.10gb | 7 | 10 GB |
| 2g.20gb | 3 | 20 GB |
| 3g.40gb | 2 | 40 GB |
| 7g.80gb | 1 | 80 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 解决什么问题#
默认调度器的死锁场景:
作业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 定义#
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队列和配额#
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 | 长时间公平性(考虑历史使用量) |
抢占机制#
# 高优先级 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 的
preemptaction 按优先级和队列权重决定抢谁
Kueue:K8s SIG 官方方案#
四层对象模型#
ResourceFlavor(资源类别:A100/H100/spot/on-demand)
↓
ClusterQueue(配额池:定义谁能用多少 + borrowingLimit)
↓
LocalQueue(namespace 级入口:业务提交 Job 的界面)
↓
Workload(实际执行的 Job/RayJob/PyTorchJob)ResourceFlavor 定义:
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 定义:
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: LowerPriorityLocalQueue 定义(业务侧):
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
name: team-nlp-queue
namespace: team-nlp
spec:
clusterQueue: ai-team-1-cq # 指向 ClusterQueueKueue vs Volcano#
| 维度 | Kueue | Volcano |
|---|---|---|
| 定位 | 队列管理(admission gate) | 批调度器(替代 kube-scheduler) |
| API 风格 | 贴近原生 K8s(用原生 Job) | 自有 CRD(Volcano Job) |
| Gang Scheduling | all-or-nothing admission | 原生 PodGroup |
| 生态集成 | RayJob/Kubeflow/PyTorchJob/JobSet | Kubeflow/MPI/Spark |
| 跨集群 | MultiKueue | 无 |
| 成熟度 | v1beta2(SIG 项目) | 稳定多年 |
| 学习曲线 | 低(贴近 K8s 原生) | 中 |
借用与抢占#
Kueue 的 cohort 机制让同一组里的队列可以互借资源:
# ClusterQueue 的 borrowingLimit
resources:
- name: nvidia.com/gpu
nominalQuota: "4" # 保证配额
borrowingLimit: "8" # 最多借到 8
preemption:
reclaimWithinCohort: LowerPriority # 高优先级可抢回借出的资源借用场景:
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 配置#
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 资源无法组成整节点给大训练作业。
节点 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-aware | GPU 尽量调度到同一 NVSwitch 域 | 拓扑感知调度 |
Volcano binpack 配置:
# 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 Job | Ray 生态内的训练/调参任务 | 半(KubeRay 注入) |
| Karpenter | GPU 节点弹性伸缩 | 是 |
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"可观测"。
实战要点#
队列设计跟组织架构对齐:一个团队一个 Queue,而不是一个模型一个 Queue。团队内部资源分配由团队自管,跨团队走 Queue 配额。
Gang Scheduling 是必须的:没有它,高负载下 GPU 集群会陷入死锁——4 个 Worker 上去了等第 5 个,其他作业也起不来。Volcano 和 Kueue 都支持。
抢占必须配合 checkpoint:否则抢一次损失几小时训练进度。训练框架要支持周期性 checkpoint + 抢占前 preStop hook 保存。
GPU 共享有取舍:MIG 隔离强但规格固定;时间片灵活但无显存隔离;HAMi 折中且支持国产化。生产多租户用 MIG,开发测试用时间片/HAMi。
监控指标进 Grafana:pending_jobs、queue_usage、session_duration 是排障关键。GPU 利用率 < 20% 持续 30min 要告警(可能碎片化或僵尸任务)。
Kueue 的 cohort 借用要设上限:
borrowingLimit不设的话一个队列能借光所有资源,导致其他队列饿死。Topology-aware 调度对 NVLink 敏感场景必备:8 卡 TP 训练如果 GPU 分布在不同 NVSwitch 域,跨域带宽会掉 50%+。
资源池分层:生产推理(高优先级,不可抢占)、训练(中优先级,可抢占)、实验(低优先级,可抢占)分队列管理。
小结#
AI 算力调度的核心是解决 K8s 默认调度器处理不了的三件事:
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 语义约定。