L1.4 资源调度与编排
三维坐标
layer: L1(计算与编排)|level: Senior|pillar: 硬件架构上一篇我们把单台 GPU 服务器的物理形态(NVLink / NUMA / PCIe 拓扑)讲透了。本篇要解决的是集群尺度的核心矛盾:一堆异构 GPU 节点摆在那里,怎么把一个分布式训练作业「调对」(拓扑友好、Gang 原子)并「用满」(隔离 + 利用率核算)。这是 AI Infra 工程师从「会跑单机」迈向「会管集群」的分水岭。
学习目标
- 前置知识:读过 L1.7 K8s 核心对象(Pod/Service/YAML 基础)与 L1.2/L1.3(知道 NVLink/RDMA 带宽量级、单机 GPU 服务器的 NUMA/PCIe 拓扑);用过基础
kubectl、能看懂一份 YAML;知道「分布式训练要多卡同时通信」即可。GPU 节点部署见 L1.9 GPU Operator;Slurm/Ray 提交见 L1.10。无需写过调度器,也无需自有 GPU 集群。 - 学完产出:① 能画出 K8s GPU 调度全栈链路(device-plugin → kubelet → 调度器 → Gang 插件),说清每一跳「在解决什么」以及 DRA 为何要接替 device-plugin;② 能用「最小事务单元从 Pod 提升到作业」一句话讲清 Gang 调度为什么能解资源碎片死锁,并复述「A 占 4 卡、B 占 4 卡互相饿死」的反例;③ 能解释拓扑感知调度的收益来自哪里——把同机 NVLink(~600 GB/s 级)与跨机 RDMA(~50–100 GB/s 级)的带宽差,换算成训练吞吐差;④ 能对比 K8s / Slurm / Ray 三种调度心智模型的 GPU 申请方式与适用场景,知道一个成熟平台为何往往是三者的混合体;⑤ 亲手在无物理 GPU 的开发机上用 kind + 伪造算力 + Volcano,观测「凑不齐 minMember 全组 Pending → 凑齐后整体 Running」的原子调度行为。
- 阅读姿势:盯住一条主线——「AI 集群调度的所有复杂度,都来自训练作业『全有或全无 + 拓扑敏感』这条与传统 Web 调度相反的特性」。无论是 Gang 的原子凑齐、拓扑感知的就近排布,还是 MIG/vGPU 的隔离核算,本质都在回答同一个问题:怎么把一堆昂贵且稀缺的异构 GPU,既「调对」又「用满」。
背景与现状
通用 Web 服务的调度哲学是「化整为零」——把一个无状态请求拆到任意空闲节点上即可。而 AI 训练作业的调度哲学恰好相反,是「化零为整」:一个 8 机 64 卡的训练任务,要么 64 张卡同时就位、彼此还得物理上离得近,要么干脆别启动——只到位 60 张卡不是「完成了 94%」,而是「死锁了 100%」。
这条「全有或全无 / 拓扑敏感」的特性,决定了 AI 集群调度与传统 K8s 调度有三个根本分歧:
- 原子性(Gang):分布式训练的所有 Worker 互为
All-Reduce的对端,缺一个就集体卡在NCCL通信屏障上空耗显存。默认 K8s 调度器逐 Pod 调度,会造成 资源碎片死锁(A 作业占了 4 卡等第 5 卡,B 作业占了 4 卡也等第 5 卡,互相饿死)。 - 拓扑敏感:同一台机器内 NVLink 带宽是跨机 RDMA 的数倍。把 8 个 Worker 随机撒到 8 台机器,和塞进 1 台机器的 8 张卡,训练吞吐可能差 2–3 倍。
- 昂贵且稀缺:一张 H100 的小时成本是普通 CPU 核的成千上万倍。调度器的 KPI 不再是「延迟」,而是 GPU 利用率(实打实的 SM 占用 / 计费时长) 与 排队公平性。
业界信号(截至 2026 年中):CNCF 项目 Volcano、Kubernetes 官方 SIG 的 Coscheduling 插件、以及 NVIDIA 的 GPU Operator,本质都在给「云原生调度器」补上「批量计算(HPC)」的能力。2025–2026 这一领域有三处关键演进:① DRA(Dynamic Resource Allocation,动态资源分配) 在 K8s 1.32 进入 beta、并以 K8s 1.34(2025 H2)核心 GA 为目标,正逐步取代老旧的 device-plugin 模型来表达「GPU 分区/共享/拓扑约束」这类复杂资源需求;② Kueue 成为 K8s 原生作业排队/配额的事实标准(支持 cohort、公平共享、拓扑感知调度 TAS);③ NVIDIA 于 2024 年底收购 Run:ai,并把其调度能力以 KAI Scheduler 开源整合进生态。而传统超算阵营的 Slurm(持续主导 HPC,并推进「Slurm on Kubernetes」的 Slinky 项目)与编程式的 Ray,则代表另外两条调度心智模型。一个成熟的 AI 平台,往往是这几者的混合体。
规模实感:到 2025–2026,单集群规模已被推到十万卡级——xAI 的 Colossus(孟菲斯) 据公开报道以约 10 万张 H100 起步并持续向更大规模扩展,Meta 规划吉瓦级数据中心(Prometheus/Hyperion),Google 的 TPU v7(Ironwood)单 Pod 达 9216 芯片量级。集群越大,「把卡调对、把卡用满」的每 1% 利用率提升,对应的就是数千万美元级的算力价值。
原理与架构
2.1 K8s GPU 调度全栈:从 device plugin 到 Gang
要理解 K8s 怎么把一张 GPU「调」出去,必须看清 Device Plugin → kubelet → 调度器 → Gang 插件 这条完整链路。
逐层读这张图:
- Device Plugin(设备插件):GPU 不是 K8s 原生资源(K8s 只懂
cpu/memory)。NVIDIA 的nvidia-device-plugin以 DaemonSet 跑在每个节点,通过 gRPC 的ListAndWatch接口向 kubelet 上报扩展资源nvidia.com/gpu: 8,并在容器启动时把/dev/nvidiaX设备节点与驱动库挂进去。这是「K8s 能看见 GPU」的根。演进提示(2025–2026):device-plugin 的「整数计数」模型难以表达 GPU 分区/共享/拓扑约束,K8s 正用 DRA(Dynamic Resource Allocation) 接替——以ResourceClaim/DeviceClass描述「我要一块带 NVLink 域、支持 MIG 切分的 GPU」,NVIDIA 已发布k8s-dra-driver(含 GPU/ComputeDomain、IMEX/NVLink 域支持)。device-plugin 仍是当前生产主力,DRA 是下一代方向。 - GPU Operator(自动化运维):手动在几百个节点上装对的驱动版本 + container-toolkit + device-plugin 是运维噩梦。GPU Operator 用一组 Operator 自动完成驱动安装、device plugin 部署、DCGM 监控、MIG 管理的全套编排——把「节点 GPU 就绪」变成声明式。
- Gang 调度(成组调度):默认调度器逐 Pod 调度无法保证原子性。Volcano / Coscheduling 引入 PodGroup 概念:声明
minMember: 8,调度器会先在「假想中」凑齐 8 个 Pod 的资源,全部满足才一次性 Bind,否则全部挂起。这从根上消灭了「半个作业占着卡互相饿死」的资源碎片死锁。
2.2 Gang 调度为何能解死锁:两阶段对比
| 维度 | 默认调度器(逐 Pod) | Gang 调度器(Volcano/Coscheduling) |
|---|---|---|
| 调度单元 | 单个 Pod | PodGroup(整组) |
| 资源不足时 | 已起的 Pod 先占住卡 → 碎片 | 全组挂起(Pending),不占一张卡 |
| 死锁场景 | A 占 4 卡等第 5,B 占 4 卡等第 5 → 互饿 | 凑不齐 minMember 则谁都不起,资源回收 |
| 抢占 | 难以做组级抢占 | 支持按 PodGroup 优先级整组抢占 |
| NCCL 屏障 | 部分 Worker 在线 → 集体卡死空耗显存 | 全员同时就绪 → 通信屏障一次性通过 |
值得注意的是:Gang 调度的精髓不是「调度更快」,而是把调度的最小事务单元从「Pod」提升到「作业」,让「全有或全无」成为调度器的一等公民。没有它,分布式训练在繁忙集群上几乎必然遭遇碎片死锁。
2.3 拓扑感知亲和性调度
凑齐 8 张卡只是「调对」的一半,另一半是这 8 张卡彼此离得够近。这就是拓扑感知调度(Topology-Aware Scheduling)。
- GPU 间拓扑:
nvidia-smi topo -m会打出卡间互联矩阵(NV#表示 NVLink 几条、PIX/PXB表示同 PCIe 交换机、SYS表示要跨 NUMA/QPI)。调度器应优先把同一作业的 Worker 排到 NVLink 全连接的同机 8 卡,而非跨机。 - 节点间拓扑:跨机走 RDMA/InfiniBand,调度器应感知 同一 Spine/Leaf 交换机下、同一 SuperPod 的节点,缩短
All-Reduce的跨网跳数。Volcano 的network-topology-aware插件、K8s 的topology.kubernetes.io/*标签均服务于此。 - 机内 NUMA 亲和:GPU 挂在某个 NUMA node 的 PCIe 根下,喂数据的 CPU 进程与内存若分配到另一个 NUMA node,数据要跨 QPI 搬运,吞吐骤降。这要靠下文的 cgroups/NUMA 亲和绑定来收口。
量级感知:同机 8 卡 NVLink(~600 GB/s 级别双向)vs 跨机 RDMA(~50–100 GB/s 级别),带宽差近一个数量级。拓扑调度做得好不好,直接决定大模型训练的有效算力(MFU)能否接近理论上限。
2.4 三种调度心智模型:K8s / Slurm / Ray
| 系统 | 心智模型 | GPU 申请方式 | 适用场景 |
|---|---|---|---|
| Kubernetes + Volcano | 声明式、容器原生、长 期服务化 | resources.limits: nvidia.com/gpu: 8 | 训推混合、平台化多租户 |
| Slurm | 命令式、批作业队列、HPC 血统 | #SBATCH --gres=gpu:8 | 科研超算、纯训练大集群 |
| Ray | 编程式、任务/Actor 级细粒度 | @ray.remote(num_gpus=1) | 强化学习、超参搜索、流水线 |
- Slurm:用 GRES(Generic RESource) 抽象 GPU,
--gres=gpu:a100:4申请、--partition选队列、sbatch提交、squeue看排队、scontrol管节点。它的 Gang 语义是天生的——一个srun的所有 task 由 Slurm 统一拉起。队列策略靠QOS、fairshare、preempt实现公平与抢占。 - Ray:调度粒度细到「函数调用 / Actor」。
ray.init()连上集群后,@ray.remote(num_gpus=1)装饰的函数会被调度到有空闲 GPU 的节点;ray job submit提交作业。它把「GPU 是一种逻辑资源池」的思想做到极致,特别适合 RLHF 这种「训练 + 推理 + 环境交互」交织的复杂流水线。
动手实践:kind 上模拟 GPU + Gang 调度
实验目标:在完全无物理 GPU 的开发机上,用 kind(Docker 里跑 K8s)起一个集群,安装 fake GPU device plugin 让节点「假装」有 GPU,再装 Volcano,提交一个带 Gang 调度的 Job,亲手观测「凑不齐 minMember 全组 Pending → 凑齐后整体 Running」的原子调度行为。产出物:kubectl get podgroup 与 kubectl get pods 的状态对照。
3.1 环境准备
# 依赖:docker、kubectl、kind、helm(任选包管理器安装)
# kind:本地用容器模拟 K8s 节点
go install sigs.k8s.io/kind@latest 2>/dev/null || \
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 && \
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
kind version && kubectl version --client && helm version
3.2 起一个多节点 kind 集群
cat > kind-gpu.yaml <<'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
kind create cluster --name gpu-lab --config kind-gpu.yaml
kubectl get nodes
3.3 路径 A(无 GPU,推荐):用 fake GPU device plugin 伪造算力
无物理 GPU 时,我们直接给节点打上 fake 扩展资源,让调度器以为有 GPU。最轻量的做法是用 kubectl proxy 给节点 PATCH nvidia.com/gpu 容量(生产对应 NVIDIA 的真实 device plugin DaemonSet)。
# 给两个 worker 各「伪造」4 张 GPU。注意 ~1nvidia.com~1gpu 是 JSON Pointer 转义
for N in $(kubectl get nodes -l '!node-role.kubernetes.io/control-plane' -o name); do
kubectl proxy --port=8001 & PROXY_PID=$!; sleep 2
NODE=${N#node/}
curl -s --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op":"add","path":"/status/capacity/nvidia.com~1gpu","value":"4"}]' \
http://localhost:8001/api/v1/nodes/${NODE}/status >/dev/null
kill $PROXY_PID
done
# 验证:每个 worker 应显示 nvidia.com/gpu: 4
kubectl get nodes -o custom-columns=NODE:.metadata.name,GPU:.status.capacity.nvidia\\.com/gpu
备选方案:社区有现成的
kwok(K8s WithOut Kubelet)可批量伪造大规模带 GPU 的「幽灵节点」,以及fake-gpu-device-plugin(Run:ai 开源)以 DaemonSet 形式上报假 GPU 并伪造nvidia-smi。本实验用 PATCH 法是为了零额外依赖、聚焦调度行为本身。
3.4 安装 Volcano(Gang 调度器)
helm repo add volcano-sh https://volcano-sh.github.io/helm-charts
helm repo update
helm install volcano volcano-sh/volcano -n volcano-system --create-namespace
# 等待组件就绪
kubectl -n volcano-system wait --for=condition=Available deploy --all --timeout=180s
kubectl -n volcano-system get pods
3.5 提交一个 Gang 调度作业
我们提交一个需要 6 张 GPU、minAvailable=6 的作业(集群共伪造了 8 张,足够)。关键是用 Volcano 的 Job CRD,它原生带 PodGroup 语义。
# gang-job.yaml
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: dist-train-demo
spec:
minAvailable: 6 # Gang:必须 6 个 Pod 同时就绪才整体调度
schedulerName: volcano # 关键:交给 Volcano 调度,而非默认调度器
queue: default
tasks:
- replicas: 6
name: worker
template:
spec:
containers:
- name: trainer
image: busybox:1.36
command: ["sh", "-c", "echo gang-member-up && sleep 600"]
resources:
requests:
nvidia.com/gpu: 1 # 每个 Worker 要 1 张(伪造)GPU
limits:
nvidia.com/gpu: 1
restartPolicy: Never
kubectl apply -f gang-job.yaml
# 观察 PodGroup:Inqueue → Running,phase 体现 Gang 状态
kubectl get podgroup -w
# 观察 Pod:要么 6 个一起 Running,要么集体 Pending(凑不齐时)
kubectl get pods -l volcano.sh/job-name=dist-train-demo -o wide
验证 Gang 死锁规避:把 minAvailable 与 replicas 改 成 10(超过伪造的 8 卡),重新 apply,你会看到全部 10 个 Pod 卡在 Pending、且一张卡都没占用——这正是 Gang 调度「凑不齐就全不起、绝不制造碎片」的核心价值。对比默认调度器,那 8 个 Pod 会先把卡占满、剩 2 个永远 Pending 且无法回收。
# 清理
kubectl delete -f gang-job.yaml
kind delete cluster --name gpu-lab
本机实测(WSL2 / kind v1.32 / Volcano helm):
[gang] running_pods=6 (expect 6)
dist-train-demo-... PodGroup STATUS=Running MINMEMBER=6 RUNNINGS=6
[ok] kind gang lab passed
一键复现:bash run_kind_gang.sh(ai-infra-labs 仓库)。
踩坑预警 (Gotchas)
schedulerName漏写 = Gang 失效:不写schedulerName: volcano,作业会落到默认调度器,PodGroup 语义形同虚设。这是最常见的「装了 Volcano 却没生效」的根因。- PATCH 伪造的容量会被覆盖:节点状态由 kubelet 持续上报,PATCH 的
capacity可能被刷掉。仅适合短时实验;长期模拟请用kwok或fake-gpu-device-pluginDaemonSet。 - JSON Pointer 转义:路径里
nvidia.com/gpu的/必须写成~1、~写成~0,否则 PATCH 报路径错误。 minAvailable≤replicas:前者大于后者会永远凑不齐而 Pending,调试时务必核对两个数。- kind 无 NVIDIA runtime:kind 默认无法挂载真 GPU。要在 kind 里跑真 GPU,需额外配置
nvidia-container-runtime并挂载设备——本实验刻意走伪造路径绕开这一层复杂度。
深入思考
下面三题每题先给题干,再用
<details>折叠一份图文并茂的参考答案。建议先合上答案自己想 3 分钟,再展开对照。
思考题 1:碎片 vs 利用率的两难
Gang 调度为了避免死锁,会让「凑不齐的作业」整组挂起、释放已占资源。但这意味着集群可能出现「有卡空着、却没人用」的瞬时空窗。结合 2.2 节的 Gang 原子调度,作为平台架构师,你会如何用 backfill(回填小作业填空隙)+ 抢占优先级 在「避免死锁」与「最大化利用率」之间取得平衡?请结合 Volcano 的 reclaim/backfill action 设计一套队列策略。
展开参考答案(含 Volcano 五大 action 流水图 + 算一遍)
结论:Gang 制造的空窗是「为原子性付的保费」,而非纯损耗——正确的解法不是放弃 Gang,而是用「高优先训练作业占主航道 + backfill 用碎片时间塞小作业 + preempt/reclaim 在配额被借用时强制回收」三层兜底,把空窗填上又不破坏大作业的原子性。
一套可落地的队列策略:
- 分队列 + weight 公平:建
train(高 weight)、batch(中)、spot(低、可被抢占)三队列,用 Volcano 的proportion插件按 weight 分配 deserved 配额,cohort 内允许借用。 - action 顺序:
enqueue → allocate → backfill → preempt → reclaim。allocate先满足高优 Gang 作业;backfill在「高优作业凑不齐留下的空窗」里塞spot队列的小作业(单 Pod、无 Gang 约束,能随时被踢);preempt/reclaim在高优作业终于凑齐、或某队列超用配额时,把spot作业整组踢掉腾卡。 - 关键护栏:被 backfill 进来的作业必须是可中断、无 Gang 强约束的(如评测、数据预处理),否则一旦被抢占又会制造新的半成品碎片。
用具体数字算一遍(64 卡集群,量级估算):
- 一个 48 卡 训练作业入队,但当前只有 40 卡 空闲 → Gang 让它整组 Pending,8 卡空窗。
- 若不做 backfill:这 40 卡(加新空出的卡)一直空着等第 48 张,利用率 = (64−40)/64 ≈ 37.5%,且持续数分钟到数十分钟。
- 开 backfill:把 40 张空闲卡塞满 40 个
spot小推理/评测作业,瞬时利用率拉到 ~100%。 - 当第 48 张卡空出、大作业凑齐:
preempt一次性踢掉其中 48 个 spot 作业(它们可中断、loss 可接受),大作业整组 Running。净效果:空窗期被小作业填满,大作业原子性毫发无损。
思考题 2:隔离粒度的成本账
一张 A100 可用 MIG 切成最多 7 份独立实例(硬件级隔离、显存/SM 物理分区) ,也可用 vGPU 做时间片软切分,还可以靠 cgroups + NUMA/IRQ 亲和 在整卡上做进程级软隔离。结合 2.3 节的拓扑亲和与背景中的「GPU 利用率」KPI,给定「大量小推理请求(如 1.5B 模型)」与「少量大训练作业」混跑的集群,你会怎么组合 MIG/vGPU/cgroups?请从利用率核算角度论证:为什么「按整卡计费」会让 MIG 的 ROI 难以体现,又该如何设计按 MIG 实例/SM-时的计量口径?
展开参考答案(含三种隔离粒度对比图 + 计费口径对比表)
结论:隔离粒度要「按负载画像选」——小推理用 MIG 做硬件级强隔离吃满碎片算力,大训练独占整卡 + NVLink 拓扑亲和,cgroups/NUMA 只做整卡内进程兜底;而「按整卡计费」会把 MIG 切出来的 7 份算力当成 1 份卖,ROI 自然算不出来,必须改成「按 MIG 实例 / SM-时」的细粒度计量才能让分区的价值落到账面。
计费口径对比:
| 计量口径 | 一张被 MIG 切成 7 份的 A100 | 问题 / 价值 |
|---|---|---|
| 按整卡计费 | 仍只能记为「1 张卡 × 时长」 | 7 个租户共享,平台收 1 份钱 → MIG 越切越「亏」,ROI 算不出来 |
| 按 MIG 实例-时 | 记为「7 × MIG 实例 × 时长」 | 每个 1g.10gb 实例独立计费,分区价值落到账面 |
| 按 SM-时 / 显存-时 | 按实际占用的 SM 份额 × 时长 | 最精确,能反映 3g.20gb 比 1g.10gb 贵 3 倍 |
为什么整卡计费让 MIG 的 ROI 难体现:MIG 的 核心价值是「把一张利用率只有 15% 的整卡,切成 7 份各自跑满的小卡,整体利用率拉到 80%+」。但如果计费系统只认「整卡 × 时长」,那么无论切不切,账面都只产出「1 卡时」的收入——切分带来的利用率收益在账上完全不可见,平台没有动力去做 MIG,租户也无法为「只用 1/7 张卡」少付钱。
怎么设计细粒度计量:① 通过 DCGM 采集每个 MIG 实例的 SM 占用、显存占用、计费时长;② 把计费单位从「物理 GPU」改成「MIG profile(如 1g.10gb/3g.20gb)× 时长」,不同 profile 对应不同单价(与其占用的 SM/显存份额成比例);③ 对走 vGPU/cgroups 软隔离的负载,则按「分配的算力配额 × 时长」近似。这样一来,「大量小推理用 MIG 吃满碎片」的利用率收益,才能真正转化为账面 ROI——计费口径不改,隔离粒度做得再细也是自我感动。
思考题 3:三调度器混合架构
一个同时承载「大规模预训练(适合 Slurm 的 HPC 队列)」「平台化推理服务(适合 K8s 长服务)」「RLHF 流水线(适合 Ray 的细粒度)」的统一集群——结合 2.4 节三种调度心智模型,你会让三套调度器抢同一批 GPU,还是静态分池?如果要做动态借还,关键的「资源真相源(single source of truth)」该放在哪一层?跨调度器的 Gang 原子性又如何保证不被打破?
展开参考答案(含「真相源单一化」分层架构图 + 取舍对比)
结论:三套调度器直接抢同一批卡必然脑裂(各自以为卡是空的,双重分配后 Gang 原子性被打破);正确做法是设一个「上层资源真相源 / meta-scheduler」持有全集群 GPU 的唯一账本,把卡以『可借还的池』形式租给下层 K8s/Slurm/Ray,借还以整 Gang 块为最小单位,从而既能动态调剂、又不破坏任何一个分布式作业的原子性。
为什么不能直接抢同一批卡:三套调度器各有独立的资源视图,若同时认为某 8 张卡空闲,会双重分配(double-booking)——Slurm 拉起的训练任务和 K8s 的推理 Pod 抢同一张卡,显存 OOM,而 Gang 作业一旦有一个 Worker 拿不到卡,整组卡在 NCCL 屏障空耗。这正是 2.4 节三种心智模型「各管各」带来的根本风险。
静态分池 vs 动态借还的取舍:
| 方案 | 优势 | 代价 |
|---|---|---|
| 静态分池 | 简单、零脑裂风险、各调度器互不干扰 | 池间不能借还 → 训练池闲时推理池却排队,整体利用率低 |
| 动态借还(推荐) | 利用率高,闲时算力可跨池调剂 | 需一个上层真相源仲裁,借还协议要保证 Gang 原子 |
真相源放哪一层 + 如何保 Gang 原子:① 真相源放在三套调度器之上的 meta-scheduler / quota 层(如用 Kueue 的 cohort 做统一配额池,或 NVIDIA KAI、自研 quota 服务),它是 GPU 归属的唯一账本,下层调度器只能「向它申请、用完归还」,绝不直接认领物理卡;② 借还的最小单位是「整 Gang 块」——要借就借满一个分布式作业需要的整组卡(如 8 卡一块),且借出后这块卡在真相源里标记为「已租给 Slurm」,K8s/Ray 看不到;③ 归还时同样整块回收,借用期内不可被打断(或仅允许带优先级的整组抢占),这样任何一个下层调度器内部的 Gang 作业,其原子性边界都落在「一块已确定归属的卡」之内,不会被另一套调度器从中间挖走一张卡——单一真相源 + 整块借还 = 动态调剂与 Gang 原子性可以兼得。
延伸阅读
1. 核心 Paper / 设计文档
- Gang Scheduling 经典思想(Ousterhout, 1982)— 理解「成组调度」的理论起点,所有 Volcano/Coscheduling 的祖师爷。
- Kubernetes SIG-Scheduling 的 Coscheduling KEP 与 Capacity Scheduling 提案 — 理解 K8s 官方如何把 HPC 能力补进云原生调度器。
- NVIDIA Multi-Instance GPU (MIG) User Guide — 理解 A100/H100 硬件级 GPU 分区的原理与限制。
2. 相关高 Star 仓库与源码必读路径
volcano-sh/volcano— 入口看pkg/scheduler/actions/(enqueue/allocate/backfill/preempt/reclaim 五大 action)与pkg/scheduler/plugins/gang/(Gang 插件核心)。kubernetes-sigs/kueue— K8s 原生作业排队/配额(cohort、公平共享、拓扑感知 TAS)的事实标准实现。NVIDIA/KAI-Scheduler— Run:ai 被 NVIDIA 收购后开源的 GPU 调度器,理解分时/超分/fractional GPU 的工程实现。kubernetes-sigs/dra-example-driver与 NVIDIAk8s-dra-driver— 理解 DRA(动态资源分配)如何接替 device-plugin 表达复杂 GPU 资源。NVIDIA/k8s-device-plugin— 看cmd/nvidia-device-plugin/的ListAndWatch与AllocategRPC 实现,理解资源上报与设备挂载。NVIDIA/gpu-operator— 理解驱动/toolkit/DCGM/MIG-manager 的声明式编排。ray-project/ray— 看python/ray/_private/resource_spec.py与 scheduler,理解num_gpus细粒度调度。
3. 优质博客 / 文档
- Volcano 官方文档「Gang Scheduling」与「Network Topology Aware Scheduling」章节。
- Slurm 官方文档
gres.conf/Sharing Consumable Resources (GRES)章节,对照 K8s device plugin 理解两套 GPU 抽象。 - Run:ai / NVIDIA 技术博客关于 GPU 利用率核算(GPU Utilization vs MFU) 与 fractional GPU 的系列文章。
下一篇 → L1.5 多地域与混合云算力网络:把调度的边界从「单集群」放大到「跨地域、跨云」,讲清算力联邦、数据就近与跨域网络的工程权衡。