开源vGPU方案 HAMi实现细粒度GPU切分——筑梦之路
前言
为什么需要 GPU 共享、切分等方案?
在使用GPU的过程中我们会发现,直接在裸机环境使用,都可以多个进程共享 GPU,怎么到 k8s 环境就不行了?
1. 资源感知
在 k8s 中资源是和节点绑定的,对于 GPU 资源,我们使用 NVIDIA 提供的 device-plugin 进行感知,并上报到 kube-apiserver,这样我们就能在 Node 对象上看到对应的资源。
kubectl describe node gpu01|grep Capacity -A 7
Capacity:cpu: 128ephemeral-storage: 879000896Kihugepages-1Gi: 0hugepages-2Mi: 0memory: 1056457696Kinvidia.com/gpu: 8pods: 110
该节点除了基础的 cpu、memory 之外,还有一个nvidia.com/gpu: 8 信息,表示该节点上有 8 个 GPU 。
2. 资源申请
apiVersion: v1
kind: Pod
metadata:name: gpu-pod
spec:containers:- name: gpu-containerimage: nvidia/cuda:11.0-base # 一个支持 GPU 的镜像resources:limits:nvidia.com/gpu: 1 # 申请 1 个 GPUcommand: ["nvidia-smi"] # 示例命令,显示 GPU 的信息restartPolicy: OnFailure
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 共享、切分等方案。
什么是 HAMi?
https://github.com/Project-HAMi/HAMi
HAMi 全称是:Heterogeneous AI Computing Virtualization Middleware,HAMi 给自己的定位或者希望是做一个异构算力虚拟化平台。原第四范式 k8s-vgpu-scheduler, 这次改名 HAMi 同时也将核心的 vCUDA 库 libvgpu.so 也开源了。但是现在比较完善的是对 NVIDIA GPU 的 vGPU 方案,因此我们可以简单认为他就是一个 vGPU 方案。
整体架构

特性
使用 HAMi 最大的一个功能点就是可以实现 GPU 的细粒度的隔离,可以对 core 和 memory 使用 1% 级别的隔离。
apiVersion: v1
kind: Pod
metadata:name: gpu-pod
spec:containers:- name: ubuntu-containerimage: ubuntu:18.04command: ["bash", "-c", "sleep 86400"]resources:limits:nvidia.com/gpu: 1 # 请求1个vGPUsnvidia.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% 的算力
设计
HAMi 实现 GPU core 和 memory 隔离、限制是使用的 vCUDA 方案

HAMi 使用的是软件层面的 vCUDA 方案,对 NVIDIA 原生的 CUDA 驱动进行重写(libvgpu.so),然后挂载到 Pod 中进行替换,然后在自己的实现的 CUDA 驱动中对 API 进行拦截,实现资源隔离以及限制的效果。
例如:原生 libvgpu.so 在进行内存分配时,只有在 GPU 内存真的用完的时候才会提示 CUDA OOM,但是对于 HAMi 实现的 libvgpu.so 来说,检测到 Pod 中使用的内存超过了 Resource 中的申请量就直接返回 OOM,从而实现资源的一个限制。
然后在执行 nvidia-smi 命令查看 GPU 信息时,也只返回 Pod Resource 中申请的资源,这样在查看时也进行隔离。
HAMi 部署
HAMi 提供了 Helm Chart 安装
1. 部署 GPU Operator
HAMi 会依赖 NVIDIA 的那一套,因此推荐先部署 GPU-Operator
此处留着补充
部署好 GPU Operator 之后再部署 HAMi。
2. 部署 HAMi
# 添加repo仓库helm repo add hami-charts https://project-hami.github.io/HAMi/# 获取k8s版本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-plugin 与 vgpu-scheduler 两个 pod 状态为Running 即为安装成功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
3. 自定义配置
官方文档:
HAMi-config-cn.md: https://github.com/Project-HAMi/HAMi/blob/master/docs/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"为不会
4. 验证
查看 Node GPU 资源
环境中只有一个物理 GPU,但是 HAMi 默认会扩容 10 倍,理论上现在 Node 上能查看到 1*10 = 10 个 GPU。
默认参数就是切分为 10 个,可以设置
kubectl get node xxx -oyaml|grep capacity -A 7capacity:cpu: "4"ephemeral-storage: 206043828Kihugepages-1Gi: "0"hugepages-2Mi: "0"memory: 15349120Kinvidia.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,那么它们可以利用节点上所有可用的资源。
apiVersion: v1
kind: Pod
metadata:name: gpu-pod
spec:containers:- name: ubuntu-containerimage: ubuntu:18.04command: ["bash", "-c", "sleep 86400"]resources:limits:nvidia.com/gpu: 1 # 请求1个vGPUsnvidia.com/gpumem: 3000 # 每个vGPU申请3000m显存 (可选,整数类型)nvidia.com/gpucores: 30 # 每个vGPU的算力为30%实际显卡的算力 (可选,整数类型)
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
HAMi 大致实现原理
通过替换容器中的 libvgpu.so 库,实现 CUDA API 拦截,最终实现对 GPU core 和 memory 的隔离和限制。
参考资料:
第四范式 k8s-vgpu-scheduler: https://github.com/4paradigm/k8s-vgpu-scheduler
本文搜集来自开源 vGPU 方案:HAMi,实现细粒度 GPU 切分
相关文章:
开源vGPU方案 HAMi实现细粒度GPU切分——筑梦之路
前言 为什么需要 GPU 共享、切分等方案? 在使用GPU的过程中我们会发现,直接在裸机环境使用,都可以多个进程共享 GPU,怎么到 k8s 环境就不行了? 1. 资源感知 在 k8s 中资源是和节点绑定的,对于 GPU 资源…...
性能测试工具JMeter
本次使用的博客系统的url: http://8.137.19.140:9090/blog_edit.html 1. JMeter介绍 环境要求:要求java,jdk版本大于8; Apache JMeter 是 Apache 组织基于 Java 开发的压⼒测试⼯具,⽤于对软件做性能测试;…...
Kubernetes ETCD的恢复与备份
在 Kubernetes 中,ETCD 扮演着至关重要的角色: 1. 集群状态存储 2. 服务发现 3. 配置管理 4. 分布式锁和协调 5. 故障恢复 ETCD 存储了 Kubernetes 集群中所有的状态信息,包括节点、Pod、Service、ConfigMap、Secrets 等。ETCD 支持服务发现…...
笔记整理—linux网络部分(2)Linux网络框架
前文说过,在OSI中将网络分为7层,这是理论上将其分为7层,但实际上可以将其分为4层。如TCP协议就是将其分为4层。理论只是提出一种指导意见,但不是行业范本。 驱动层只关系有没有接到包,不关心包经过多少次转发ÿ…...
深度学习500问——Chapter17:模型压缩及移动端部署(5)
文章目录 17.9.5 ShuffleNet- v1 17.9.6 ShuffleNet- v2 17.10 现有移动端开源框架及其特点 17.10.1 NCNN 17.10.2 QNNPACK 17.9.5 ShuffleNet- v1 ShuffleNet 是Face团队提出的,晚于MobileNet两个月在arXiv上公开《ShuffleNet: An Extremely Efficient…...
分布式ID多种生成方式
分布式ID 雪花算法(时间戳41机器编号10自增序列号10) 作用:希望ID按照时间进行有序生成 原理: 即一台带有编号的服务器在毫秒级时间戳内生成带有自增序号的ID,这个ID保证了自增性和唯一性 雪花算法根据结构的生成ID个数的上线时…...
时间序列预测(六)——循环神经网络(RNN)
目录 一、RNN的基本原理 1、正向传播(Forward Pass): 2、计算损失(Loss Calculation) 3、反向传播——反向传播通过时间(Backpropagation Through Time,BPTT) 4、梯度更新&…...
Day2算法
Day2算法 1.算法的基本概念 算法: 对特定问题求解步骤的一种描述,他叔指令的有限序列,其中的每条指令表示一个或多个操作。 算法的特性: 1.有穷性: 一个算法必须总在执行有穷步之后结束,且每一步都可…...
智洋创新嵌入式面试题汇总及参考答案
堆和栈有什么区别 内存分配方式 栈由编译器自动分配和释放,函数执行时,函数内局部变量等会在栈上分配空间,函数执行结束后自动回收。例如在一个简单的函数int add(int a, int b)中,参数a和b以及函数内部的一些临时变量都会在栈上分配空间,函数调用结束后这些空间就会被释放…...
无线网卡知识的学习-- wireless基础知识(nl80211)
1. 基本概念 mac80211 :这是最底层的模块,与hardware offloading 关联最多。 mac80211 的工作是给出硬件的所有功能与硬件进行交互。(Kernel态) cfg80211:是设备和用户之间的桥梁,cfg80211的工作则是观察跟踪wlan设备的实际状态. (Kernel态) nl80211: 介于用户空间与内核…...
除了 Python,还有哪些语言适合做爬虫?
以下几种语言也适合做爬虫: 一、Java* 优势: 强大的性能和稳定性:Java 运行在 Java 虚拟机(JVM)上,具有良好的跨平台性和出色的内存管理机制,能够处理大规模的并发请求和数据抓取任务&#x…...
JS | JS中类的 prototype 属性和__proto__属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。 构造函数的子类有prototype属性。 …...
15分钟学Go 第3天:编写第一个Go程序
第3天:编写第一个Go程序 1. 引言 在学习Go语言的过程中,第一个程序通常是“Hello, World!”。这个经典的程序不仅教会你如何编写代码,还引导你理解Go语言的基本语法和结构。本节将详细介绍如何编写、运行并理解第一个Go程序,通过…...
简单的常见 http 响应状态码
简单的常见 http 响应状态码 HTTP状态码(HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码。它由 RFC 2616 规范定义,所有状态码的第一个数字代表了响应的五种状态之一。 1. 大体分类 状态码类别解释1xx信息性响…...
2024年【安全员-C证】复审考试及安全员-C证模拟考试题
安全员-C证考试是针对生产经营单位的安全生产管理人员进行的职业资格认证考试。考试内容涵盖安全生产法律法规、安全管理知识、安全技术措施等多个方面。通过考试,可以检验考生对安全生产知识的掌握程度,提高安全管理水平,确保生产安全。 二…...
RT-Thread之STM32使用定时器实现输入捕获
前言 基于RT-Thread的STM32开发,配置使用定时器实现输入捕获。 比如配置特定通道捕获上升沿,该通道对应的引脚有上升沿信号输入,则触发捕获中断。 一、新建工程 二、工程配置 1、打开CubeMX 进行工程配置 2、时钟使用外部高速晶振 3、配置…...
数字图像处理:图像分割应用
数字图像处理:图像分割应用 图像分割是图像处理中的一个关键步骤,其目的是将图像分成具有不同特征的区域,以便进一步的分析和处理。 1.1 阈值分割法 阈值分割法(Thresholding)是一种基于图像灰度级或颜色的分割方法&…...
Java面试宝典-并发编程学习02
目录 21、并行与并发有什么区别? 22、多线程中的上下文切换指的是什么? 23、Java 中用到的线程调度算法是什么? 24、Java中线程调度器和时间分片指的是什么? 25、什么是原子操作?Java中有哪些原子类? 26、w…...
【每日一题】洛谷 - 快速排序模板
今天的每日一题来自洛谷,题目要求对给定的 N N N 个正整数进行从小到大的排序,并输出结果。我们将使用经典的**快速排序算法(QuickSort)**来解决这一问题。下面我将从问题分析、代码实现、及快速排序的核心思想进行详细说明。 题…...
Django模型优化
1、创建一个Django项目 可参考之前的带你快速体验Django web应用 我使用的是mysql数据库。按照上述教程完成准备工作。 2、创建一个app并完成注册 demo主要来完成创建用户、修改用户、查询用户、删除用户的操作。 python manage.py startapp test0023、app的目录 新建templ…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...
