异构算力虚拟化中间件HAMi介绍与简单使用

一、介绍

概述

​ HAMi,英文全称是“Heterogeneous AI Computing Virtualization Middleware”,是一个GPU与加速卡(支持的GPU与国产加速卡型号与具体特性请查看此项目官网: https://github.com/Project-HAMi/HAMi/ )虚拟化国产开源项目,实现了以kubernetes为基础的容器场景下GPU或加速卡虚拟化。此项目最初由第四范式公司开源,原名“k8s-vGPU-scheduler”,后改名为HAMi(中文名为哈蜜),经过两三年的发展,现已在国内与国际上愈加流行,成为小型或科研机构提供容器场景下GPU或加速卡设备虚拟化的解决方案雏形或基础。它提供了管理多种不同类型的异构设备(目前支持NVIDIA GPU、寒武纪MLU、天数智芯GPU、中科署光DCU、华为昇腾NPU、摩尔线程GPU)的能力,能够在Pod之间共享异构设备,根据设备的拓扑信息和调度策略做出更好的调度决策。

​ 考虑到当前国内人工智能、高性能计算、机器学习等需要大量计算任务的工作负载对NVIDIA GPU及其他国产加速卡如海光DCU、昇腾NPU、寒武纪MLU等的使用特点。第一,其中一个显著的特点就是一个加速卡最多只能分配给一个任务,这个加速卡的资源如计算核心与显存并未被充分复用,其中很大一部分内存可能被浪费掉了,HAMi这一开源项目能够解决这一问题。第二,HAMi使得能够在同一个k8s环境中同时支持对多个不同品牌的加速设备(异构加速设备)的虚拟共享,这就是HAMi名称中异构的来源。(笔者目前的理解,其他功能如“动态MIG切片”还有待进一起深入研究学习中)。

能力与特性

​ HAMi 提供了针对包括NVIDIA GPU在内的等异构设备的虚拟化能力,能力主要包括两个方面即设备复用与资源隔离。

  • 设备复用:
    • 允许通过指定显存来申请虚拟算力设备
    • 允许通过指定算力使用比例来申请虚拟算力设备
  • 资源隔离:
    • 算力资源的硬隔离

​ HAMi 调度器提供了管理GPU集群的能力,具体包括如下功能特性(不同GPU或加速卡支持的功能特性可能会稍有差别,我只用过少数几种,此处未能完整归纳总结。以前官网提供了功能特性支持矩阵,最近删除了):

  • GPU 共享: 每个任务可以只占用一部分显卡,多个任务可以共享一张显卡
  • 可限制分配的显存大小: 可以用显存值(例如3000M)或者显存比例(例如50%)来分配GPU,vGPU调度器会确保任务使用的显存不会超过分配数值
  • 可限制分配的计算核心数: 可以用计算核心比例(例如30%)来分配GPU
  • 虚拟显存: 你可以超额使用显存,并将内存当作显存的交换区使用
  • 指定GPU型号:当前任务可以通过设置annotation的方式,来选择使用或者不使用某些具体型号的GPU
  • 无侵入: vGPU调度器兼容nvidia官方插件的显卡分配方式,所以安装完毕后,你不需要修改原有的任务文件就可以使用vGPU的功能。当然,你也可以自定义的资源名称

构架设计

​ HAMi项目官网是:https://github.com/Project-HAMi/HAMi/tree/master ,目前(2025年1月初)稳定版本是v2.4.1 。

​ HAMi 包含以下几个组件,一个统一的mutatingwebhook,一个统一的调度器,以及针对各种不同的异构算力设备对应的设备插件和容器内的控制组件,整体的架构特性如下图所示。

hami-arch.png

二、部署升级与卸载

安装要求

  • NVIDIA drivers >= 440
  • nvidia-docker version > 2.0
  • docker/containerd/cri-o,已配置nvidia作为默认runtime
  • Kubernetes version >= 1.16
  • glibc >= 2.17 & glibc < 2.3.0
  • kernel version >= 3.10
  • helm > 3.0

安装

​ 以下以安装与使用HAMi2.4.0为例进行说明。

执行安装

1
2
3
4
5
6
7
8
9
10
11
12
13
#查看k8s版本
root@controller01:~# kubectl version --short
Client Version: v1.23.10
Server Version: v1.23.10

#首先需要将所有要使用到的GPU节点打上gpu=on标签,没有此标签的k8s节点会被HAMi忽略 
root@controller01:~# kubectl label node {nodename} gpu=on
#在 helm 中添加hami chart仓库
root@controller01:~# helm repo add hami-charts https://project-hami.github.io/HAMi/
#使用以下命令进行部署#并客制化参数,resourceName是申请vgpu个数的资源名,默认值是"nvidia.com/gpu",它跟NVIDIA device pulgin安装后生成的资源名一样,为了区别二者,此处将其客制化为"nvidia.com/vgpu"
#参数“--version 2.4.0“是指定此次安装的 HAMi 版本,如果不指定的话,默认安装最新版本2.4.1
root@controller01:~# helm install hami hami-charts/hami --set resourceName=nvidia.com/vgpu -n kube-system --version 2.4.0
#安装耗时可能要几分钟,安装完成时输出如下
image-20250104180859741

​ 上述“helm install”命令可以客制化的参数较多,比如:

  • devicePlugin.deviceSplitCount:

    整数类型,预设值是10。GPU的分割数,每一张GPU都不能分配超过其配置数目的任务。若其配置为N的话,每个GPU上最多可以同时存在N个任务

  • resourceMemPercentage:

    字符串类型,申请vgpu显存比例资源名,默认: "nvidia.com/gpumem-percentage"

  • 其他可客制化参数

    可以直接参考官方文档:https://github.com/Project-HAMi/HAMi/blob/v2.4.0/docs/config_cn.md

确认安装情况

1
2
#1.查看已发布版本
root@controller01:~# helm -n kube-system list
image-20250104180953889
1
2
3
4
5
6
7
8
#2.查看k8s集群内资源对象,正常情况类似如下
root@controller01:~# kubectl -n kube-system get all -l app.kubernetes.io/component
#每个被打了标签“gpu=on”的k8s节点上都会有一个名为hami-device-plugin-xxx的pod;
#同时会有一个名为hami-scheduler-xxx 的pod
#会有hami-device-plugin-monitor、hami-scheduler两个service
#会有一个名为hami-device-plugin的daemonset
#会有一个名为hami-scheduler的deployment
#会有一个名为hami-scheduler-yyy的replicaset
image-20250104181215738
1
2
#3.查看k8s节点拥有的属性
root@controller01:~# kubectl describe node controller01 | more
image-20250104181300572

升级

1
2
3
#比如先前安装了HAMi2.4.0版本,现在想升级到HAMi2.4.1版本
#执行升级HAMi版本
root@controller01:~# helm upgrade hami hami-charts/hami  -n kube-system --version 2.4.1
image-20250104181337643
1
2
#查看升级后的HAMi版本
root@controller01:~# helm -n kube-system list
image-20250104181359099

卸载

1
2
#在任意目录直接执行如下命令即可
root@controller01:~# helm uninstall hami -n kube-system

三、简单使用

简单用法讲解

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
root@controller01:~# vi gpu-test3.yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-test3-01
spec:
restartPolicy: OnFailure
nodeSelector:
gpu: "on"
containers:
- name: gpu-test
imagePullPolicy: IfNotPresent
#此镜像中封装的是一个基于pytorch2.2.2的图像识别模型训练程序
image: monkjiang/ubuntu2004:pytorch2.2.2-classification-example
command:
- python3
- /opt/classification/train.py
resources:
limits:
nvidia.com/vgpu: 2 # 请求2个vGPUs
#nvidia.com/gpumem: 3000 # 每个vGPU申请的显存大小(只能是整数值,单位是MB)
nvidia.com/gpumem-percentage: 50 # 每个vGPU申请的显存是所在物理GPU的50%(不能跟nvidia.com/gpumem一起用)
nvidia.com/gpucores: 30 # 每个vGPU的利用率上限为物理GPU的30%
#nvidia.com/priority: 0 # 只有两种优先级:0表示高优先级,1表示低优先级,默认是1
#除非与其他高优先级task共享同一个GPU节点,否则高优先级task的resourceCores没有软上限(The utilization of high priority task won't be limited to resourceCores unless sharing GPU node with other high priority tasks.)
#如果没有其他task共享同一个物理GPU,低优先级task的resourceCores没有软上限(The utilization of low priority task won't be limited to resourceCores if no other tasks sharing its GPU.)

1
2
3
4
5
6
7
8
9
#可以在上述yaml文件的 metadata 中添加annotations,实现对异构资源的自定义使用   annotations:
#metadata:
# annotations:
# nvidia.com/nouse-gputype: "A40" #指定GPU型号黑名单
# nvidia.com/use-gputype: "A40" #指定GPU型号白名单
# nvidia.com/nouse-gpuuuid: "GPU-cfca7d85-be74-7c22-4385-6fd15d698cb4" #指定GPU uuid使用黑名单
# nvidia.com/use-gpuuuid: "GPU-2e240d3e-69be-8cec-3ffc-d9da5cc7b8c3" #指定GPU uuid使用白名单
# hami.io/node-scheduler-policy: "spread" #预设值为"binpack", 表示GPU节点调度策略,"binpack"表示尽量将任务分配到同一个GPU节点上,"spread"表示尽量将任务分配到不同GPU节点上
# hami.io/gpu-scheduler-policy: "binpack" #预设值为"spread",表示GPU调度策略,"binpack"表示尽量将任务分配到同一个GPU上,"spread"表示尽量将任务分配到不同GPU上
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
#应用上述文件
root@controller01:~# kubectl apply -f gpu-test3.yaml
pod/gpu-test3-01 created

root@controller01:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
gpu-test3-01 1/1 Running 0 11s 10.233.81.115 controller01 <none> <none>

#可以看到pod/gpu-test3-01的宿主机是controller01,上面有一个GPU进程
root@controller01:~# nvidia-smi
Wed Oct 23 15:35:58 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA XXX Off | 00000000:41:00.0 Off | 0 |
| 0% 45C P0 111W / 300W | 682MiB / 46068MiB | 16% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA XXX Off | 00000000:C1:00.0 Off | 0 |
| 0% 35C P8 29W / 300W | 3MiB / 46068MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| 0 N/A N/A 28054 C python3 674MiB |
+-----------------------------------------------------------------------------------------+
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
#进入pod的容器内部查看,可以看到有两个vGPU,它们的显存都只有23034MiB。虽然申请了两个vGPU,但其实只有0号vGPU被使用了,它的GPU使用率是15%,临时功率是113W
root@controller01:/opt/installPkgs/k8s-vgpu-basedon-HAMi# kubectl exec -it gpu-test3-01 -- bash
root@gpu-test3-01:/opt/classification# nvidia-smi
[HAMI-core Msg(281:140275770877760:libvgpu.c:836)]: Initializing.....
Wed Oct 23 07:36:22 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA XXX Off | 00000000:41:00.0 Off | 0 |
| 0% 49C P0 113W / 300W | 612MiB / 23034MiB | 15% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA XXX Off | 00000000:C1:00.0 Off | 0 |
| 0% 35C P8 29W / 300W | 0MiB / 23034MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
+-----------------------------------------------------------------------------------------+
[HAMI-core Msg(281:140275770877760:multiprocess_memory_limit.c:468)]: Calling exit handler 281
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
41
42
43
44
#查看pod日志
root@controller01:~# kubectl logs gpu-test3-01 | more
[HAMI-core Msg(1:140168594040448:libvgpu.c:836)]: Initializing.....
2024-10-23 07:35:46.333 | INFO | __main__:<module>:97 - Namespace(batch_size=32, epochs=10, lr=0.01, save_dir='./output', train_data_dir='./data/cifar10/train', val_data
_dir='./data/cifar10/val', weights=None)
[HAMI-core Warn(1:140168594040448:utils.c:183)]: get default cuda from (null)
[HAMI-core Msg(1:140168594040448:libvgpu.c:855)]: Initialized
2024-10-23 07:35:46.643 | INFO | __main__:main:21 - Using device: cuda
2024-10-23 07:35:47.103 | INFO | __main__:main:50 - Epoch: 1/10
2024-10-23 07:35:47.104 | INFO | __main__:main:52 - Training
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47283240960 total=47608692736 limit=24152899584 usage=310526720
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47216132096 total=47608692736 limit=24152899584 usage=331498240
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47195160576 total=47608692736 limit=24152899584 usage=352469760
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47060942848 total=47608692736 limit=24152899584 usage=486687488
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=46924627968 total=47608692736 limit=24152899584 usage=623002368
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47056748544 total=47608692736 limit=24152899584 usage=488784640
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47054651392 total=47608692736 limit=24152899584 usage=490881792
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=46916239360 total=47608692736 limit=24152899584 usage=629293824
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47031582720 total=47608692736 limit=24152899584 usage=513950464
[HAMI-core Msg(1:140168594040448:memory.c:511)]: orig free=47029485568 total=47608692736 limit=24152899584 usage=516047616
2024-10-23 07:35:47.801 | INFO | __main__:main:60 - Step: 1 Loss: 2.342037
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46995931136 total=47608692736 limit=24152899584 usage=543723008
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46998028288 total=47608692736 limit=24152899584 usage=541625856
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46995931136 total=47608692736 limit=24152899584 usage=541625856
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46863810560 total=47608692736 limit=24152899584 usage=673746432
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46995931136 total=47608692736 limit=24152899584 usage=541625856
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46998028288 total=47608692736 limit=24152899584 usage=539528704
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46998028288 total=47608692736 limit=24152899584 usage=539528704
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47012708352 total=47608692736 limit=24152899584 usage=524848640
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47012708352 total=47608692736 limit=24152899584 usage=524848640
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=46880587776 total=47608692736 limit=24152899584 usage=656969216
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47014805504 total=47608692736 limit=24152899584 usage=522751488
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47033679872 total=47608692736 limit=24152899584 usage=501779968
[HAMI-core Msg(1:140164068443904:memory.c:511)]: orig free=47033679872 total=47608692736 limit=24152899584 usage=501779968
2024-10-23 07:35:48.273 | INFO | __main__:main:60 - Step: 2 Loss: 2.255279
2024-10-23 07:35:48.308 | INFO | __main__:main:60 - Step: 3 Loss: 2.317460
2024-10-23 07:35:48.329 | INFO | __main__:main:60 - Step: 4 Loss: 2.280499
...

更多官方示例

​ 参考:https://github.com/Project-HAMi/HAMi/tree/v2.4.0/examples

四、问题或faq

参考官网issues:https://github.com/Project-HAMi/HAMi/issues

五、资源监控

可以参考笔者归纳总结的这篇文章: https://jiangsanyin.github.io/2024/10/07/GPU%E8%99%9A%E6%8B%9F%E5%8C%96%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AEHAMi%E4%B8%AD%E4%BD%BF%E7%94%A8prometheus%E4%B8%8Egrafana%E5%81%9A%E7%9B%91%E6%8E%A7/ , 建议直接看官方文档。此监控相关文档由笔者自己编写,已经提交给HAMi官方项目仓库、当前被HAMi官方项目仓库接受并合入主线分支中。

六、更新与参考

后续如有修正或完善,将继续更新。可能后续会发表HAMi系列学习记录。

七、参考资源

  • HAMi代码仓库:https://github.com/Project-HAMi/HAMi
  • 项目官网:https://project-hami.io/
  • 官方公众号:“HAMi Project”
  • KubeSphere B站官方账号发表的一个以HAMi为主题的推讲视频,在2024-12-31 下午发布

异构算力虚拟化中间件HAMi介绍与简单使用
https://jiangsanyin.github.io/2024/10/22/异构算力虚拟化中间件HAMi介绍与简单使用/
作者
sanyinjiang
发布于
2024年10月22日
许可协议