2.2 GPU 资源建模:Device Plugin、CDI 与 Extended Resource Kubernetes 并不天生认识 GPU。要让它知道「这台节点上有 8 张 A100」、让 Pod 能声明「我要 1 张 GPU」,需要一整套发现、上报、调度、隔离的机制——这就是本节要讲清的三件套:Device Plugin、CDI、Extended Resource。 2.2.1 问题:如何让 K8s 认识一块 GPU K8s 原生管理的资源有两类:可压缩资源(CPU、设备带宽) 和 不可压缩资源(内存、磁盘)。这些都是「连续可分」的——0.5 核 CPU 是合法的申请。但 GPU 是离散设备:要么整张给一个 Pod,要么不给,没有「0.
Kubernetes 并不天生认识 GPU。要让它知道「这台节点上有 8 张 A100」、让 Pod 能声明「我要 1 张 GPU」,需要一整套发现、上报、调度、隔离的机制——这就是本节要讲清的三件套:Device Plugin、CDI、Extended Resource。
K8s 原生管理的资源有两类:可压缩资源(CPU、设备带宽) 和 不可压缩资源(内存、磁盘)。这些都是「连续可分」的——0.5 核 CPU 是合法的申请。但 GPU 是离散设备:要么整张给一个 Pod,要么不给,没有「0.5 张 GPU」的概念(除非用 MIG/MPS 切分,见第 3 章)。
更关键的是,K8s 调度器默认只认识上述两类内置资源,并不知道节点上挂着什么 PCIe 设备。要让 GPU 进入 K8s 的视野,需要回答三个问题:
这三步对应三个机制:Device Plugin(发现+上报)、Extended Resource(资源计量)、CDI(标准化的设备注入)。
Device Plugin(设备插件) 是 K8s 提供的扩展机制,允许第三方厂商把各种硬件设备(GPU、FPGA、SR-IOV 网卡、InfiniBand HCA 等)接入 K8s。NVIDIA 提供的 k8s-device-plugin 是 AI 场景最常用的实现。
Device Plugin 的工作流程是一个清晰的注册-分配回路:
这个流程的关键步骤:
nvidia.com/gpu)。["GPU-abc", "GPU-def"])。nvidia.com/gpu: 1,调度器根据节点上报的可用数量过滤节点。Allocate 方法,Device Plugin 返回要挂载的设备文件路径与驱动库。/dev/nvidia0、CUDA 运行时库等挂载进容器,容器内进程即可使用 GPU。💡 判读:Device Plugin 的本质是一个「设备经纪人」——它在 K8s 与硬件之间充当翻译,让 K8s 不需要为每种硬件写专门的调度逻辑,只要遵守 Device Plugin 接口,任何设备都能接入。
Device Plugin 注册的资源(如 nvidia.com/gpu)属于 Extended Resource(扩展资源)。这是 K8s 用来管理「整数计量的扩展资源」的机制,有几个特性:
| 资源类型 | 计量 | 是否可超售 | 典型例子 |
|---|---|---|---|
| CPU | millicores(毫核) | 是 | cpu: 500m |
| 内存 | 字节 | 否(但不强制) | memory: 4Gi |
| Extended Resource | 整数 | 否 | nvidia.com/gpu: 1 |
Extended Resource 的设计哲学是「专门给离散且不可超售的硬件资源用」,因此非常适合 GPU、FPGA、专用加速卡。
⚠️ 注意:原生 Extended Resource 只支持「整张分配」。要实现「一个 Pod 用半张 GPU」或「多个 Pod 共享一张 GPU」,需要配合 MIG、MPS 或时间分片机制(详见第 3 章)。这是 GPU 调度从「独占」走向「共享」的关键演进。
Device Plugin 机制虽然解决了 GPU 接入问题,但有三个长期痛点:
Allocate 方法返回的是「要挂载的设备文件 + 环境变量」,但具体怎么挂载(哪些库、哪些环境变量、什么权限)由各 Device Plugin 自己写,逻辑分散且不一致。为了解决这些问题,CNCF 的 Container Device Interface(CDI,容器设备接口) 应运而生。CDI 的核心思想是:把设备注入标准化为一份 JSON 描述文件,让 Device Plugin 只负责生成 CDI 文件,容器运行时只负责解析 CDI 文件,二者彻底解耦。
CDI 把设备描述抽象成一份「设备规格」JSON,包含:
nvidia.com/gpu=GPU-abc。/dev/nvidia0)、驱动库。CUDA_VISIBLE_DEVICES)。容器运行时(如 containerd 1.7+)原生支持 CDI,Pod 声明要某个 CDI 设备,运行时直接按 JSON 描述挂载,Device Plugin 不再需要关心运行时细节。
| 维度 | Device Plugin(旧) | CDI(新) |
|---|---|---|
| 注入逻辑 | 分散在各 Plugin | 统一 JSON 描述 |
| 运行时适配 | 各自适配 | 运行时统一解析 |
| 复杂设备支持 | 弱 | 强(MIG、SR-IOV) |
| 标准化程度 | 厂商自定义 | CNCF 标准 |
| 落地状态 | 主流(多数集群) | 新部署首选 |
💡 判读:CDI 不是 Device Plugin 的替代者,而是它的「注入层升级」。Device Plugin 仍然负责设备发现与上报,只是把「怎么注入容器」这件事交给标准化的 CDI 文件。新建 AI 集群推荐直接用支持 CDI 的 NVIDIA Device Plugin 版本。
Device Plugin、Extended Resource、CDI 三者不是替代关系,而是堆叠的层级,各管一段:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 360" font-family="sans-serif" font-size="13"> <!-- 标题 --> <text x="360" y="24" text-anchor="middle" font-size="15" font-weight="bold">GPU 资源建模三层关系</text> <!-- 三层堆叠 --> <rect x="60" y="50" width="600" height="70" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/> <text x="360" y="78" text-anchor="middle" font-weight="bold" fill="#9d174d">Device Plugin:发现 + 上报</text> <text x="360" y="100" text-anchor="middle" font-size="12" fill="#831843">gRPC 注册到 Kubelet,ListAndWatch 上报设备,Allocate 返回注入规格</text> <rect x="60" y="140" width="600" height="70" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/> <text x="360" y="168" text-anchor="middle" font-weight="bold" fill="#854d0e">Extended Resource:资源计量</text> <text x="360" y="190" text-anchor="middle" font-size="12" fill="#713f12">整数计量、不可超售、调度器按节点过滤(nvidia.com/gpu: 1)</text> <rect x="60" y="230" width="600" height="70" rx="8" fill="#dbeafe,stroke:#2563eb" stroke="#2563eb" stroke-width="1.5"/> <text x="360" y="258" text-anchor="middle" font-weight="bold" fill="#1e40af">CDI:标准化注入</text> <text x="360" y="280" text-anchor="middle" font-size="12" fill="#1e3a8a">JSON 设备规格,容器运行时统一解析,支持 MIG/SR-IOV</text> <!-- 关系箭头 --> <text x="40" y="320" font-size="12" fill="#475569">三者各管一段:Plugin 发现→Resource 计量→CDI 注入</text> </svg>
把三层串起来理解:
一个 Pod 申请 GPU 的声明(伪代码示意,非完整 YAML):
spec: containers: - name: trainer resources: limits: nvidia.com/gpu: 1 # 申请 1 张 GPU cpu: 8 memory: 64Gi
调度器的工作就是:扫描集群中所有节点的 nvidia.com/gpu 可用数量,找到剩余 >= 1 的节点,结合其他约束(亲和、污点)打分,选一个节点落地。落到节点后,Kubelet 调用 Device Plugin + CDI 把 GPU 注入容器,容器内 nvidia-smi 即可看到分配的卡。
Device Plugin + Extended Resource + CDI 这套机制解决了「GPU 进入 K8s」的问题,但它的资源模型是整张独占的——一张 GPU 一次只能给一个 Pod。这导致 1.1 节讲的「GPU 孤岛、利用率低」问题依然存在。
要实现「一张 GPU 给多个 Pod 共享」,需要在此基础上引入 GPU 切分技术(MIG、MPS、时分复用),这就是第 3 章的开篇主题。可以把第 2 章和第 3 章理解为:
💡 判读:理解了「Device Plugin 解决接入、Extended Resource 解决计量、CDI 解决注入」这三层关系,后续学共享 GPU(MIG/MPS)与拓扑感知调度时,就能明白它们都是在「Device Plugin 之上」叠加的新能力,而非另起炉灶。
下一节《2.3 GPU/CPU 混合调度与异构资源管理》将讲清一个多型号 GPU 集群如何被组织、调度与隔离。