实验 5: 使用 nvml-mock 进行 Fake-GPU 调度
本实验使用 NVIDIA 的 nvml-mock 库在本地 kind 集群中模拟一个高端 GPU 节点——8 张虚拟 A100 GPU。你将直接基于 main 分支构建 HAMi,然后验证 GPU 调度功能:共享、显存/算力限制、百分比显存申请以及多 GPU 分配——全部无需物理硬件。
你将得到什么
完成本实验后,你将得到一个本地 Kubernetes 集群:
- nvml-mock 让节点上报 8 张虚拟 A100 GPU(HAMi 将每张物理 GPU 切成 10 个虚拟槽位后,节点显示
nvidia.com/gpu: 80) - HAMi device-plugin 和调度器使用当前
main分支镜像运行 - 验证的 Pod 包括:单 GPU、GPU 共享、显存/算力限制、百分比显存以及多 GPU 分配
此环境中不存在真实的 CUDA 运行时。Pod 使用 busybox 并设置 CUDA_DISABLE_CONTROL=true,以阻止 HAMi 的控制库尝试真实设备访问。显存和算力限制的运行时强制执行仍需要物理 GPU。
安装全景图
整个安装过程共 10 步:
| 步骤 | 目的 | 解决什么问题 |
|---|---|---|
| 创建 kind 集群 | 引导本地 Kubernetes | 提供测试环境 |
| 构建并部署 nvml-mock | 模拟 8 张虚拟 A100 GPU | 无需硬件即可发现 GPU |
| 基于 main 构建 HAMi | 编译最新的调度器和 device-plugin | 确保 MIG 修复已包含在内 |
| 部署 HAMi | 安装控制面组件 | 启用 GPU 切分和调度 |
| 验证 GPU 资源 | 检查 nvidia.com/gpu: 80 | 确认虚拟 GPU 槽位已注册 |
| 基础 GPU 调度 | 单 GPU Pod 分配 | 验证调度器基本功能 |
| GPU 共享 | 在同一 GPU 上时间片共享 4 个 Pod | 测试并发 GPU 访问 |
| 显存与算力限制 | 强制执行 gpumem 和 gpucores | 验证资源约束 |
| 百分比显存 | 申请 30% GPU 显存 | 测试百分比分配 |
| 多 GPU 分配 | 单 Pod 绑定 2 张 GPU | 验证多 GPU 绑定 |
前提条件
- macOS
- Linux (Ubuntu)
- macOS,Intel 或 Apple Silicon 均可
- 已安装并运行 Docker Desktop 或 OrbStack
- 可使用 Homebrew
安装前提工具:
brew install kind kubectl helm git go
验证版本:
kind version # 0.20+
kubectl version --client --short # 1.31+
helm version # 3.x
go version # 1.21+
- Ubuntu 20.04 LTS 或更高版本,x86_64
- 已安装并运行 Docker Engine
安装前提工具:
# kind
KIND_VERSION=v0.23.0
curl -Lo ./kind "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64"
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && rm kubectl
# Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Go
GO_VERSION=1.24.0
curl -LO "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc && source ~/.bashrc
验证版本:
kind version # 0.20+
kubectl version --client --short # 1.31+
helm version # 3.x
go version # 1.21+
Windows 用户请使用 WSL2 配合 Ubuntu,并按照上面的 Linux 选项卡操作。
步骤 1:创建 kind 集群
kind create cluster --name nvml-mock-test
设置一次 NODE_NAME 变量——后续所有命令都会用到它:
NODE_NAME=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
echo "NODE_NAME=${NODE_NAME}"
示例输出:
NODE_NAME=nvml-mock-test-control-plane
步骤 2:构建并部署 nvml-mock
nvml-mock 提供一个虚假的 libnvidia-ml.so、虚拟的 /dev/nvidia* 设备节点以及 PCI 拓扑条目,让 HAMi 的 device-plugin 在节点上看到 8 张 A100 GPU。
2.1 克隆并构建
git clone https://github.com/NVIDIA/k8s-test-infra.git
cd k8s-test-infra
docker build -t nvml-mock:local -f deployments/nvml-mock/Dockerfile .
首次构建会下载基础镜像层,可能需要 5–10 分钟。后续构建会使用 Docker 层缓存。
2.2 加载到 kind
kind load docker-image nvml-mock:local --name nvml-mock-test
2.3 通过 Helm 安装
helm install nvml-mock oci://ghcr.io/nvidia/k8s-test-infra/chart/nvml-mock \
--set image.repository=nvml-mock \
--set image.tag=local \
--wait --timeout 120s
该 Chart 默认配置 A100 配置文件:每节点 8 张 GPU,驱动版本 550.163.01,虚假驱动根目录位于 /var/lib/nvml-mock/driver。这个驱动根路径会在步骤 4 中传给 HAMi。
2.4 验证 GPU 发现
kubectl get node ${NODE_NAME} \
-o custom-columns=NAME:.metadata.name,GPU_PRESENT:.metadata.labels.nvidia\\.com/gpu\\.present
预期输出:
NAME GPU_PRESENT
nvml-mock-test-control-plane true
步骤 3:基于 main 分支构建 HAMi
main 分支包含一个修复:当未启用 MIG 时,阻止调用 nvidia-mig-parted。从源码构建可确保该修复已包含在内,无需等待正式发布版本。
3.1 克隆并初始化子模块
cd ~
git clone https://github.com/Project-HAMi/HAMi.git
cd HAMi
git submodule update --init --recursive
3.2 构建 Docker 镜像
docker build -t hami:local -f docker/Dockerfile .
HAMi 使用三阶段 Dockerfile:Go 构建阶段、CUDA 库构建阶段以及最终运行时阶段。首次构建需要数分钟,因为它要拉取 CUDA 基础镜像;后续运行会使用层缓存。
3.3 加载到 kind
kind load docker-image hami:local --name nvml-mock-test
调度器和 device-plugin 二进制文件都打包在单个 hami:local 镜像中。
步骤 4:部署 HAMi
4.1 通过 Helm 安装
helm install hami ./charts/hami \
-n kube-system \
--set devicePlugin.image.repository=hami \
--set devicePlugin.image.tag=local \
--set scheduler.image.repository=hami \
--set scheduler.image.tag=local \
--set devicePlugin.nvidiaDriverRoot=/var/lib/nvml-mock/driver \
--set scheduler.kubeScheduler.imageTag=v1.35.0
devicePlugin.nvidiaDriverRoot 让 HAMi 指向由 nvml-mock 安装的虚假驱动库。
4.2 给节点打标签
device-plugin 启动前必须执行此操作 HAMi device-plugin DaemonSet 的 NODE SELECTOR 为 gpu=on。如果没有这个标签,DESIRED 会保持为 0,不会调度任何 Pod,也不会注册任何 GPU。
kubectl label node ${NODE_NAME} gpu=on
确认 DaemonSet 现在调度了一个 Pod:
kubectl -n kube-system get daemonset hami-device-plugin
预期输出:
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
hami-device-plugin 1 1 0 1 0 gpu=on 4m22s