开源 vGPU 方案:HAMi,实现细粒度 GPU 切分

news2025/6/11 8:21:31

vgpu-hami.png

本文主要分享一个开源的 GPU 虚拟化方案:HAMi,包括如何安装、配置以及使用。
相比于上一篇分享的 TimeSlicing 方案,HAMi 除了 GPU 共享之外还可以实现 GPU core、memory 得限制,保证共享同一 GPU 的各个 Pod 都能拿到足够的资源。

1.为什么需要 GPU 共享、切分等方案?

开始之前我们先思考一个问题,为什么需要 GPU 共享、切分等方案?

或者说是另外一个问题:明明直接在裸机环境使用,都可以多个进程共享 GPU,怎么到 k8s 环境就不行了

推荐阅读前面几篇文章:这两篇分享了如何在各个环境中使用 GPU,在 k8s 环境则推荐使用 NVIDIA 提供的 gpu-operator 快速部署环境。

GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU

GPU 环境搭建指南:使用 GPU Operator 加速 Kubernetes GPU 环境搭建

这两篇则分析了 device-plugin 原理以及在 K8s 中创建一个申请 GPU 的 Pod 后的一些列动作,最终该 Pod 是如何使用到 GPU 的。

Kubernetes教程(二一)—自定义资源支持:K8s Device Plugin 从原理到实现

Kubernetes教程(二二)—在 K8S 中创建 Pod 是如何使用到 GPU 的:device plugin&nvidia-container-toolkit 源码分析

看完之后,大家应该就大致明白了。

资源感知

首先在 k8s 中资源是和节点绑定的,对于 GPU 资源,我们使用 NVIDIA 提供的 device-plugin 进行感知,并上报到 kube-apiserver,这样我们就能在 Node 对象上看到对应的资源了。

就像这样:

root@liqivm:~# k describe node gpu01|grep Capacity -A 7
Capacity:
  cpu:                128
  ephemeral-storage:  879000896Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             1056457696Ki
  nvidia.com/gpu:     8
  pods:               110

可以看到,该节点除了基础的 cpu、memory 之外,还有一个nvidia.com/gpu: 8 信息,表示该节点上有 8 个 GPU。

资源申请

然后我们就可以在创建 Pod 时申请对应的资源了,比如申请一个 GPU:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
  - name: gpu-container
    image: nvidia/cuda:11.0-base   # 一个支持 GPU 的镜像
    resources:
      limits:
        nvidia.com/gpu: 1          # 申请 1 个 GPU
    command: ["nvidia-smi"]         # 示例命令,显示 GPU 的信息
  restartPolicy: OnFailure

apply 该 yaml 之后,kube-scheduler 在调度该 Pod 时就会将其调度到一个拥有足够 GPU 资源的 Node 上。

同时该 Pod 申请的部分资源也会标记为已使用,不会在分配给其他 Pod。

到这里,问题的答案就已经很明显的。

  • 1)device-plugin 感知到节点上的物理 GPU 数量,上报到 kube-apiserver
  • 2)kube-scheduler 调度 Pod 时会根据 pod 中的 Request 消耗对应资源

即:Node 上的 GPU 资源被 Pod 申请之后,在 k8s 中就被标记为已消耗了,后续创建的 Pod 会因为资源不够导致无法调度

实际上:可能 GPU 性能比较好,可以支持多个 Pod 共同使用,但是因为 k8s 中的调度限制导致多个 Pod 无法正常共享。

因此,我们才需要 GPU 共享、切分等方案。

上一篇文章一文搞懂 GPU 共享方案: NVIDIA Time Slicing 中给大家分享了一个 GPU 共享方案。

可以实现多个 Pod 共享同一个 GPU,但是存在一个问题:Pod 之间并未做任何隔离,每个 Pod 能用到多少 GPU core、memory 都靠竞争,可能会导致部分 Pod 占用大部分资源导致其他 Pod 无法正常使用的情况。

今天给大家分享一个开源的 vGPU 方案 HAMi。

ps:NVIDIA 也有自己的 vGPU 方案,但是需要 license

2. 什么是 HAMi?

HAMi 全称是:Heterogeneous AI Computing Virtualization Middleware,HAMi 给自己的定位或者希望是做一个异构算力虚拟化平台。

原 第四范式 k8s-vgpu-scheduler, 这次改名 HAMi 同时也将核心的 vCUDA 库 libvgpu.so 也开源了。

但是现在比较完善的是对 NVIDIA GPU 的 vGPU 方案,因此我们可以简单认为他就是一个 vGPU 方案。

整体架构如下:

hami-arch.png

可以看到组件还是比较多的,设计到 Webhook、Scheduler、Device Plugin、HAMi-Core 等等。

这篇文章只讲使用,因此架构、原理就一笔带过,后续也会有相关文章,欢迎关注~。

Feature

使用 HAMi 最大的一个功能点就是可以实现 GPU 的细粒度的隔离,可以对 core 和 memory 使用 1% 级别的隔离。

具体如下:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
    - name: ubuntu-container
      image: ubuntu:18.04
      command: ["bash", "-c", "sleep 86400"]
      resources:
        limits:
          nvidia.com/gpu: 1 # 请求1个vGPUs
          nvidia.com/gpumem: 3000 # 每个vGPU申请3000m显存 (可选,整数类型)
          nvidia.com/gpucores: 30 # 每个vGPU的算力为30%实际显卡的算力 (可选,整数类型)
  • nvidia.com/gpu:请求一个 GPU
  • nvidia.com/gpumem:只申请使用 3000M GPU Memory
  • nvidia.com/gpucores:申请使用 30% 的 GPU core,也就是该 Pod 只能使用到 30% 的算力

相比于上文分享了 TimeSlicing 方案,HAMi 则是实现了 GPU core 和 memory 的隔离。

在开源方案里面已经算是比较优秀的了。

Design

HAMi 实现GPU core 和 memory 隔离、限制是使用的 vCUDA 方案,具体设计如下:

hami-core-design.png

大部分使用 GPU 的应用都是用的 CUDA,HAMi 也是用的 vCUDA 方案,对 NVIDIA 原生的 CUDA 驱动进行重写,然后挂载到 Pod 中进行替换,然后在自己的实现的 CUDA 驱动中对 API 进行拦截,使用资源隔离以及限制的效果。

例如:原生 CUDA 驱动进行内存分配,只有在 GPU 内存真的用完的时候才会提示 CUDA OOM,但是对于 HAMi CUDA 驱动来说,检测到 Pod 中使用的内存超过了 Resource 中的申请量就直接返回 OOM,从而实现资源的一个限制。

然后在执行 nvidia-smi 命令查看 GPU 信息时,也只返回 Pod Resource 中申请的资源,这样在查看时也进行隔离。

ps:需要对 CUDA 和 NVML 的部分 API 拦截。

3. HAMi 部署

HAMi 提供了 Helm Chart 安装也是比较简单的。

部署 GPU Operator

需要注意的是 HAMi 会依赖 NVIDIA 的那一套,因此推荐先部署 GPU-Operator。

参考这篇文章 --> GPU 环境搭建指南:使用 GPU Operator 加速 Kubernetes GPU 环境搭建

部署好 GPU Operator 之后在部署 HAMi。

部署 HAMi

首先使用 helm 添加我们的 repo

helm repo add hami-charts https://project-hami.github.io/HAMi/

随后,使用下列指令获取集群服务端版本

这里使用的是 v1.27.4 版本

kubectl version

在安装过程中须根据集群服务端版本(上一条指令的结果)指定调度器镜像版本,例如集群服务端版本为 v1.27.4,则可以使用如下指令进行安装

helm install hami hami-charts/hami --set scheduler.kubeScheduler.imageTag=v1.27.4 -n kube-system

通过kubectl get pods指令看到 vgpu-device-pluginvgpu-scheduler 两个pod 状态为Running 即为安装成功

root@iZj6c5dnq07p1ic04ei9vwZ:~# kubectl get pods -n kube-system|grep hami
hami-device-plugin-b6mvj                          2/2     Running   0          42s
hami-scheduler-7f5c5ff968-26kjc                   2/2     Running   0          42s

自定义配置

官方文档:HAMi-config-cn.md

你可以在安装过程中,通过-set来修改以下的客制化参数,例如:

helm install vgpu vgpu-charts/vgpu --set devicePlugin.deviceMemoryScaling=5 ...
  • devicePlugin.deviceSplitCount: 整数类型,预设值是 10。GPU 的分割数,每一张GPU 都不能分配超过其配置数目的任务。若其配置为N的话,每个 GPU 上最多可以同时存在 N 个任务。
  • devicePlugin.deviceMemoryScaling: 浮点数类型,预设值是1。NVIDIA 装置显存使用比例,可以大于1(启用虚拟显存,实验功能)。对于有 M显存大小的 NVIDIA GPU,如果我们配置devicePlugin.deviceMemoryScaling参数为 S ,在部署了我们装置插件的Kubenetes 集群中,这张 GPU 分出的 vGPU 将总共包含 S * M 显存。
  • devicePlugin.migStrategy: 字符串类型,目前支持"none“与“mixed“两种工作方式,前者忽略 MIG 设备,后者使用专门的资源名称指定 MIG 设备,使用详情请参考mix_example.yaml,默认为"none"
  • devicePlugin.disablecorelimit: 字符串类型,"true"为关闭算力限制,“false"为启动算力限制,默认为"false”
  • scheduler.defaultMem: 整数类型,预设值为 5000,表示不配置显存时使用的默认显存大小,单位为 MB
  • scheduler.defaultCores: 整数类型(0-100),默认为0,表示默认为每个任务预留的百分比算力。若设置为 0,则代表任务可能会被分配到任一满足显存需求的 GPU 中,若设置为100,代表该任务独享整张显卡
  • scheduler.defaultGPUNum: 整数类型,默认为1,如果配置为0,则配置不会生效。当用户在 pod 资源中没有设置 nvidia.com/gpu 这个 key 时,webhook 会检查 nvidia.com/gpumem、resource-mem-percentage、nvidia.com/gpucores 这三个 key 中的任何一个 key 有值,webhook 都会添加 nvidia.com/gpu 键和此默认值到 resources limit中。
  • resourceName: 字符串类型, 申请vgpu个数的资源名, 默认: “nvidia.com/gpu”
  • resourceMem: 字符串类型, 申请vgpu显存大小资源名, 默认: “nvidia.com/gpumem”
  • resourceMemPercentage: 字符串类型,申请vgpu显存比例资源名,默认: “nvidia.com/gpumem-percentage”
  • resourceCores: 字符串类型, 申请vgpu算力资源名, 默认: “nvidia.com/cores”
  • resourcePriority: 字符串类型,表示申请任务的任务优先级,默认: “nvidia.com/priority”

除此之外,容器中也有对应配置

  • GPU_CORE_UTILIZATION_POLICY: 字符串类型,“default”, “force”, “disable” 代表容器算力限制策略, "default"为默认,"force"为强制限制算力,一般用于测试算力限制的功能,"disable"为忽略算力限制
  • ACTIVE_OOM_KILLER: 字符串类型,“true”, “false” 代表容器是否会因为超用显存而被终止执行,"true"为会,"false"为不会

我们只是简单 Demo 就不做任何配置直接部署即可。

4. 验证

查看 Node GPU 资源

类似于上一篇分享的 TimeSlicing 方案,在安装之后,Node 上可见的 GPU 资源也是增加了。

环境中只有一个物理 GPU,但是 HAMi 默认会扩容 10 倍,理论上现在 Node 上能查看到 1*10 = 10 个 GPU。

默认参数就是切分为 10 个,可以设置。

$ kubectl get node xxx -oyaml|grep capacity -A 7
  capacity:
    cpu: "4"
    ephemeral-storage: 206043828Ki
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 15349120Ki
    nvidia.com/gpu: "10"
    pods: "110"

验证显存和算力限制

使用以下 yaml 来创建 Pod,注意 resources.limit 除了原有的 nvidia.com/gpu 之外还新增了 nvidia.com/gpumem 和 nvidia.com/gpucores,用来指定显存大小和算力大小。

  • nvidia.com/gpu:请求的 vgpu 数量,例如 1
  • nvidia.com/gpumem :请求的显存数量,例如 3000M
  • nvidia.com/gpumem-percentage:显存百分百,例如 50 则是请求 50%显存
  • nvidia.com/priority: 优先级,0 为高,1 为底,默认为 1。
    • 对于高优先级任务,如果它们与其他高优先级任务共享 GPU 节点,则其资源利用率不会受到 resourceCores 的限制。换句话说,如果只有高优先级任务占用 GPU 节点,那么它们可以利用节点上所有可用的资源。
    • 对于低优先级任务,如果它们是唯一占用 GPU 的任务,则其资源利用率也不会受到 resourceCores 的限制。这意味着如果没有其他任务与低优先级任务共享 GPU,那么它们可以利用节点上所有可用的资源。

完整 gpu-test.yaml 内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
    - name: ubuntu-container
      image: ubuntu:18.04
      command: ["bash", "-c", "sleep 86400"]
      resources:
        limits:
          nvidia.com/gpu: 1 # 请求1个vGPUs
          nvidia.com/gpumem: 3000 # 每个vGPU申请3000m显存 (可选,整数类型)
          nvidia.com/gpucores: 30 # 每个vGPU的算力为30%实际显卡的算力 (可选,整数类型)

Pod 能够正常启动

root@iZj6c5dnq07p1ic04ei9vwZ:~# kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
gpu-pod   1/1     Running   0          48s

进入 Pod执行 nvidia-smi 命令,查看 GPU 信息,可以看到展示的限制就是 Resource 中申请的 3000M。

root@iZj6c5dnq07p1ic04ei9vwZ:~# kubectl exec -it gpu-pod -- bash
root@gpu-pod:/# nvidia-smi
[HAMI-core Msg(16:139711087368000:libvgpu.c:836)]: Initializing.....
Mon Apr 29 06:22:16 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      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  Tesla T4                       On  |   00000000:00:07.0 Off |                    0 |
| N/A   33C    P8             15W /   70W |       0MiB /   3000MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+
[HAMI-core Msg(16:139711087368000:multiprocess_memory_limit.c:434)]: Calling exit handler 16

根据最后的日志就是 HAMi 的 CUDA 驱动打印的。

[HAMI-core Msg(16:139711087368000:multiprocess_memory_limit.c:434)]: Calling exit handler 16

**【Kubernetes 系列】**持续更新中,搜索公众号【探索云原生】订阅,文章。


5. 小结

本文主要分享了开源 vGPU 方案 HAMi,并通过简单 Demo 进行了验证。

为什么需要 GPU 共享、切分?

在 k8s 中使用默认 device plugin 时,GPU 资源和物理 GPU 是一一对应的,导致一个物理 GPU 被一个 Pod 申请后,其他 Pod 就无法使用了。

为了提高资源利用率,因此我们需要 GPU 共享、切分等方案。

HAMi 大致实现原理

通过替换容器中的 libvgpu.so 库,实现 CUDA API 拦截,最终实现对 GPU core 和 memory 的隔离和限制。

更加详细的原理分析,可以期待后续文章~

最后在贴一下相关文章,推荐阅读:

  • GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU

  • GPU 环境搭建指南:使用 GPU Operator 加速 Kubernetes GPU 环境搭建

  • Kubernetes教程(二一)—自定义资源支持:K8s Device Plugin 从原理到实现

  • Kubernetes教程(二二)—在 K8S 中创建 Pod 是如何使用到 GPU 的:device plugin&nvidia-container-toolkit 源码分析

  • 一文搞懂 GPU 共享方案: NVIDIA Time Slicing

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406280.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

盲盒一番赏小程序:引领盲盒新潮流

在盲盒市场日益火爆的今天,如何才能在众多盲盒产品中脱颖而出?盲盒一番赏小程序给出了答案,它以创新的玩法和优质的服务,引领着盲盒新潮流。 一番赏小程序的最大特色在于其独特的赏品分级制度。赏品分为多个等级,从普…

边缘计算设备全解析:边缘盒子在各大行业的落地应用场景

随着工业物联网、AI、5G的发展,数据量呈爆炸式增长。但你有没有想过,我们生成的数据,真的都要发回云端处理吗?其实不一定。特别是在一些对响应时间、网络带宽、数据隐私要求高的行业里,边缘计算开始“火”了起来&#…

Linux实现线程同步的方式有哪些?

什么是线程同步? 想象一下超市收银台:如果所有顾客(线程)同时挤向同一个收银台(共享资源),场面会一片混乱。线程同步就是给顾客们发"排队号码牌",确保: 有序访…

python学习day39

图像数据与显存 知识点回顾 1.图像数据的格式:灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…

年度峰会上,抖音依靠人工智能和搜索功能吸引广告主

上周早些时候举行的第五届年度TikTok World产品峰会上,TikTok推出了一系列旨在增强该应用对广告主吸引力的功能。 新产品列表的首位是TikTok Market Scope,这是一个全新的分析平台,为广告主提供整个考虑漏斗的全面视图,使他们能够…

如何使用CodeRider插件在IDEA中生成代码

一、环境搭建与插件安装 1.1 环境准备 名称要求说明操作系统Windows 11JetBrains IDEIntelliJ IDEA 2025.1.1.1 (Community Edition)硬件配置推荐16GB内存50GB磁盘空间 1.2 插件安装流程 步骤1:市场安装 打开IDEA,进入File → Settings → Plugins搜…

电脑定时关机工具推荐

软件介绍 本文介绍一款轻量级的电脑自动关机工具,无需安装,使用简单,可满足定时关机需求。 工具简介 这款关机助手是一款无需安装的小型软件,文件体积仅60KB,下载后可直接运行,无需复杂配置。 使用…

前端异步编程全场景解读

前端异步编程是现代Web开发的核心,它解决了浏览器单线程执行带来的UI阻塞问题。以下从多个维度进行深度解析: 一、异步编程的核心概念 JavaScript的执行环境是单线程的,这意味着在同一时间只能执行一个任务。为了不阻塞主线程,J…

分布式光纤声振传感技术原理与瑞利散射机制解析

分布式光纤传感技术(Distributed Fiber Optic Sensing,简称DFOS)作为近年来迅速发展的新型感知手段,已广泛应用于边界安防、油气管道监测、结构健康诊断、地震探测等领域。其子类技术——分布式光纤声振传感(Distribut…

RocketMQ 客户端负载均衡机制详解及最佳实践

延伸阅读:🔍「RocketMQ 中文社区」 持续更新源码解析/最佳实践,提供 RocketMQ 专家 AI 答疑服务 前言 本文介绍 RocketMQ 负载均衡机制,主要涉及负载均衡发生的时机、客户端负载均衡对消费的影响(消息堆积/消费毛刺等…

【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理

这篇学习笔记是Spring系列笔记的第7篇,该笔记是笔者在学习黑马程序员SSM框架教程课程期间的笔记,供自己和他人参考。 Spring学习笔记目录 笔记1:【SSM】Spring基础: IoC配置学习笔记-CSDN博客 对应黑马课程P1~P20的内容。 笔记2…

C++课设:实现本地留言板系统(支持留言、搜索、标签、加密等)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、项目功能概览与亮点分析1. 核心功能…

【见合八方平面波导外腔激光器专题系列】用于干涉光纤传感的低噪声平面波导外腔激光器2

----翻译自Mazin Alalus等人的文章 摘要 1550 nm DWDM 平面波导外腔激光器具有低相位/频率噪声、窄线宽和低 RIN 等特点。该腔体包括一个半导体增益芯片和一个带布拉格光栅的平面光波电路波导,采用 14 引脚蝶形封装。这种平面波导外腔激光器设计用于在振动和恶劣的…

Xcode 16.2 版本 pod init 报错

Xcode 版本升级到 16.2 后,项目执行 pod init 报错; ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchron…

timestamp时间戳转换工具

作为一名程序员,一款高效的 在线转换工具 (在线时间戳转换 计算器 字节单位转换 json格式化)必不可少!https://jsons.top 排查问题时非常痛的点: 经常在秒级、毫秒级、字符串格式的时间单位来回转换,于是决定手撸一个…

数据库管理与高可用-MySQL故障排查与生产环境优化

目录 #1.1MySQL单案例故障排查 1.1.1MySQL常见的故障排查 1.1.2MySQL主从故障排查 #2.1MySQL优化 2.1.1硬件方面的优化 2.1.2进程方面的优化 #3.1MySQL存储引擎 3.1.1 MyISAM存储引擎 3.1.2 InnoDB存储引擎 1.1MySQL单案例故障排查 1.1.1MySQL常见的故障排查 (1&…

LangChain + LangSmith + DeepSeek 入门实战:构建代码生成助手

本文基于 Jupyter Notebook 实践代码,结合 LangChain、LangSmith 和 DeepSeek 大模型,手把手演示如何构建一个代码生成助手,并实现全流程追踪与优化。 一、环境准备与配置 1. 安装依赖 pip install langchain langchain_openai2. 设置环境变…

【Elasticsearch基础】Elasticsearch批量操作(Bulk API)深度解析与实践指南

目录 1 Bulk API概述 1.1 什么是批量操作 1.2 Bulk API的优势 2 Bulk API的工作原理 2.1 请求处理流程 2.2 底层机制 3 Bulk API的使用方法 3.1 基本请求格式 3.2 操作类型示例 3.3 响应格式 4 Bulk API的最佳实践 4.1 批量大小优化 4.2 错误处理策略 4.3 性能调…

MySQL 数据库深度剖析:事务、SQL 优化、索引与 Buffer Pool

在当今数据驱动的时代,数据库作为数据存储与管理的核心,其性能与可靠性至关重要。MySQL 作为一款广泛使用的开源数据库,在众多应用场景中发挥着关键作用。在这篇博客中,我将围绕 MySQL 数据库的核心知识展开,涵盖事务及…

MAZANOKE结合内网穿透技术实现跨地域图像优化服务的远程访问过程

文章目录 前言1. 关于MAZANOKE2. Docker部署3. 简单使用MAZANOKE4. 安装cpolar内网穿透5. 配置公网地址6. 配置固定公网地址总结 前言 在数字世界高速发展的今天,您是否察觉到那些静默增长的视觉数据正在悄然蚕食存储空间?随着影像记录成为日常习惯&…