【中阶·云原生】GPU 感知调度与设备插件机制深度解析:从 Device Plugin 到 DRA 的架构演进

【中阶·云原生】GPU 感知调度与设备插件机制深度解析:从 Device Plugin 到 DRA 的架构演进

专栏:《AI 工程与安全深度实战》· 第2轮·第1篇

前言

  • 核心痛点:AI 训练与推理工作负载对 GPU 资源的需求远超传统 CPU 工作负载,Kubernetes 原生调度器无法感知 GPU 拓扑、共享模式与设备健康状态,导致资源浪费、调度失败与性能下降
  • 适配人群:具备 Kubernetes 基础知识的平台工程师、MLOps 工程师、AI 基础设施架构师,希望深入理解 GPU 调度底层机制并掌握生产级部署能力
  • 收获能力:读完可掌握 Device Plugin 框架原理、DRA 新一代资源分配模型、NVIDIA GPU Operator 全栈组件、MIG/MPS/Time-slicing 三种 GPU 共享模式、KAI Scheduler 调度策略,并具备从零搭建生产级 GPU 集群的实战能力

目录

  • 技术背景与演进逻辑
  • Device Plugin 框架:GPU 调度的基石
  • DRA:新一代设备资源分配模型
  • NVIDIA GPU Operator:GPU 全栈管理引擎
  • GPU 共享模式:MIG、MPS 与 Time-slicing
  • KAI Scheduler:AI 工作负载感知调度
  • 技术优缺点与适用场景
  • 实战落地:生产级 GPU 集群部署
  • 全文总结
  • 参考资料

技术背景与演进逻辑

GPU 调度的行业痛点

2026 年,AI 基础设施面临的核心矛盾是:GPU 硬件成本高昂,但利用率普遍偏低。根据公开数据,企业 GPU 集群平均利用率仅为 30%-50%,大量算力在等待调度、跨 NUMA 访问、错误共享模式中被浪费。

传统 Kubernetes 调度模型将 GPU 视为"不透明的整数资源",通过 nvidia.com/gpu: 1 这样的扩展资源声明来请求设备。这种模型存在以下根本性缺陷:

缺陷维度 具体表现 影响
无拓扑感知 调度器不感知 NVLink、PCIe、NUMA 拓扑 跨 NUMA 的 GPU 通信带宽下降 50%+
无共享语义 整数资源不可超额分配 小模型推理独占整卡,资源浪费严重
无参数化选择 无法按显存、算力、型号筛选设备 混合型号集群调度失败率高
无生命周期管理 Pod 删除即释放,无法跨 Pod 持有 Notebook 场景 GPU 被抢占后丢失状态
无健康状态联动 设备故障后调度器无感知 Pod 被调度到故障 GPU 上反复重启

技术演进时间线

复制代码
2018 ──→ 2020 ──→ 2022 ──→ 2024 ──→ 2025 ──→ 2026
  │         │         │         │         │         │
  │         │         │         │         │         └── DRA GA (K8s 1.34)
  │         │         │         │         │             KAI Scheduler 0.5
  │         │         │         │         │             GPU Operator 24.9
  │         │         │         │         │
  │         │         │         │         └── NVIDIA 捐赠 DRA 驱动至 CNCF
  │         │         │         │             KubeCon NA 2025
  │         │         │         │
  │         │         │         └── KAI Scheduler 开源
  │         │         │             DRA Beta (K8s 1.31)
  │         │         │
  │         │         └── Device Plugin GA (K8s 1.26)
  │         │             Topology Manager GA
  │         │             GPU Operator 首发
  │         │
  │         └── Device Plugin Beta (K8s 1.12)
  │             MIG 技术随 A100 发布
  │
  └── Device Plugin Alpha (K8s 1.8)
      NVIDIA 首个 Device Plugin 实现

2026 年 GPU 调度技术全景

当前 Kubernetes GPU 调度已形成三层架构:

复制代码
[AI 工作负载层]
    │
    ├── 训练任务(Job/PyTorchJob/RayCluster)
    │      │
    │      └── 需求:多卡原子调度 + NVLink 拓扑 + 整卡独占
    │
    ├── 推理服务(Deployment/Knative Service)
    │      │
    │      └── 需求:低延迟 + GPU 共享 + 弹性扩缩
    │
    └── 开发环境(Notebook/VS Code Server)
           │
           └── 需求:按需分配 + 超额订阅 + 快速回收

    ↓ 调度抽象层

[调度与资源管理层]
    │
    ├── KAI Scheduler ──→ Gang 调度 + 队列优先级 + 抢占
    │
    ├── DRA Driver ──→ ResourceClaim + DeviceClass + CEL 过滤
    │
    └── GPU Operator ──→ 驱动 + 容器工具 + MIG 管理 + DCGM 监控

    ↓ 设备抽象层

[硬件层]
    │
    ├── NVIDIA GPU(A100 / H100 / H200 / B100)
    │      │
    │      ├── 整卡模式
    │      ├── MIG 分区模式(最多 7 实例)
    │      ├── MPS 共享模式(软件多路复用)
    │      └── Time-slicing 时间片模式
    │
    ├── AMD GPU(MI300X)
    │
    └── Intel GPU(Flex / Max 系列)

Device Plugin 框架:GPU 调度的基石

框架架构

Device Plugin 是 Kubernetes v1.8 引入的扩展机制(v1.26 GA),允许第三方厂商以标准化方式将硬件设备暴露给 kubelet,无需修改 Kubernetes 核心代码。GPU 调度的整个历史始于这一框架。

复制代码
[Device Plugin 架构]

[Kubelet]
    │
    ├── Device Manager(内建组件)
    │      │
    │      ├── 注册管理 ──→ 监听 /var/lib/kubelet/device-plugins/kubelet.sock
    │      │
    │      ├── 设备清单 ──→ 维护节点可分配设备列表
    │      │
    │      ├── 分配决策 ──→ 调用 Plugin.Allocate() 获取容器配置
    │      │
    │      └── 健康监控 ──→ 接收 ListAndWatch 流式更新
    │
    ├── Topology Manager(v1.27 GA)
    │      │
    │      └── NUMA 感知 ──→ 协调 CPU/内存/设备的拓扑亲和性
    │
    └── PodResources API(v1.28 GA)
           │
           └── 监控发现 ──→ 暴露已分配设备信息给 DCGM 等监控组件

注册流程详解

Device Plugin 的注册是一个严格的有序流程,任何步骤错序都会导致注册失败。

复制代码
[Device Plugin 启动与注册流程]

[Device Plugin Pod 启动]
    │
    ├── Step 1: 初始化
    │      │
    │      └── 厂商特定初始化(加载驱动、探测设备、校验健康)
    │
    ├── Step 2: 启动 gRPC Server
    │      │
    │      └── 监听 Unix Socket
    │             路径: /var/lib/kubelet/device-plugins/<plugin>.sock
    │             ⚠️ 此路径为硬编码,不受 kubelet --root-dir 影响
    │
    ├── Step 3: 调用 Registration.Register()
    │      │
    │      └── 向 kubelet 注册,声明:
    │             ├── Socket 名称
    │             ├── API 版本
    │             └── ResourceName(如 nvidia.com/gpu)
    │
    └── Step 4: 进入 Serving 模式
           │
           ├── ListAndWatch ──→ 持续上报设备列表与健康状态
           │
           └── 等待 Allocate 请求 ──→ 容器创建时被调用

关键设计约束 :Plugin 必须在 gRPC Server 启动之后才能调用 Register。如果先注册再启动服务,kubelet 会因无法连接到 Socket 而拒绝注册。

gRPC API 深度解析

Device Plugin 暴露五个 gRPC 方法,其中两个是必选的,三个是可选的:

复制代码
service DevicePlugin {
    // 必选方法
    rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse)
    rpc Allocate(AllocateRequest) returns (AllocateResponse)

    // 可选方法
    rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions)
    rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse)
    rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse)
}

ListAndWatch 是设备生命周期管理的核心。Plugin 通过流式响应持续向 kubelet 推送设备状态变更:

  • 首次调用:返回当前所有健康设备的完整列表
  • 设备状态变更:设备故障、恢复、热插拔时推送增量更新
  • kubelet 重启检测:Plugin 监控自身 Socket 文件被删除的事件,自动重新注册

当设备被标记为不健康时,kubelet 会递减该节点上该资源的 Allocatable 计数(但不改变 Capacity),从而阻止新 Pod 被调度到故障设备上。已分配到故障设备的 Pod 会继续运行,直到依赖设备的应用代码失败并触发重启。

Allocate 是容器创建时的关键调用。当 kubelet 为一个 Pod 分配了设备后,会调用对应 Plugin 的 Allocate 方法,传入待分配的设备 ID 列表。Plugin 返回 AllocateResponse,其中包含:

  • 设备节点挂载(/dev/nvidia0 等)
  • 环境变量(NVIDIA_VISIBLE_DEVICES 等)
  • 文件系统挂载(CUDA 运行时库、驱动库)
  • CDI 设备名称(v1.31 GA,标准化设备发现机制)
yaml 复制代码
# Device Plugin AllocateResponse 示例(简化)
container_responses:
  - device_nodes:
      - host_path: /dev/nvidia0
        container_path: /dev/nvidia0
        permissions: "rw"
      - host_path: /dev/nvidiactl
        container_path: /dev/nvidiactl
        permissions: "rw"
    mounts:
      - host_path: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.550.127.05
        container_path: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so
        read_only: true
    envs:
      - key: NVIDIA_VISIBLE_DEVICES
        value: "GPU-xxxx-xxxx-xxxx"

Topology Manager 集成

Topology Manager 是 kubelet 内建组件(v1.27 GA),负责协调 CPU、内存、设备的 NUMA 拓扑亲和性。Device Plugin 通过 TopologyInfo 结构向 Topology Manager 报告设备的 NUMA 位置:

复制代码
[Topology Manager 决策流程]

[Pod 创建请求]
    │
    ├── CPU Manager ──→ 候选 CPU 集合(NUMA 0)
    │
    ├── Memory Manager ──→ 候选内存节点(NUMA 0)
    │
    ├── Device Manager ──→ 候选 GPU(NUMA 0, TopologyInfo)
    │
    └── Topology Manager ──→ 跨资源拓扑对齐决策
           │
           ├── 最佳策略(BestEffort):尽可能对齐,允许不对齐
           ├── 严格策略(Strict):必须全部对齐,否则拒绝调度
           └── 单 NUMA 策略(SingleNumaNode):所有资源必须在同一 NUMA 节点

在多 GPU 训练场景中,拓扑感知至关重要。以 8 卡 H100 节点为例,GPU 0-3 通常连接在 NUMA 0 上,GPU 4-7 连接在 NUMA 1 上。如果一个 4 卡训练任务被调度到跨 NUMA 的 GPU 组合(如 GPU 0,1,4,5),跨 NUMA 的 NVLink 通信带宽会显著下降,直接影响训练吞吐量。

PodResources API

PodResources API(v1.28 GA)是 kubelet 提供的只读 gRPC 服务,供监控组件发现节点上已分配的设备信息:

复制代码
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse)
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse)
    rpc Get(GetPodResourcesRequest) returns (GetPodResourcesResponse)
}

DCGM Exporter 等监控组件通过此 API 获取每个 Pod 实际使用的 GPU 设备 ID,从而将 GPU 指标(利用率、显存、温度)与 Pod 维度关联,构建出细粒度的可观测性仪表盘。

DRA:新一代设备资源分配模型

从 Device Plugin 到 DRA 的范式转变

Dynamic Resource Allocation(DRA)在 Kubernetes 1.34(2026 年 3 月)正式 GA,是 Device Plugin 框架的演进替代方案。DRA 的核心设计理念是:将设备从"不透明的整数资源"升级为"声明式的 API 对象"

复制代码
[Device Plugin vs DRA 对比]

Device Plugin 模型:
    Pod ──→ resources.limits: nvidia.com/gpu: 1
    │
    └── kubelet ──→ Device Manager ──→ 随机分配一个可用 GPU
                   无拓扑约束、无参数选择、无共享语义

DRA 模型:
    Pod ──→ resourceClaims: [my-gpu-claim]
    │
    ├── ResourceClaim ──→ DeviceClass: gpu.nvidia.com
    │      │
    │      ├── selectors: CEL 表达式(型号/显存/算力过滤)
    │      ├── constraints: 拓扑约束(NVLink 域/NUMA 节点)
    │      └── adminAccess: 共享语义(多 Pod 共享同一 Claim)
    │
    └── DRA Driver ──→ 在 bind 时解析 Claim 到真实设备

DRA 核心 API 对象

DRA 引入了三个核心 API 对象,形成声明式的设备分配模型:

DeviceClass 是集群管理员定义的设备类别,类似于 StorageClass 对存储的抽象:

yaml 复制代码
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
  name: gpu.nvidia.com
spec:
  selectors:
  - device:
      driver: gpu.nvidia.com

ResourceClaim 是工作负载对设备的声明式请求,类似于 PersistentVolumeClaim:

yaml 复制代码
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
  name: my-h100-claim
spec:
  devices:
    requests:
    - name: gpu
      deviceClassName: gpu.nvidia.com
      selectors:
      - cel:
          expression: >
            device.attributes['gpu.nvidia.com'].productName ==
            'NVIDIA H100 80GB HBM3' &&
            device.capacity['gpu.nvidia.com'].memory >= 80
      constraints:
      - requests: ["gpu"]
        matchAttribute: "gpu.nvidia.com/nvlinkDomain"

ResourceClaimTemplate 是用于自动创建 ResourceClaim 的模板,每个 Pod 实例获得独立的 Claim:

yaml 复制代码
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: single-gpu-template
spec:
  spec:
    devices:
      requests:
      - name: gpu
        deviceClassName: gpu.nvidia.com
        selectors:
        - cel:
            expression: "device.capacity['gpu.nvidia.com'].memory >= 24"

CEL 表达式过滤

DRA 使用 Kubernetes 通用表达式语言(CEL)进行设备属性过滤,这是 Device Plugin 完全不具备的能力:

过滤维度 CEL 表达式示例 说明
产品型号 device.attributes['gpu.nvidia.com'].productName == 'NVIDIA H100' 精确匹配 GPU 型号
显存容量 device.capacity['gpu.nvidia.com'].memory >= 80 筛选显存 >= 80GB 的设备
计算能力 device.attributes['gpu.nvidia.com'].computeCapability >= 9.0 筛选 SM 9.0+ 架构
MIG 配置 device.attributes['gpu.nvidia.com'].mig.profile == '1g.10gb' 匹配 MIG 分区配置
厂商标签 device.attributes['gpu.nvidia.com'].labels['team'] == 'ml-research' 自定义标签过滤

拓扑约束

DRA 的 constraints 字段允许声明设备间的拓扑关系,这是分布式训练的关键需求:

yaml 复制代码
# 要求 8 张 GPU 必须在同一个 NVLink 域内
spec:
  devices:
    requests:
    - name: gpus
      deviceClassName: gpu.nvidia.com
      count: 8
    constraints:
    - requests: ["gpus"]
      matchAttribute: "gpu.nvidia.com/nvlinkDomain"

matchAttribute 约束确保所有请求的设备共享相同的属性值------在上例中,8 张 GPU 必须属于同一个 NVLink 域,避免跨 socket 的带宽瓶颈。

DRA 与 Device Plugin 共存策略

DRA 和 Device Plugin 可以在同一集群中共存,但同一节点上的同一资源类型不能同时由两者管理。NVIDIA 推荐的迁移策略是:

复制代码
[迁移路径]

阶段 1(当前):
    │
    ├── 旧节点 ──→ Device Plugin(nvidia.com/gpu 扩展资源)
    │
    └── 新节点 ──→ DRA Driver(ResourceClaim 模型)
           │
           └── 通过 node label 区分调度目标

阶段 2(逐步迁移):
    │
    ├── 全部节点 ──→ DRA Driver
    │
    └── Device Plugin DaemonSet 降级为可选(仅用于旧版客户端兼容)

阶段 3(完全迁移):
    │
    └── 移除 Device Plugin,全面使用 DRA

NVIDIA GPU Operator:GPU 全栈管理引擎

组件架构

NVIDIA GPU Operator 是管理 Kubernetes GPU 节点全栈软件的 Helm Chart,它自动化了从驱动安装到监控导出的完整生命周期:

复制代码
[GPU Operator 组件架构]

GPU Operator Helm Chart
    │
    ├── NVIDIA Driver Manager
    │      │
    │      └── 自动安装/升级 GPU 内核模块(nvidia.ko, nvidia-uvm.ko)
    │            版本:550.127.05(当前稳定)
    │
    ├── NVIDIA Container Toolkit
    │      │
    │      └── 配置 containerd/CRI-O 的 NVIDIA 运行时
    │            使容器能够访问 /dev/nvidia* 设备节点
    │
    ├── NVIDIA Device Plugin(可选,DRA 迁移后可禁用)
    │      │
    │      └── 向 kubelet 注册 nvidia.com/gpu 扩展资源
    │
    ├── NVIDIA DRA Driver(独立 Helm Chart)
    │      │
    │      └── 注册 gpu.nvidia.com DeviceClass
    │            处理 ResourceClaim 的绑定与释放
    │
    ├── NVIDIA MIG Manager
    │      │
    │      └── 管理 MIG 分区配置的创建、切换、销毁
    │            处理 drain-reconfigure-uncordon 流程
    │
    ├── NVIDIA DCGM Exporter
    │      │
    │      └── 导出 GPU 指标到 Prometheus
    │            GPU 利用率 / 显存使用 / 温度 / 功耗
    │
    └── NVIDIA vGPU Manager(可选)
           │
           └── 管理 vGPU 许可证与虚拟化实例

关键版本信息(2026 年 6 月)

组件 版本 说明
Kubernetes 1.34 DRA GA 版本
NVIDIA GPU Operator v24.9.2 驱动 + 容器工具 + MIG + DCGM
NVIDIA DRA Driver v25.3.0 ResourceClaim 支持
NVIDIA 驱动 550.127.05 支持 CUDA 12.6
KAI Scheduler v0.5.1 Gang 调度 + 队列管理
CUDA 12.6 最新稳定版

DCGM 监控指标体系

DCGM(Data Center GPU Manager)Exporter 暴露的关键 Prometheus 指标:

指标名称 含义 告警阈值建议
DCGM_FI_DEV_GPU_UTIL GPU 计算利用率(%) 训练期间 < 70% 需排查
DCGM_FI_DEV_FB_USED 显存使用量(MiB) 接近总显存需关注 OOM
DCGM_FI_DEV_GPU_TEMP GPU 温度(°C) > 85°C 告警
DCGM_FI_DEV_POWER_USAGE 功耗(W) 接近 TDP 需关注散热
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL NVLink 总带宽(字节) 低于预期需排查拓扑
DCGM_FI_PROF_SM_ACTIVE SM 活跃比例(%) 低于 50% 表示未充分利用

GPU 共享模式:MIG、MPS 与 Time-slicing

三种共享模式对比

模式 隔离级别 支持 GPU 最大实例数 适用场景
MIG(Multi-Instance GPU) 硬件隔离:独立显存、SM 分区、故障域 A100, H100, H200, B100 7 / GPU 生产多租户推理、SaaS
MPS(Multi-Process Service) 软件隔离:共享 SM、共享显存总线 任意 CUDA GPU ~48 客户端 并发小模型推理、低延迟批处理
Time-slicing 无隔离:CUDA 上下文切换 任意 CUDA GPU 无限(可超售) 开发集群、Jupyter Notebook、CI
整卡独占 完全隔离 任意 1 大模型训练、延迟敏感推理

MIG:硬件级分区

MIG(Multi-Instance GPU)是 NVIDIA A100 及以后架构引入的硬件分区技术。它将一张物理 GPU 划分为最多 7 个独立实例,每个实例拥有:

  • 独立的 SM(Streaming Multiprocessor)分区
  • 独立的 L2 缓存分区
  • 独立的显存通道
  • 独立的故障域(一个实例故障不影响其他实例)

H100 80GB 的典型 MIG 配置:

配置方案 实例数 每实例显存 每实例 SM 适用场景
7x 1g.10gb 7 10 GB 1/7 小模型推理(8B 以下)
4x 2g.20gb 4 20 GB 2/7 中等模型推理(13B 量化)
3x 3g.40gb 3 40 GB 3/7 大模型推理(70B 量化)
1x 7g.80gb 1 80 GB 7/7 等同整卡(禁用 MIG)
混合配置 2+1+... 可变 可变 多种模型混合部署

MIG 的关键限制:分区配置需要 GPU 重置,这意味着必须先驱逐节点上所有使用该 GPU 的 Pod。生产环境中,应通过 GPU Operator 的 MIG Manager 自动化 drain-reconfigure-uncordon 流程,并在维护窗口执行配置变更。

MPS:软件级共享

MPS(Multi-Process Service)通过一个中心化的服务器进程,将多个 CUDA 客户端的 kernel 调度到同一 GPU 的 SM 上并发执行。与 MIG 的硬件分区不同,MPS 是软件层面的多路复用:

复制代码
[MPS 架构]

[MPS Control Daemon]
    │
    └── MPS Server(每个 GPU 一个)
           │
           ├── Client 1 ──→ CUDA Stream 1 ──→ SM 分区
           ├── Client 2 ──→ CUDA Stream 2 ──→ SM 分区
           ├── Client 3 ──→ CUDA Stream 3 ──→ SM 分区
           └── ...
                   │
                   └── 共享显存总线,硬件调度器分配 SM 时间

MPS 的优势在于:对于 kernel 启动频繁的小型推理任务,MPS 的并发执行比 Time-slicing 的上下文切换效率更高。但 MPS 的隔离性较弱------一个客户端的 CUDA 错误可能导致 MPS Server 崩溃,影响所有客户端。

Time-slicing:时间片共享

Time-slicing 是最简单的 GPU 共享方式:将一张物理 GPU 暴露为 N 个虚拟设备,通过 CUDA 上下文切换在不同客户端之间轮流执行。

复制代码
[Time-slicing 工作原理]

物理 GPU
    │
    ├── 虚拟设备 0 ──→ Pod A(推理中)
    ├── 虚拟设备 1 ──→ Pod B(等待上下文切换)
    ├── 虚拟设备 2 ──→ Pod C(等待上下文切换)
    └── 虚拟设备 3 ──→ Pod D(等待上下文切换)
           │
           └── CUDA 驱动按时间片轮转调度
                 无性能保证,无隔离

Time-slicing 适用于开发和测试环境,其中 GPU 大部分时间处于空闲状态,让多个工程师共享同一张 GPU 可以显著降低成本。但绝不应在生产 SLO 环境中依赖 Time-slicing,因为一个高负载的 Notebook 可以饿死同 GPU 上的所有其他客户端。

KAI Scheduler:AI 工作负载感知调度

为什么默认调度器不够用

Kubernetes 默认的 kube-scheduler 采用"逐 Pod 调度 + Filter-Score"模型。这种模型对于分布式训练任务存在根本性缺陷:

复制代码
[默认调度器的问题]

16 卡训练任务(PodGroup: 16 个 Pod)
    │
    ├── kube-scheduler 逐个调度
    │      │
    │      ├── Pod 1-8 ──→ 调度到 Node A(有 8 张空闲 GPU)✅
    │      ├── Pod 9-14 ──→ 调度到 Node B(有 6 张空闲 GPU)✅
    │      └── Pod 15-16 ──→ Pending(无可用 GPU)❌
    │
    └── 结果:
           ├── Node A 上 8 张 GPU 空转等待 Pod 15-16
           ├── Node B 上 6 张 GPU 空转等待 Pod 15-16
           └── 每小时浪费 $600+(按 H100 云上价格估算)

这就是"部分调度"问题:分布式训练要么全部 Pod 同时启动,要么全部等待------调度 14 个 Pod 而留下 2 个 Pending,比不调度更浪费。

KAI Scheduler 核心机制

KAI Scheduler 是 NVIDIA 开源的 Kubernetes 调度器,专为 AI/ML 工作负载设计。它作为第二个调度器与 kube-scheduler 并行运行,Pod 通过 schedulerName: kai-scheduler 显式选择。

Gang Scheduling(组调度) 是 KAI 的核心能力。通过 PodGroup 对象,KAI 确保一个训练任务的所有 Pod 原子性地被调度------要么全部成功,要么全部等待:

复制代码
[Gang Scheduling 流程]

[提交 Job: parallelism=8]
    │
    ├── KAI 读取 PodGroup: minMember=8
    │
    ├── 检查集群可用 GPU 资源
    │      │
    │      ├── 可用 >= 8 张 ──→ 原子调度全部 8 个 Pod ✅
    │      │
    │      └── 可用 < 8 张 ──→ 全部 Pending,等待释放 ✅
    │                           (不浪费任何 GPU)
    │
    └── 调度完成后,PodGroup 状态变为 Running

Hierarchical Queues(层级队列) 允许按团队/项目组织 GPU 配额:

复制代码
[队列层级示例]

root
    │
    ├── team-training(quota: 32 GPU, limit: 48 GPU)
    │      │
    │      ├── project-llm(quota: 16, priority: 100)
    │      │
    │      └── project-vision(quota: 16, priority: 80)
    │
    └── team-inference(quota: 16 GPU, limit: 24 GPU)
           │
           ├── project-serving(quota: 8, priority: 90)
           │
           └── project-staging(quota: 8, priority: 50)
  • quota:保证配额,其他队列不可抢占
  • limit:上限配额,可借用空闲资源
  • overQuotaWeight:借用空闲资源时的权重
  • priority:抢占优先级,高优先级队列可驱逐低优先级的借用资源

Priority-based Preemption(优先级抢占) 确保关键任务优先获得资源。当一个高优先级训练任务提交时,KAI 可以驱逐低优先级队列中借用的 burst 容量,但不会触碰其他队列的保证配额。

KAI vs Kueue vs Volcano

特性 KAI Scheduler Kueue Volcano
定位 GPU AI/ML 专用调度 通用批处理队列层 HPC 批处理调度
Gang 调度 ✅ 原生支持 ✅ 通过 PodGroup ✅ 原生支持
拓扑感知 ✅ 与 DRA 深度集成 ❌ 委托给 kube-scheduler ❌ 无
GPU 共享感知 ✅ MIG/MPS/Time-slicing ❌ 无 ❌ 无
队列层级 ✅ 层级队列 + 抢占 ✅ ClusterQueue + LocalQueue ✅ Queue + Queue
DRA 集成 ✅ 原生支持 🔄 开发中 ❌ 不支持
适用场景 GPU 密集型 AI 集群 CPU/GPU 混合批处理 MPI/HPC 传统作业

技术优缺点与适用场景

技术优势

维度 优势 量化收益
拓扑感知 DRA + Topology Manager 确保 GPU 通信路径最优 训练吞吐量提升 20-35%
资源利用率 MIG 分区 + 共享模式减少 GPU 空闲 推理成本降低 60-70%
调度效率 KAI Gang 调度消除部分调度浪费 GPU 利用率从 60% 提升至 85%
声明式管理 ResourceClaim + DeviceClass 声明式设备管理 运维复杂度降低 50%
可观测性 DCGM + PodResources 构建细粒度监控 故障定位时间从小时级降至分钟级

现存局限

局限 影响 缓解方案
DRA 仅 K8s 1.34+ 旧集群无法使用 分阶段迁移,Device Plugin 共存
MIG 重配置需 GPU 重置 配置变更需驱逐 Pod 维护窗口执行,GPU Operator 自动化
KAI 与 kube-scheduler 分裂 需显式指定 schedulerName 统一标注规范,CI 检查
驱动版本碎片化 节点间驱动不一致导致 CUDA 错误 GPU Operator 统一管理驱动版本
MIG 分区数上限 7 大规模多租户场景不够 结合 MPS 或 Time-slicing 分层

生产适用场景

复制代码
[场景选择决策树]

[AI 工作负载类型]
    │
    ├── 大模型训练(70B+)
    │      │
    │      └── 整卡独占 + KAI Gang 调度 + NVLink 拓扑约束
    │
    ├── 微调任务(7-13B)
    │      │
    │      └── 整卡独占 + KAI 队列调度
    │
    ├── 生产推理(小模型 < 8B)
    │      │
    │      └── MIG 1g.10gb 分区 + kube-scheduler
    │
    ├── 生产推理(大模型 70B+)
    │      │
    │      └── 整卡或 MIG 3g.40gb + kube-scheduler
    │
    ├── 低延迟批处理推理
    │      │
    │      └── MPS 共享 + kube-scheduler
    │
    ├── 开发 Notebook
    │      │
    │      └── Time-slicing(4-8 副本)+ kube-scheduler
    │
    └── CI / 模型验证
           │
           └── Time-slicing 或 MIG + 短 TTL

实战落地:生产级 GPU 集群部署

环境准备

集群要求

  • Kubernetes v1.34+(DRA GA 版本)
  • containerd 作为容器运行时
  • GPU 节点已安装 NVIDIA 物理驱动(或由 GPU Operator 自动安装)
  • Helm v3.x

节点标签与污点

bash 复制代码
# 标签 GPU 节点
kubectl label nodes gpu-node-01 node-type=gpu
kubectl label nodes gpu-node-01 gpu-product=nvidia-h100

# 添加污点,确保非 GPU 工作负载不会调度到 GPU 节点
kubectl taint nodes gpu-node-01 nvidia.com/gpu=present:NoSchedule

Step 1:安装 NVIDIA GPU Operator

bash 复制代码
# 添加 NVIDIA Helm 仓库
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# 安装 GPU Operator
helm install --wait \n  -n gpu-operator --create-namespace \n  gpu-operator nvidia/gpu-operator \n  --version v24.9.2 \n  --set driver.version=550.127.05 \n  --set toolkit.enabled=true \n  --set migManager.enabled=true \n  --set dcgmExporter.enabled=true

验证安装

bash 复制代码
# 检查所有 DaemonSet 就绪
kubectl get daemonsets -n gpu-operator

# 预期输出:
# nvidia-driver-daemonset     3/3
# nvidia-container-toolkit    3/3
# nvidia-mig-manager          3/3
# nvidia-dcgm-exporter        3/3

常见问题排查

症状 可能原因 解决方案
Driver DaemonSet CrashLoop 内核版本与驱动不兼容 检查 NVIDIA 兼容性矩阵
Container Toolkit 启动失败 containerd 配置冲突 检查 /etc/containerd/config.toml
MIG Manager 不响应 GPU 固件版本过低 升级 GPU VBIOS

Step 2:安装 NVIDIA DRA Driver

bash 复制代码
# 安装 DRA Driver(独立于 GPU Operator 的 Helm Chart)
helm install --wait \n  -n nvidia-dra-driver --create-namespace \n  nvidia-dra-driver nvidia/nvidia-dra-driver \n  --version v25.3.0 \n  --set resources.gpus.enabled=true \n  --set resources.computeDomains.enabled=true

验证 DeviceClass 注册

bash 复制代码
# 查看已注册的 DeviceClass
kubectl get deviceclasses.resource.k8s.io

# 预期输出:
# NAME              AGE
# gpu.nvidia.com    5m

# 查看节点上的设备
kubectl get devices.resource.k8s.io -o wide

# 预期输出(每个物理 GPU 一行):
# NAME           DRIVER           NODE         CLASS
# gpu-node-01-0  gpu.nvidia.com   gpu-node-01  gpu.nvidia.com
# gpu-node-01-1  gpu.nvidia.com   gpu-node-01  gpu.nvidia.com
# ...

Step 3:安装 KAI Scheduler

bash 复制代码
# 安装 KAI Scheduler
helm install --wait \n  -n kai-scheduler --create-namespace \n  kai-scheduler nvidia/kai-scheduler \n  --version v0.5.1 \n  --set global.registry=nvcr.io/nvidia/kai \n  --set queueController.enabled=true \n  --set podGrouper.enabled=true

创建队列层级

yaml 复制代码
# queue-hierarchy.yaml
apiVersion: kai.scheduler/v1alpha1
kind: Queue
metadata:
  name: team-ml
spec:
  parent: root
  resources:
    gpu:
      quota: 32
      limit: 48
      overQuotaWeight: 2
  priority: 100
---
apiVersion: kai.scheduler/v1alpha1
kind: Queue
metadata:
  name: project-training
spec:
  parent: team-ml
  resources:
    gpu:
      quota: 16
      limit: 32
  priority: 90
---
apiVersion: kai.scheduler/v1alpha1
kind: Queue
metadata:
  name: project-inference
spec:
  parent: team-ml
  resources:
    gpu:
      quota: 16
      limit: 16
  priority: 80

Step 4:提交 Gang 调度训练任务

yaml 复制代码
# training-job.yaml
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: h100-nvlink-claim
spec:
  spec:
    devices:
      requests:
      - name: gpus
        deviceClassName: gpu.nvidia.com
        count: 8
        selectors:
        - cel:
            expression: >
              device.attributes['gpu.nvidia.com'].productName
              .startsWith('NVIDIA H100')
      constraints:
      - requests: ["gpus"]
        matchAttribute: "gpu.nvidia.com/nvlinkDomain"
---
apiVersion: scheduling.run.ai/v1
kind: PodGroup
metadata:
  name: llama-finetune
spec:
  minMember: 8
  queue: project-training
  priorityClassName: train-high
---
apiVersion: batch/v1
kind: Job
metadata:
  name: llama-finetune
spec:
  parallelism: 8
  completions: 8
  template:
    metadata:
      labels:
        pod-group-name: llama-finetune
    spec:
      schedulerName: kai-scheduler
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      containers:
      - name: trainer
        image: myorg/llama-trainer:v2
        resources:
          limits:
            cpu: "8"
            memory: "64Gi"
          claims:
          - name: gpu
      resourceClaims:
      - name: gpu
        resourceClaimTemplateName: h100-nvlink-claim

部署流程

bash 复制代码
kubectl apply -f queue-hierarchy.yaml
kubectl apply -f training-job.yaml

# 监控调度状态
kubectl get podgroup llama-finetune -w
kubectl get pods -l pod-group-name=llama-finetune

Step 5:配置 GPU 监控

yaml 复制代码
# ServiceMonitor for DCGM Exporter
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: nvidia-dcgm-exporter
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  endpoints:
  - port: metrics
    interval: 15s

Grafana Dashboard 核心面板

面板 PromQL 查询 用途
GPU 利用率 DCGM_FI_DEV_GPU_UTIL{node=~"$node"} 监控计算负载
显存使用 DCGM_FI_DEV_FB_USED{node=~"$node"} 排查 OOM 风险
GPU 温度 DCGM_FI_DEV_GPU_TEMP{node=~"$node"} 散热监控
NVLink 带宽 rate(DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL[5m]) 拓扑健康检查
Pod 级 GPU DCGM_FI_DEV_GPU_UTIL * on(pod) group_left() kube_pod_info Pod 维度利用率

生产避坑经验

坑 1:驱动版本漂移

多节点集群中,不同节点的 NVIDIA 驱动版本不一致是 CUDA 错误的首要原因。症状是:同一个 Container 镜像在 Node A 上正常运行,在 Node B 上报 CUDA driver version is insufficient

解决方案 :由 GPU Operator 统一管理驱动版本,禁止手动安装。使用 nodeSelector 将对驱动版本敏感的工作负载锁定到特定节点池。

坑 2:MIG 重配置竞态

MIG 分区切换需要 GPU 重置,这会导致所有使用该 GPU 的 Pod 被终止。如果 MIG Manager 的 drain-reconfigure-uncordon 流程与 Deployment Controller 的快速重调度发生竞态,可能出现 Pod 被重新调度到正在重配置的 GPU 上的情况。

解决方案 :在维护窗口执行 MIG 重配置。使用 nvidia.com/mig.config.state.pending annotation 触发 GPU Operator 的自动化流程,并确保节点 cordon 在 drain 完成前生效。

坑 3:KAI 队列饥饿

在多团队共享集群中,如果高优先级队列持续借用低优先级队列的 burst 容量,低优先级队列的任务可能长时间无法调度。

解决方案 :合理设置 quota(保证配额)和 limit(上限配额),确保每个团队有足够的保证资源。使用 overQuotaWeight 控制借用权重,避免单一队列垄断空闲资源。

坑 4:ResourceClaim CEL 表达式静默失败

DRA 的 CEL 表达式在属性名拼写错误时不会报错,而是返回空结果,导致 ResourceClaim 无法匹配到任何设备,Pod 永久 Pending。

解决方案 :在编写 CEL 表达式前,先用 kubectl get devices.resource.k8s.io -o yaml 查看完整的设备属性列表,确认属性名拼写正确。

全文总结

2026 年的 Kubernetes GPU 调度已从"不透明的整数资源"演进为"声明式的设备管理"。核心架构变化是三层分离:

  • 设备抽象层 (DRA):ResourceClaim + DeviceClass + CEL 过滤,替代 Device Plugin 的 nvidia.com/gpu: 1 模型,支持拓扑约束、参数化选择、设备共享
  • 工作负载调度层(KAI):Gang 调度 + 层级队列 + 优先级抢占,解决分布式训练的原子调度需求,与 DRA 深度集成
  • 硬件管理层(GPU Operator + MIG/MPS/Time-slicing):驱动自动化 + 分区管理 + 监控导出,将 GPU 节点的运维复杂度降至最低

生产级 GPU 集群的关键实践:

  1. DRA 替代 Device Plugin:Kubernetes 1.34+ 集群应优先采用 DRA,获得拓扑感知和声明式设备管理能力
  2. MIG 用于生产推理:将大 GPU 切分为多个隔离实例,推理成本可降低 60-70%
  3. KAI Gang 调度用于分布式训练:消除部分调度浪费,GPU 利用率从 60% 提升至 85%
  4. GPU Operator 统一管理:驱动版本、容器工具、MIG 配置、监控导出一站式管理
  5. DCGM + Prometheus + Grafana 构建可观测性:Pod 维度的 GPU 指标是性能调优和故障排查的基础

参考资料

本期专栏更新说明

本文为《AI 工程与安全深度实战》订阅专栏持续迭代内容,专栏按初/中/高阶递进规划,长期更新 AI 云原生架构、GPU 算力工程、LLMOps 运维智能化、模型安全攻防、供应链安全、安全治理与合规实践,一次订阅,永久持续更新。

专栏推荐