GPU 虚拟化原理
在 AI 推理场景中,一个常见的困境是:GPU 很贵,但大多数时候都是闲的。
一个典型的推理服务往往只占用 GPU 20%~40% 的算力和少量显存,剩余资源就这样空转着。Kubernetes 的默认 GPU 调度模型偏偏是独占的:nvidia.com/gpu: 1 意味着整张卡归你,其他 Pod 一律等待。想让多个推理服务共享一张 GPU?标准 Device Plugin 做不到,因为它只能向调度器上报设备数量(整数),根本没有"显存配额"这个概念。
于是出现了各种 GPU 共享方案。NVIDIA 官方的时间切片(Time-Slicing)可以让多个 Pod 同时被调度,但没有显存隔离,一个 Pod OOM 会拖垮整张卡上的所有任务。MIG 硬件分 区有真正的隔离,但只有 A100、H100 这类数据中心级卡才支持。
HAMi 走了另一条路:不改驱动、不改应用,通过 CUDA API 劫持在软件层实现 GPU 虚拟化,多个 Pod 共享同一张物理 GPU,每个 Pod 只能"看到"自己申请的那部分显存,超额分配直接返回 OOM。这是一个 CNCF Sandbox 项目,前身为 k8s-vGPU-scheduler。
本文先从 Kubernetes GPU 调度的原理讲起,理解默认模型的局限性,再深入 HAMi 的架构和实现,看它是如何绕过这些限制的。
Kubernetes GPU 调度原理
Device Plugin
Kubernetes 原生不直接管理 GPU 等异构硬件资源。为此,Kubernetes 提供了 Device Plugin 扩展机制,允许硬件厂商将自定义设备资源注册到 Kubelet,供调度器使用。
Device Plugin 本身以 DaemonSet 方式部署,运行在每个 GPU 节点上,负责向 Kubelet 注册设备、上报资源、响应分配请求。下图展示了从 Device Plugin 启动到 GPU Pod 运行的完整时序:
各步骤说明如下:
| 步骤 | 参与方 | 说明 |
|---|---|---|
| ① | Kubelet | 启动时创建 Registration gRPC 服务,监听 kubelet.sock,等待 Device Plugin 注册 |