搜索中...
🔍

未找到相关结果

Akemi

HAMi GPU测试框架

字数统计: 1.6k阅读时长: 8 min
2026/07/28

起因是我发现我们客户在做GPU基准测试的时候,使用的是OrionX驱动科技的方案,并且它是优于原生的k8s调度方案的,那么有没有开源版本的类似的组件,可以让我来进行测试呢?

有的兄弟有的

GPU 虚拟化方案调研

[table]

  1. “完整 GPU 虚拟化”需要替换 libcuda.so,自己做不现实

这是核心结论。要实现真正的显存隔离 + 算力比例控制,需要在 CUDA 驱动层做拦截(hook libcuda.so),这活只有趋动科技和 HAMi 这类专业项目才做得好。

  1. NVIDIA 原生方案的局限
text
1
2
3
4
5
6
Time-Slicing: Pod A 用 10ms → Pod B 用 10ms → Pod A 用 10ms ...
(轮流用,不是同时用,显存没隔离)
MPS: Pod A 和 Pod B 真正同时跑
(并发了,但显存没限额,A 能把显存吃光)
MPS + TS: 多组 Pod 轮流,每组内并发
(调度+执行都超分了,但显存还是没硬限制)
  1. HAMi 的优势(相对其他方案)

[table]

  1. 为什么选 HAMi 做测试
  • 开源免费,没有厂商锁定
  • CNCF 孵化项目,社区有保障
  • 能做到显存限额 + 算力比例(MPS/TS 做不到)
  • K8s 原生集成,部署简单
  • 是 OrionX 的直接开源替代

kind集群

单节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
apiServerAddress: "0.0.0.0"
apiServerPort: 6443
nodes:
- role: control-plane
extraMounts:
- hostPath: /usr/lib/wsl/lib
containerPath: /usr/lib/wsl/lib
readOnly: true
- hostPath: /dev
containerPath: /dev
- hostPath: /usr/lib/wsl/drivers
containerPath: /usr/lib/wsl/drivers
readOnly: true
- hostPath: /proc/driver
containerPath: /host/proc/driver
readOnly: true

kind create cluster --name gpu-test --config kind-gpu-config.yaml

kubectl get nodes
NAME STATUS ROLES AGE VERSION
gpu-test-control-plane Ready control-plane 76m v1.32.2

环境准备

因为我使用的是笔记本(WSL2+kind)搭建单节点k8s集群,所以会需要做一些额外步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
docker exec gpu-test-control-plane bash -c '
export http_proxy=http://192.168.10.238:7897
export https_proxy=http://192.168.10.238:7897
export no_proxy=deb.debian.org,debian.org
rm -f /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
rm -f /etc/apt/sources.list.d/nvidia-container-toolkit.list
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --batch --yes --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed "s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g" | \
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt-get update && apt-get install -y nvidia-container-toolkit
'

# 验证
docker exec gpu-test-control-plane nvidia-container-cli info
NVRM version: 610.62
CUDA version: 13.3
Device Index: 0
Device Minor: 0
Model: NVIDIA GeForce RTX 4050 Laptop GPU
Brand: GeForce
GPU UUID: GPU-2b3d8a57-d275-92ab-c84a-638c4a872e45
Bus Location: 00000000:01:00.0
Architecture: 8.9

NVIDIA容器运行时

containerd针对于1.x版本和2.0版本的配置文件,容器运行时字段不同:

[table]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 我的是containerd 2.0的,我直接写子配置文件
docker exec gpu-test-control-plane bash -c '
cat > /etc/containerd/conf.d/99-nvidia.toml << EOF
[plugins."io.containerd.cri.v1.runtime".containerd]
default_runtime_name = "nvidia"

[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime"
EOF

cat > /etc/containerd/conf.d/98-runc.toml << EOF2
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
EOF2

systemctl restart containerd
'

# 验证
docker exec gpu-test-control-plane crictl info 2>&1 | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(list(d['config']['containerd']['runtimes'].keys()))
"
['nvidia', 'runc']

Helm部署HAMi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 添加与更新
helm repo add hami-charts https://project-hami.github.io/HAMi/
export https_proxy=http://192.168.10.238:7897
export http_proxy=http://192.168.10.238:7897
helm repo update

helm search repo hami-charts
NAME CHART VERSION APP VERSION DESCRIPTION
hami-charts/hami 2.9.0 2.9.0 Heterogeneous AI Computing Virtualization Middl...

# 拉取
helm pull hami-charts/hami --version 2.9.0 --untar
ls hami/
Chart.lock Chart.yaml README.md charts templates values.yaml

# values.yaml中是HAMi框架自己本身的参数
resourceName: "nvidia.com/gpu" # GPU 设备数
resourceMem: "nvidia.com/gpumem" # 显存(MB)
resourceCores: "nvidia.com/gpucores" # 算力(百分比)
deviceSplitCount: 10 # 每张物理卡分几个 vGPU
deviceMemoryScaling: 1 # 显存超分倍率(1=不超分)
deviceCoreScaling: 1 # 算力超分倍率

# 部署
cd hami
helm upgrade --install hami ./ -f values.yaml --create-namespace -n hami

验证:HAMi性能限制

故意超限,看会不会被拦住

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
kind load docker-image nvidia/cuda:12.0.0-base-ubuntu22.04 --name gpu-test

# 创建两个pod
cat > gpu-pod-test-a.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: test-mem-5g
spec:
restartPolicy: Never
containers:
- name: test
image: nvidia/cuda:12.0.0-base-ubuntu22.04
command: ["sh", "-c", "nvidia-smi && echo '=== 尝试分配 2GB 显存 ===' && python3 -c 'import ctypes; libcudart = ctypes.CDLL(\"libcudart.so\"); ptr = ctypes.c_void_p(); err = libcudart.cudaMalloc(ctypes.byref(ptr), 2*1024*1024*1024); print(f\"cudaMalloc 2GB: {\"成功\" if err == 0 else f\"失败 error={err}\"}\")' 2>&1 || echo 'python3 不可用,用 nvidia-smi 验证'; sleep 300"]
resources:
limits:
nvidia.com/gpu: 1
nvidia.com/gpumem: 5000
nvidia.com/gpucores: 100
EOF

cat > gpu-pod-test-b.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: test-mem-1g
spec:
restartPolicy: Never
containers:
- name: test
image: nvidia/cuda:12.0.0-base-ubuntu22.04
command: ["sh", "-c", "nvidia-smi && echo '=== 尝试分配 2GB 显存 ===' && python3 -c 'import ctypes; libcudart = ctypes.CDLL(\"libcudart.so\"); ptr = ctypes.c_void_p(); err = libcudart.cudaMalloc(ctypes.byref(ptr), 2*1024*1024*1024); print(f\"cudaMalloc 2GB: {\"成功\" if err == 0 else f\"失败 error={err}\"}\")' 2>&1 || echo 'python3 不可用,用 nvidia-smi 验证'; sleep 300"]
resources:
limits:
nvidia.com/gpu: 1
nvidia.com/gpumem: 1000
nvidia.com/gpucores: 100
EOF
kubectl apply -f gpu-pod-test-a.yaml
kubectl apply -f gpu-pod-test-b.yaml

Pod A:拿到 1 个 vGPU,显存上限 5000MB → 占用100%

Pod B:拿到 1 个 vGPU,显存上限 1000MB → 占用100%

正常应该B拿不到,因为A已经占用100%

1
2
3
4
kubectl get pods -w
NAME READY STATUS RESTARTS AGE
test-mem-1g 0/1 Pending 0 99s
test-mem-5g 1/1 Running 0 99s

查看kubectl describe pod test-mem-1g,可以看到是被hami拦下来了

1
2
3
4
Warning  FailedScheduling  116s                hami-scheduler  0/1 nodes are available: 1 NodeUnfitPod. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
Warning FailedScheduling 84s (x2 over 115s) hami-scheduler 0/1 nodes are available: 1 NodeUnfitPod. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
Warning FilteringFailed 26s (x6 over 117s) hami-scheduler 1 nodes CardInsufficientCore(gpu-test-control-plane)
Warning FilteringFailed 26s (x6 over 117s) hami-scheduler no available node, 1 nodes do not meet

性能/业务基准测试

在开始测试之前,首先需要一个基线

[table]

场景 A — 独占模式

Pod 不设 gpumem/gpucores,独占整张卡。跑 gpu-burn 60 秒,记录 TFLOPS。

场景 B — 共享模式

Pod 设 gpumem: 3000 + gpucores: 50。跑同样的 gpu-burn 60 秒,记录 TFLOPS。

对比

[table]

↑以上指的是使用通用压测工具的思路,

但是实际生产中,会使用业务提供的脚本进行压测

CATALOG
  1. 1. GPU 虚拟化方案调研
  2. 2. kind集群
  3. 3. 环境准备
  4. 4. NVIDIA容器运行时
  5. 5. Helm部署HAMi
  6. 6. 验证:HAMi性能限制
  7. 7. 性能/业务基准测试