首页 > 数据库 > 数据库开发技术 > GPU越来越难买,如何提高利用率
点击上方蓝字 👆关注公众号,别让消息溜走
后台回复【加群】,与牛人互动不停
随着人工智能与机器学习技术的快速发展,在 Kubernetes 上运行模型训练、图像处理类程序的需求日益增加,而实现这类需求的基础,就是 Kubernetes 对 GPU 等硬件加速设备的支持与管理。
Kubernetes 支持对节点上的 AMD 和 NVIDIA GPU (图形处理单元)进行管理,目前处于稳定状态。
在 GPU 的支持上,最基本的诉求其实非常简单:我只要在 Pod 的 YAML 里面,声明某容器需要的 GPU 个数,那么 Kubernetes 为我创建的容器里就应该出现对应的 GPU 设备,以及它对应的驱动目录。
以 NVIDIA 的 GPU 设备为例,上面的需求就意味着当用户的容器被创建之后,这个容器里必须出现如下两部分设备和目录:
1. GPU 设备,比如
/dev/nvidia0;2. GPU 驱动目录,比如
/usr/local/nvidia/*。
其中,GPU 设备路径,正是该容器启动时的 Devices 参数;而驱动目录,则是该容器启动时的 Volume 参数。
所以,在 Kubernetes 的 GPU 支持的实现里,kubelet 实际上就是将上述两部分内容,设置在了创建该容器的 CRI (Container Runtime Interface)参数里面。这样,等到该容器启动之后,对应的容器里就会出现 GPU 设备和驱动的路径了。
不过,Kubernetes 在 Pod 的 API 对象里,并没有为 GPU 专门设置一个资源类型字段,而是使用了一种叫作 Extended Resource(ER)的特殊字段来负责传递 GPU 的信息。比如下面这个例子:
apiVersion: v1
kind: Pod
metadata:
name: cuda-vector-add
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vector-add
image: "k8s.gcr.io/cuda-vector-add:v0.1"
resources:
limits:
nvidia.com/gpu: 1
可以看到,在上述 Pod 的 limits 字段里,这个资源的名称是nvidia.com/gpu,它的值是 1。也就是说,这个 Pod 声明了自己要使用一个 NVIDIA 类型的 GPU。
在 Kubernetes 的 GPU 支持方案里,你并不需要真正去做上述关于 Extended Resource 的这些操作。在 Kubernetes 中,对所有硬件加速设备进行管理的功能,都是由一种叫作 Device Plugin 的插件来负责的。这其中,当然也就包括了对该硬件的 Extended Resource 进行汇报的逻辑。
Kubernetes 实现了设备插件(Device Plugins) 以允许 Pod 访问类似 GPU 这类特殊的硬件功能特性。
作为集群管理员,你要在节点上安装来自对应硬件厂商的 GPU 驱动程序,并运行来自 GPU 厂商的对应的设备插件。
• AMD
• NVIDIA
当以上条件满足时,Kubernetes 将暴露 amd.com/gpu 或 nvidia.com/gpu 为 可调度的资源。
你可以通过请求 <vendor>.com/gpu 资源来使用 GPU 设备,就像你为 CPU 和内存所做的那样。不过,使用 GPU 时,在如何指定资源需求这个方面还是有一些限制的:
• GPUs 只能设置在limits部分,这意味着:
• 你可以指定 GPU 的 limits 而不指定其 requests,Kubernetes 将使用限制 值作为默认的请求值;
• 你可以同时指定 limits 和 requests,不过这两个值必须相等。
• 你不可以仅指定 requests 而不指定 limits。
• 容器(以及 Pod)之间是不共享 GPU 的。GPU 也不可以过量分配(Overcommitting)。
• 每个容器可以请求一个或者多个 GPU,但是用小数值来请求部分 GPU 是不允许的。
和 CPU 资源不同的是,硬件加速设备类型有多种,比如说 GPUs、NICs、FPGAs,而且它们的厂商也不止一家,Kubernetes 要想挨个支持是不现实的,所以 Kubernetes 就把这些硬件加速设备统一当做
扩展资源来处理。Kubernetes 在 Pod 的 API 对象里并没有提供像 CPU 那样的资源类型,它使用
扩展资源资源字段来传递 GPU 信息。
apiVersion: v1
kind: Pod
metadata:
name: cuda-vector-add
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vector-add
# https://github.com/kubernetes/kubernetes/blob/v1.7.11/test/images/nvidia-cuda/Dockerfile
image: "k8s.gcr.io/cuda-vector-add:v0.1"
resources:
limits:
nvidia.com/gpu: 1 # requesting 1 GPU
要想使用上面 yaml 文件声明使用 GPU 设备,那么需要先在 Node 节点上安装设备插件Device Plugin。
设备插件与设备厂商绑定,不同厂商提供了不同的 Device Plugin。
官方的 AMD GPU 设备插件 有以下要求:
• Kubernetes 节点必须预先安装 AMD GPU 的 Linux 驱动。
如果你的集群已经启动并且满足上述要求的话,可以这样部署 AMD 设备插件:
kubectl create -f https://raw.githubusercontent.com/RadeonOpenCompute/k8s-device-plugin/r1.10/k8s-ds-amdgpu-dp.yaml
官方的 NVIDIA GPU 设备插件 有以下要求:
• Kubernetes 的节点必须预先安装了 NVIDIA 驱动
• Kubernetes 的节点必须预先安装 nvidia-docker 2.0
• **默认运行时必须设置为 nvidia-container-runtime**,而不是 runc
• NVIDIA 驱动版本 ~= 384.81
• Kubernetes 版本 >= 1.10
具体参考 https://github.com/NVIDIA/k8s-device-plugin
需要在所有 GPU 节点上执行以下步骤。本自述文件假定 NVIDIA 驱动程序nvidia-container-toolkit已预安装。它还假设您已将其配置nvidia-container-runtime为要使用的默认低级运行时。
nvidia-container-toolkit
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sudo tee /etc/apt/sources.list.d/libnvidia-container.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
docker
kubernetes使用运行时docker,编辑通常存在的配置文件/etc/docker/daemon.json以设置nvidia-container-runtime为默认的低级运行时:
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
}
}
然后重新启动docker:
$ sudo systemctl restart docker
containerd
kubernetes使用运行时containerd,编辑通常存在的配置文件/etc/containerd/config.toml以设置 nvidia-container-runtime为默认的低级运行时:
version = 2
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "nvidia"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
privileged_without_host_devices = false
runtime_engine = ""
runtime_root = ""
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime"
然后重新启动containerd:
$ sudo systemctl restart containerd
在集群中的所有 GPU 节点上配置上述选项后,您可以通过部署以下 Daemonset 来启用 GPU 支持:
$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml
注意:这是一个简单的守护进程,旨在演示
nvidia-device-plugin。在生产环境中部署插件时,请使用helm部署。参考https://github.com/NVIDIA/k8s-device-plugin#deployment-via-helm
很多公司在使用时,并没有在 YAML 文件中指定 GPU 的个数,也没有在 Kubernetes 集群中安装 Device Plugin 插件,因为他们的程序以 DaemonSet 的方式运行,且每台机器上只有一块 GPU,这样相当于一个程序独占一个 GPU,至于把 GPU 设备及驱动加载到 Docker 容器内,可以通过在 YAML 文件中指定NVIDIA_DRIVER_CAPABILITIES环境变量来实现:
containers:
- env:
- name: NVIDIA_DRIVER_CAPABILITIES
value: compute,utility,video
具体参考:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/user-guide.html
如果集群内部的不同节点上有不同类型的 NVIDIA GPU,那么你可以使用 节点标签和节点选择器 来将 pod 调度到合适的节点上。
例如:
# 为你的节点加上它们所拥有的加速器类型的标签
kubectl label nodes <node-with-k80> accelerator=nvidia-tesla-k80
kubectl label nodes <node-with-p100> accelerator=nvidia-tesla-p100
Kubernetes 本身是通过插件扩展的机制来管理 GPU 资源的,具体来说这里有两个独立的内部机制。
• 第一个是 Extend Resources,允许用户自定义资源名称。而该资源的度量是整数级别,这样做的目的在于通过一个通用的模式支持不同的异构设备,包括 RDMA、FPGA、AMD GPU 等等,而不仅仅是为 Nvidia GPU 设计的;
• Device Plugin Framework 允许第三方设备提供商以外置的方式对设备进行全生命周期的管理,而 Device Plugin Framework 建立 Kubernetes 和 Device Plugin 模块之间的桥梁。它一方面负责设备信息的上报到 Kubernetes,另一方面负责设备的调度选择。
Extend Resources 属于 Node-level 的 api,完全可以独立于 Device Plugin 使用。而上报 Extend Resources,只需要通过一个 PACTH API 对 Node 对象进行 status 部分更新即可,而这个 PACTH 操作可以通过一个简单的 curl 命令来完成。这样,在 Kubernetes 调度器中就能够记录这个节点的 GPU 类型,它所对应的资源数量是 1。
这个 PATCH 操作,可以简单地使用 curl 命令来发起,如下所示:
# 启动 Kubernetes 的客户端 proxy,这样你就可以直接使用 curl 来跟 Kubernetes 的 API Server 进行交互了
$ kubectl proxy
# 执行 PACTH 操作
$ curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/nvidia.com/gpu", "value": "1"}]' \
http://localhost:8001/api/v1/nodes/<your-node-name>/status
PATCH 操作完成后,你就可以看到 Node 的 Status 变成了如下所示的内容:
apiVersion: v1
kind: Node
...
Status:
Capacity:
cpu: 2
memory: 2049008Ki
nvidia.com/gpu: 1
这样在调度器里,它就能够在缓存里记录下 node-1 上的nvidia.com/gpu类型的资源的数量是 1。
当然如果使用的是 Device Plugin,就不需要做这个 PACTH 操作,只需要遵从 Device Plugin 的编程模型,在设备上报的工作中 Device Plugin 就会完成这个操作。
介绍一下 Device Plugin 的工作机制,整个 Device Plugin 的工作流程可以分成两个部分:
• 一个是启动时刻的资源上报;
• 另一个是用户使用时刻的调度和运行。
Kubernetes 的 Device Plugin 机制,我可以用如下所示的一幅示意图来和你解释清楚。
Device Plugin 的开发非常简单。主要包括最关注与最核心的两个事件方法:
• 其中 ListAndWatch 对应资源的上报,同时还提供健康检查的机制。当设备不健康的时候,可以上报给 Kubernetes 不健康设备的 ID,让 Device Plugin Framework 将这个设备从可调度设备中移除;
• 而 Allocate 会被 Device Plugin 在部署容器时调用,传入的参数核心就是容器会使用的设备 ID,返回的参数是容器启动时,需要的设备、数据卷以及环境变量。
对于每一个硬件设备,都需要它所对应的 Device Plugin 进行管理,这些 Device Plugin 以客户端的身份通过 GRPC 的方式对 kubelet 中的 Device Plugin Manager 进行连接,并且将自己监听的 Unix socket api 的版本号和设备名称比如 GPU,上报给 kubelet。
我们来看一下 Device Plugin 资源上报的整个流程。总的来说,整个过程分为四步,其中前三步都是发生在节点上,第四步是 kubelet 和 api-server 的交互。
• 第一步是 Device Plugin 的注册, 需要 Kubernetes 知道要跟哪个 Device Plugin 进行交互。这是因为一个节点上可能有多个设备,需要 Device Plugin 以客户端的身份向 Kubelet 汇报三件事情。
• 我是谁? 就是 Device Plugin 所管理的设备名称,是 GPU 还是 RDMA;
• 我在哪? 就是插件自身监听的 unix socket 所在的文件位置,让 kubelet 能够调用自己;
• 交互协议,即 API 的版本号。
• 第二步是服务启动,Device Plugin 会启动一个 GRPC 的 server。在此之后 Device Plugin 一直以这个服务器的身份提供服务让 kubelet 来访问,而监听地址和提供 API 的版本就已经在第一步完成了;
• 第三步,当该 GRPC server 启动之后,kubelet 会建立一个到 Device Plugin 的 ListAndWatch 的长连接, 用来发现设备 ID 以及设备的健康状态。 当 Device Plugin 检测到某个设备不健康的时候,就会主动通知 kubelet。而此时如果这个设备处于空闲状态,kubelet 会将其移除可分配的列表。但是当这个设备已经被某个 Pod 所使用的时候,kubelet 就不会做任何事情,如果此时杀掉这个 Pod 是一个很危险的操作。
• 第四步,kubelet 会将这些设备暴露到 Node 节点的状态中,把设备数量发送到 Kubernetes 的 api-server 中。后续调度器可以根据这些信息进行调度。
需要注意的是kubelet 在向 api-server 进行汇报的时候,只会汇报该 GPU 对应的数量。 而 kubelet 自身的 Device Plugin Manager 会对这个 GPU 的 ID 列表进行保存,并用来具体的设备分配。而这个对于 Kubernetes 全局调度器来说,它不掌握这个 GPU 的 ID 列表,它只知道 GPU 的数量。这就意味着在现有的 Device Plugin 工作机制下,Kubernetes 的全局调度器无法进行更复杂的调度。
比如说想做两个 GPU 的亲和性调度,同一个节点两个 GPU 可能需要进行通过 NVLINK 通讯而不是 PCIe 通讯,才能达到更好的数据传输效果。在这种需求下,目前的 Device Plugin 调度机制中是无法实现的。
• 第一步:Pod 想使用一个 GPU 的时候,它只需要像之前的例子一样,在 Pod 的 Resource 下 limits 字段中声明 GPU 资源和对应的数量 (比如nvidia.com/gpu: 1)。Kubernetes 会找到满足数量条件的节点,然后将该节点的 GPU 数量减 1,并且完成 Pod 与 Node 的绑定。
• 第二步:绑定成功后,自然就会被对应节点的 kubelet 拿来创建容器。而当 kubelet 发现这个 Pod 的容器请求的资源是一个 GPU 的时候,kubelet 就会委托自己内部的 Device Plugin Manager 模块,从自己持有的 GPU 的 ID 列表中选择一个可用的 GPU 分配给该容器。此时 kubelet 就会向本机的 Device Plugin 发起一个 Allocate 请求,这个请求所携带的参数,正是即将分配给该容器的设备 ID 列表。
• 第三步:Device Plugin 收到 AllocateRequest 请求之后,它就会根据 kubelet 传过来的设备 ID,去寻找这个设备 ID 对应的设备路径、驱动目录以及环境变量,并且以 AllocateResponse 的形式返还给 kubelet。
• 第四步:AllocateResponse 中所携带的设备路径和驱动目录信息,一旦返回给 kubelet 之后,kubelet 就会根据这些信息执行为容器分配 GPU 的操作,这样 Docker 会根据 kubelet 的指令去创建容器,而这个容器中就会出现 GPU 设备,并且把它所需要的驱动目录给挂载进来,至此 Kubernetes 为 Pod 分配一个 GPU 的流程就结束了。
Device Plugin生命周期总结
1. Device Plugin启动时,以grpc的形式通过var/ib/kubelet/device-plugins/kubelet..sock 向Kubelet注册设备id(比如nvidia.com/gpu)
2. Kubelet将会把设备数量以Node状态上报到APIServer中,后续调度器会根据这些信息进行调度
3. Kubelet同时会建立一个到Device Plugin的listAndWatch长连接,当插件检测到某个设备不健康的时候,就会主动通知Kubelet
4. 当用户的应用请求GPU资源后,Device Plugin根据Kubeleti在Allocate请求分配好的设备id定位到对应的设备路径和驱动文件
5. Kubelet根据Device plugin提供的信息创建对应容器
我们知道 Kubernetes 可以实现对宿主机的 CPU、内存、网络实现精细化的控制,但是到目前为止(2022.05),Kubernetes 尚未实现像管理 CPU 那样来管理 GPU,比如有如下限制:
Nvidia 贡献的调度方案,只支持按较粗粒度的调度,按GPU块数调度。Nvidia GPU Device Plugin
• 对于 GPU 资源只能设置limit,这意味着requests不可以单独使用,要么只设置limit、要么同时设置二者,但二者值必须相等,不可以只设置request而不设置limit。
• 不允许以小数请求 GPU 资源分配。
• pod 及容器之间,不可以共享 GPU,且 GPU 也不可以过量分配
• 没有直观方式可获取集群层面 GPU 信息,比如 Pod / 容器与 GPU 卡绑定关系、已使用 GPU 卡数 等
• 现有 Kubernetes 对 GPU 资源的分配调度是通过 extended resource 实现的,它是基于节点上卡数量的加减调度。用户如果想知道集群中 GPU 卡的分配情况,需要遍历节点,拿到并计算这些信息。并且由于这个资源是标量的,所以并无法拿到 Pod / 容器 与卡的绑定关系。这些问题在整卡模式下不是那么凸显,但在细粒度共享模式下,就尤为严重了。
• 问题在于 GPU 资源的调度工作,实际上都是在 kubelet 上完成的。
• 而作为全局的调度器对这个参与是非常有限的,作为传统的 Kubernetes 调度器来说,它只能处理 GPU 数量。一旦你的设备是异构的,不能简单地使用数目去描述需求的时候,比如我的 Pod 想运行在两个有 nvlink 的 GPU 上,这个 Device Plugin 就完全不能处理。
• 各种 GPU 技术(nvidia docker、qGPU、vCUDA、gpu share、GPU 池化)均需独立部署组件,无法统一调度和管理
以下是共享 GPU 资源以提高利用率的一些示例工作负载:
• 低批量推理服务 ,它只能在 GPU 上处理一个输入样本
• 高性能计算( HPC )应用 ,例如模拟光子传播,在 CPU (读取和处理输入)和 GPU (执行计算)之间平衡计算。由于 CPU 核心性能的瓶颈,一些 HPC 应用程序可能无法在 GPU 部分实现高吞吐量。
• ML 模型探索的交互式开发, 使用 Jupyter notebook
• 基于 Spark 的数据分析应用程序 ,其中一些任务或最小的工作单元同时运行,并受益于更好的 GPU 利用率
• 可视化或脱机渲染应用程序 ,这可能是突发性的
• 希望使用任何可用的 GPU 进行测试的 连续集成/连续交付( CICD )管道
关于GPU切分及高效共享的解决方案,行业有很多的技术实现,大致分成Remote CUDA,CUDA原生实现,GPU硬件实现。
1. R-CUDA的方案,也叫远程CUDA和CUDA劫持,是一种基于驱动或CUDA句柄的拦截和转发机制,英伟达官方并不支持这样的做法,尤其在跨服务器请求转发方面,其性能和效果对于网络和处理延时依赖较大,对于实时性的业务场景效果并不理想,同时随着GPU驱动的升级,对于其支持的周期风险很大。
2. CUDA原生实现,主要指英伟达的vGPU全切分方案,通过定制的GPU驱动,实现GPU的时分切分,基于时间片轮训机制,实现GPU显存最小粒度的切分,该方案是英伟达的原生技术实现,需要相应的License和技术支持服务。还有一个空分。
3. GPU硬件切分技术,只要在A100和A30的GPU上提供,未来的H100也会支持,实现机制是通过GPU内部相应计算单元和显存单元的动态划分,实现硬件的空间切片,彼此的隔离性达到最佳,对于上层系统相对透明,且提供灵活的配置,该技术仅在A100和A30 GPU支持,不需要软件License。
阿里云服务团队贡献的 GPU 共享的调度方案,其目的在于解决用户共享 GPU 调度的需求
gpushare-scheduler-extender
gpushare-device-plugin
优点:
• 能够让更多的服务共享同一个GPU卡上,能够让使用者通过API描述对于一个可共享资源的申请, 并能实现该种资源的调度
缺点:
• 不支持该共享资源的隔离
前提条件:
• 延用Kubernetes Extended Resource定义,但是衡量维度最小单位从1个GPU卡变为GPU显存的MiB
• 用户申请的GPU资源上限不会超过一张卡,也就是申请的资源上限为单卡
实现思路:依赖于Kubernetes的现有工作机制:
• Extended Resource定义
• Scheduler Extender机制
• Device Plugin机制
1. 利用 kubernetes Extended Resource机制,重新定义GPU资源,主要是对显存和GPU数量的定义。
2. 利用Device Plugin机制,在节点上将GPU资源总量上报给kubelet,kubelet进一步上报给Kubernetes API Server。
3. 利用k8s scheduler Extender机制,扩展调度器功能,负责在全局调度器Filter和Bind的时候判断节点上单个GPU卡是否能够提供足够的GPU显存,并且在Bind的时刻将GPU的分配结果通过annotation记录到Pod Spec以供后续Filter检查分配结果。
4. 节点运行:当Pod和节点绑定的事件被Kubelet接收到后,Kubelet就会在节点上创建真正的Pod实体,在这个过程中, Kubelet会调用GPU Share Device Plugin的Allocate方法, Allocate方法的参数是Pod申请的gpu-mem。而在Allocate方法中,会根据GPU Share Scheduler Extender的调度决策运行对应的Pod。
后期规划
• 集成 Nvidia MPS 作为隔离选项
• 由 kubeadm 部署的 Kubernetes 集群的自动化部署
• 调度器扩展器高可用性
• 适用于 GPU、RDMA 和其他设备的通用解决方案
参考:https://github.com/volcano-sh/volcano/blob/5302995a1d000df718fee5f3044df64bcb773d8d/docs/user-guide/how_to_use_gpu_sharing.md
Volcano提供调度层面的GPU资源共享,可以使多个Pod运行在同一块GPU卡上,从而提升集群GPU资源整体利用率。
• GPU虚拟化:推理场景以及GPU开发的场景,GPU使用率普遍偏低,Volcano已实现多容器共享使用GPU,未来将进一步增强算力、显存的隔离能力,保障在提升利用率的同时,降低业务间的干扰;
• 支持GPU节点多维度资源比例分片,主要用于解决GPU节点因CPU等其他维度资源过度使用引起GPU作业饥饿但GPU资源空闲浪费的问题。允许用户设置一个主导型资源(通常设置为GPU),并可为它配置配套资源维度的预留比例(如GPU:CPU:Memory=1:4:32)。调度器在工作时将会时刻保持GPU节点上GPU、CPU、Memory的空闲资源比例不低于该设定值,因此任何时刻符合该比例需求的GPU作业均可调度到该节点,而不会引起GPU浪费。这一方法较业界其他解决方案,如GPU节点分配独立调度器、CPU型作业强制不允许调度到GPU节点等,更有利于提高节点资源利用率,使用也更加灵活。
优点:
• 调度层面是可以的多个容器共享一个gpu卡
• 目前允许填的是memory的显存大小。可以填一个gpu显存的一部分,也可以填多个gpu卡的显存量。
缺点:
• 隔离方面,本来打算做显存和算力隔离的,不过说hack cuda api或者driver会涉及法务风险,就没有继续了
• 直接指定gpu num,还不行。这个device plugin当时是做gpu共享开发的。
Volcano GPU共享设计
1. 创建一个带有volcano.sh/gpu-memory资源请求的 pod,
2. Volcano并为 pod 预测和分配 gpu 资源。添加以下注释
annotations:
volcano.sh/gpu-index: "0"
volcano.sh/predicate-time: "1593764466550835304"
1. kubelet 监视绑定到自身的 pod,并在运行容器之前调用 allocate API 设置 env。
env:
NVIDIA_VISIBLE_DEVICES: "0" # GPU card index
VOLCANO_GPU_ALLOCATED: "1024" # GPU allocated
VOLCANO_GPU_TOTAL: "11178" # GPU memory of the card
Volcano通过Kubernetes自定义扩展资源机制定义了GPU相关的volcano.sh/gpu-memory和volcano.sh/gpu-number两种资源,其中volcano.sh/gpu-memory用来描述节点GPU显存信息;volcano.sh/gpu-number用来描述节点GPU卡的数量。
Volcano通过Kubernetes提供的Device plugin实现如下功能:
• 收集集群中节点上gpu-number与显存gpu-memory
• 监控GPU健康状态
• 在集群中为申请GPU的workload挂载GPU资源
腾讯云 Tencent Kubernetes Engine qGPU 服务(以下简称 TKE qGPU)是腾讯云推出的 GPU 容器虚拟化产品,支持多个容器共享 GPU 卡并支持容器间算力和显存精细隔离,同时提供业界唯一的在离线混部能力,在精细切分 GPU 资源的基础上,在最大程度保证业务稳定的前提下,提高 GPU 使用率,帮助客户大幅度节约 GPU 资源成本。
qGPU 依托 TKE 对外开源的 Elastic GPU 框架,可实现对 GPU 算力与显存的细粒度调度,并支持多容器共享 GPU 与多容器跨 GPU 资源分配。同时依赖底层强大的 qGPU 隔离技术,可做到 GPU 显存和算力的强隔离,在通过共享使用 GPU 的同时,尽量保证业务性能与资源不受干扰。
每个 qGPU 最小分配1G显存,精度单位是1G。算力最小分配5(代表一张卡的5%),最大100(代表一张卡),精度单位是5(即5、10、15、20 ... 100)。
开启了 qGPU 能力的节点可按照 tke.cloud.tencent.com/qgpu-core: 100 | 200 | ...(N * 100,N 是整卡个数)的方式分配整卡。建议通过 TKE 的节点池能力来区分 NVIDIA 分配方式或转换到 qGPU 使用方式。
一个 GPU 上最多可创建16个 qGPU 设备。建议按照容器申请的显存大小确定单个 GPU 卡可共享部署的 qGPU 个数。
1. 常规GPU使用,按块调度
2. 使用Nvidia 提供的虚拟化技术
3. 自研实现GPU半虚拟化:基于驱动函数实现,改变了函数显存申请、释放和线程的发起函数。
1. 从社区、stackoverflow以及上述各公司的实践来看,目前GPU共享主要共享的是GPU的显存,资源隔离性差,存在资源抢占等情况,是否需要开展GPU共享相关的开发工作需要视公司机器学习对GPU的使用场景来决定;
2. GPU共享存在一定的限制:用户申请的资源限制为单卡
3. 从显存共享的角度来说,单GPU卡共享具有可行性,主要实现步骤包括:
1. 扩展资源定义,重新定义GPU资源,主要是对显存和GPU数量的定义
2. 扩展调度器,负责在全局调度器Filter和Bind的时候判断节点上单个GPU卡是否能够提供足够的GPU显存
3. 利用Nvidia Device Plugin插件机制,完成资源上报和分配
Device Plugins: https://kubernetes.io/zh/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/
AMD - deploying-amd-gpu-device-plugin: https://kubernetes.io/zh/docs/tasks/manage-gpus/scheduling-gpus/#deploying-amd-gpu-device-plugin
NVIDIA - deploying-nvidia-gpu-device-plugin: https://kubernetes.io/zh/docs/tasks/manage-gpus/scheduling-gpus/#deploying-nvidia-gpu-device-plugin
k8s-device-plugin: https://github.com/RadeonOpenCompute/k8s-device-plugin
Elastic GPU:https://github.com/elastic-ai/elastic-gpu

原文作者:云原生AI百宝箱;如有侵权,请联系管理员删除。