2026-01-12

gpu-manager 架构:Device Plugin、vCUDA 与迁移边界

复盘 tkestack/gpu-manager 的 Device Plugin、vCUDA 资源模型、库注入和拓扑分配,并说明旧 CUDA 兼容边界与现代迁移方案。

状态说明(2026-09-03):tkestack/gpu-manager 公开仓库的最新 Release 仍是 2020 年 5 月发布的 v1.1.0;配套 vcuda-controller 文档声明支持 CUDA 11.5.1 及以前版本。这里把它当作 GPU 虚拟化架构案例,不建议直接用于 2026 年新集群选型。

gpu-manager 的价值不在“还能不能原样部署”,而在它把 Kubernetes GPU 共享拆成了几个至今仍有效的问题:调度器如何表达分数资源,kubelet 如何向容器交付虚拟设备,运行时如何限制显存和计算,以及节点重启后如何恢复分配状态。

先看它解决的三个问题

  1. 资源表达:把整张 GPU 拆成 vcuda-core 与 vcuda-memory 两种可调度扩展资源。
  2. 容器交付:通过 Device Plugin 的 Allocate 响应向容器注入设备、目录、环境变量和控制库。
  3. 运行时约束:通过 vCUDA 库拦截 CUDA 调用,执行配额检查、设备视图重写和使用量采集。

这三层必须同时成立。只有调度记账,没有运行时约束,Pod 仍可能超额使用;只有拦截库,没有调度器账本,平台又不知道该把任务放到哪张卡。

组件边界

Pod / Extended Resources
          |
          v
gpu-admission / scheduler integration
          |
          v
gpu-manager DaemonSet
  - GPU discovery
  - topology and allocation state
  - Device Plugin registration
  - Allocate response
          |
          v
vCUDA controller library in container
          |
          v
NVIDIA driver and physical GPU
  • gpu-manager 运行在 GPU 节点,发现设备、维护分配状态并向 kubelet 注册 Device Plugin。
  • gpu-admission / 调度集成 处理分数资源的节点选择和拓扑约束。
  • vcuda-controller 提供容器内 CUDA 拦截库,让调度结果在运行时可执行。
  • checkpoint 与指标服务 用于恢复节点分配状态、暴露设备和任务使用信息。

这是一个强耦合系统:gpu-manager、vCUDA 库、NVIDIA 驱动、CUDA Runtime 与容器运行时必须形成经过验证的兼容矩阵。

启动链路:从节点发现到 Device Plugin 注册

启动过程可以压缩成七步:

  1. 读取节点配置、设备白名单、资源粒度与调度策略。
  2. 通过 NVML/驱动接口发现 GPU、显存与拓扑关系。
  3. 初始化本地分配状态,必要时加载 checkpoint。
  4. 准备容器需要挂载的驱动库与 vCUDA 控制库。
  5. 启动 vcuda-core、vcuda-memory 等 Device Plugin 服务。
  6. 向 kubelet 注册扩展资源,并通过 ListAndWatch 持续报告健康状态。
  7. 收到 Allocate 请求后,返回设备节点、Mount、环境变量和运行时配置。

Kubernetes Device Plugin 只定义资源注册与分配接口,不理解“20% GPU”具体如何执行。gpu-manager 必须在插件之外维护物理 GPU、虚拟份额与容器之间的映射。

vCUDA 资源模型

项目定义两个主要扩展资源:

  • tencent.com/vcuda-core:计算份额;100 表示一张完整 GPU,20 表示约 20% 计算份额。
  • tencent.com/vcuda-memory:显存份额;一个单位表示 256 MiB,因此 32 表示 8 GiB。

vcuda-core 小于 100 时表示单卡分数份额,大于等于 100 时通常要求使用 100 的整数倍。它不是 Kubernetes 原生 GPU 百分比,语义完全由 gpu-manager 与 vCUDA 运行时共同实现。

示例只展示资源契约,不代表当前集群可以直接运行:

apiVersion: v1
kind: Pod
metadata:
  name: vcuda-demo
  annotations:
    tencent.com/vcuda-core-limit: "50"
spec:
  restartPolicy: Never
  containers:
    - name: workload
      image: <your-registry>/cuda-workload:<pinned-version>
      resources:
        requests:
          tencent.com/vcuda-core: "20"
          tencent.com/vcuda-memory: "32"
        limits:
          tencent.com/vcuda-core: "20"
          tencent.com/vcuda-memory: "32"

这里申请约 20% 计算份额和 8 GiB 显存,vcuda-core-limit 把运行上限设为 50%。实际字段行为以你锁定的 gpu-manager 版本为准。

Allocate 为什么是关键接口

Device Plugin 的 Allocate 响应可以向容器交付:

  • 设备节点
  • HostPath Mount
  • 环境变量
  • CDI Device
  • 其他运行时需要的信息

gpu-manager 利用这一步把虚拟设备状态和控制库放进容器。容器启动后,动态链接器优先加载 vCUDA 库,再由它转发或限制底层 CUDA 调用。

这比“只上报多个虚拟设备 ID”复杂,但也暴露出最重要的兼容风险:只要库路径、符号版本、Driver API 覆盖或容器运行时行为变化,Pod 可能成功调度却在 CUDA 初始化阶段失败。

CUDA 拦截能控制什么,不能控制什么

vCUDA 这类方案通常在用户态处理:

  • 设备枚举与可见性
  • cudaMalloc / cuMemAlloc 等显存分配
  • Context、Stream 与 Kernel Launch 相关调用
  • 使用量采集和配额判断

它的边界同样明确:

  • 未覆盖的新 CUDA API 可能绕过限制。
  • 静态链接、显式 dlopen、自定义 Driver API 路径会增加兼容风险。
  • 用户态拦截不是 MIG 级硬件隔离,PCIe、显存带宽、缓存和故障域仍可能共享。
  • CUDA、驱动和控制库的 ABI 变化必须逐版本回归。

因此,“Pod 能看到 GPU”不是验收结束。必须证明分配、超额失败、并发干扰、故障恢复和指标链路都符合预期。

拓扑感知为什么重要

多卡任务不能只数 GPU 数量。GPU 与 CPU NUMA、PCIe Switch、NVLink/NVSwitch 的距离会影响 Host-to-Device、Peer-to-Peer 和集合通信性能。

调度和分配至少要回答:

  • 多卡任务是否落在同一高速互联域。
  • CPU 与内存是否靠近目标 GPU 的 NUMA Node。
  • 分数任务是否把同一张卡切得过碎,阻塞后续整卡任务。
  • 节点重启后恢复的虚拟份额是否仍指向同一物理设备。

这些原则仍适用于今天的 GPU 调度器,但具体拓扑 API 和设备标识不能照搬旧项目代码。

2026 年新集群该看什么

方案 主要能力 隔离边界 适用场景
NVIDIA GPU Operator Time-Slicing 把一张卡作为多个共享副本暴露 无显存硬边界,故障域共享 可信任务、低成本并发
NVIDIA MPS 多进程并发执行、部分资源限制能力 依赖 GPU 架构和 MPS 配置 同机推理或 HPC 并发
NVIDIA MIG 硬件分区与独立资源实例 最强,但规格固定且只支持部分 GPU 多租户、强故障隔离
HAMi Device Plugin + CUDA API 层限制 强于应用自限,弱于硬件分区 异构 GPU 共享平台
KAI-Scheduler GPU Sharing Reservation Pod 与调度记账 默认不强制显存边界 调度侧分数 GPU 与队列治理

进一步选型可参考站内文章:

维护旧 gpu-manager 集群的最低检查线

  1. 锁定 Linux Kernel、NVIDIA Driver、CUDA、gpu-manager 与 vcuda-controller 版本。
  2. 保留金丝雀节点,驱动或容器运行时升级先跑兼容回归。
  3. 验证 kubelet 重启、gpu-manager 重启和节点重启后的注册与 checkpoint 恢复。
  4. 检查 vcuda-core、vcuda-memory 的 Capacity、Allocatable 与 Pod 分配结果。
  5. 在容器内确认实际加载的控制库路径和符号版本。
  6. 跑显存上限、计算配额、并发干扰、CUDA 错误传播和 OOM 测试。
  7. 采集物理 GPU、虚拟份额和 Pod 三个层级的指标,并能对应到同一时间线。
kubectl get node <gpu-node> \
  -o jsonpath='{.status.capacity.tencent\.com/vcuda-core}{"\t"}{.status.capacity.tencent\.com/vcuda-memory}{"\n"}'

kubectl describe pod -n <ns> <pod>
kubectl logs -n <gpu-manager-namespace> <gpu-manager-pod>

不要把旧 README 中的宽权限示例原样搬进生产。按当前 Kubernetes 版本收紧 ServiceAccount、HostPath、HostPID 和设备访问权限。

迁移建议

  1. 盘点所有 workload 的 vcuda-core、vcuda-memory、显存峰值和计算利用率。
  2. 把旧资源单位转换为目标方案的资源模型,不能只替换资源名。
  3. 新旧节点池并行,先迁移可重试、低优先级任务。
  4. 对同一模型比较吞吐、P95/P99 延迟、显存峰值、失败率和节点密度。
  5. 为 CUDA 初始化失败、性能回退和配额失效准备快速回滚。
  6. 最后迁移在线核心服务,并删除旧 Admission、Device Plugin 和控制库注入链路。

gpu-manager 仍值得读,因为它完整展示了“调度账本 + Device Plugin 交付 + 用户态约束”的工程组合。它不再适合作为新部署默认答案,真正可复用的是组件边界、兼容矩阵和验证方法。

参考链接