实验 10: 使用虚拟 GPU 进行拓扑感知调度
本实验将带你在单节点本地集群上使用 nvml-mock 和 HAMi 模拟一套非对称的 PCIe 拓扑。你将启用 HAMi 的拓扑感知调度器,注入自定义的连接性分数来孤立某一张 GPU,然后验证:多 GPU 请求会避开这张被孤立的 GPU,而单 GPU 请求则会选中它。无需物理 GPU —— 一切都在本地 Kubernetes 集群内完成。
你将得到什么
完成本实验后,你将获得:
- 一个使用 nvml-mock 模拟 8 张 A100 GPU 的本地集群(HAMi 将每张 GPU 切分为 10 份后共 80 个虚拟槽位)
- 从已验证的提交构建并安装 HAMi,启用拓扑感知调度(
gpuSchedulerPolicy=topology-aware) - 一个节点注解(
hami.io/node-nvidia-score),定义了一套自定义拓扑,其中 GPU7 与其余所有 GPU 的连接都很差 - 证明调度器在为单个 Pod 分配 2 张 GPU(多 GPU 请求)时会避开 GPU7,而在单 GPU 请求时会选中 GPU7 —— 这两种行为都来自同一个
topology-aware策略,无需额外开关 - 通过调度器自身日志洞察其拓扑决策过程
本实验中的虚拟 GPU 拓扑分数是人为构造的 —— nvml-mock 默认上报对称的连接性,我们手动覆盖节点注解来人为制造一张"连接最差"的 GPU。本实验验证的是调度器针对已知拓扑的处理逻辑,而不是真实的 PCIe/NVLink 测量结果。
分数方向很重要。 在 HAMi 实际的调度代码中(pkg/device/nvidia/device.go),成对分数越高代表连接性越好(类似 NVLink),分数越低代表越差。多 GPU 请求会选择总分最高的组合(通过 computeBestCombination);单 GPU 请求会选择分数最低的设备(通过 computeWorstSingleCard)—— 这两条路径都由 Fit() 中同一个 needTopology 判断门控,完全由 gpuSchedulerPolicy=topology-aware 驱动。单 GPU 评分没 有单独的开关;策略一旦设置,它默认就是开启的。为了让 GPU7 成为连接最差的设备,我们把它的分数设在 50 基准线以下,而不是以上。
安装概览
整个实验共分 7 个步骤:
| 步骤 | 目的 | 解决的问题 |
|---|---|---|
| 搭建并验证环境 | 创建/验证集群,检查工具 | 确保有可用的 Kubernetes 集群 |
| 构建 nvml-mock | 模拟 8 张 A100 GPU | 为 device plugin 提供可读取的 NVML 拓扑 |
| 构建并安装 HAMi | 部署带 topology-aware 策略的调度器 | 使调度器在单 GPU 和多 GPU 请求中都能考虑连接性分数 |
| 孤立 GPU7 | 冻结 device plugin 并覆盖拓扑注解 | 制造一张已知连接性差的 GPU 用于测试 |
| 提高调度器日志级别 | 将调度器日志级别调到 -v=6 | 暴露 best device combination / worst device 拓扑日志行 |
| 验证调度行为 | 多 GPU Pod + 单 GPU Pod | 确认避开/选中行为与注入的拓扑一致 |
| 观察逐设备分数 | -v=6 级别的调度器日志 | 展示真实的 best device combination / worst device 日志行 |
前提条件
- macOS (OrbStack)
- Linux (Ubuntu + kind)
- macOS,Intel 或 Apple Silicon
- 已安装 OrbStack 并启用内置 Kubernetes
docker、git、python3- 可访问 GitHub、GHCR 和 HAMi Helm 仓库
- 至少 8 GB 空闲内存和 4 个 CPU 核心
OrbStack 自带内置 Kubernetes(基于 k3s),无需单独安装 kind 或 Docker Desktop。它占用资源更少、启动更快,是 macOS 上本地实验的首选。
检查 Helm:
helm version
如果未安装 Helm:
brew install helm
- Ubuntu 20.04 LTS 或更高版本,x86_64 或 ARM64
- Docker Engine、
kindv0.20+、kubectl、Helm 3.x git、python3- 可访问 GitHub、GHCR 和 HAMi Helm 仓库
- 至少 8 GB 空闲内存和 4 个 CPU 核心
kind(Kubernetes IN Docker)在 Docker 容器内运行完整的 Kubernetes 集群。它可以在任何安装了 Docker 的 Linux 发行版上运行,无需特殊的系统集成,是 Linux 上本地 Kubernetes 开发的标准工具。
如果需要安装以上前提条件,运行以下命令块:
# Docker Engine
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
# git、python3
sudo apt-get update && sudo apt-get install -y git python3
# kind(自动适配架构:amd64 或 arm64)
KIND_VERSION=v0.23.0
ARCH=$(dpkg --print-architecture)
curl -Lo ./kind "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-${ARCH}"
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
# kubectl(自动适配架构:amd64 或 arm64)
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/${ARCH}/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
步骤 1:搭建并验证本地环境
为什么:我们需要一个可用的 Kubernetes 集群来部署 nvml-mock 和 HAMi。
- macOS
- Linux
在 OrbStack UI 中启用后,其内置的 Kubernetes 会自动启动。验证集群是否就绪:
kubectl version
示例输出:
Client Version: v1.33.9
Kustomize Version: v5.6.0
Server Version: v1.33.9+orb1
Server Version 中的 +orb1 后缀标识了 OrbStack 内置的 Kubernetes 发行版。
创建本地 Kubernetes 集群:
kind create cluster --name topo-lab --image kindest/node:v1.35.0
示例输出:
Creating cluster "topo-lab" ...
✓ Ensuring node image (kindest/node:v1.35.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-topo-lab"
--name topo-lab 参数为集群命名,生成的节点将被称为 topo-lab-control-plane。kind 会自动将 kubectl 上下文切换到新集群。