K8S节点GPU虚拟化(vGPU)
4paradigm提供了k8s-device-plugin,该插件基于NVIDIA官方插件(NVIDIA/k8s-device-plugin),在保留官方功能的基础上,实现了对物理GPU进行切分,并对显存和计算单元进行限制,从而模拟出多张小的vGPU卡。,虚拟化之后原GPU可以切成6份使用,每份占用显存8Gi,虚拟化之后多出的显存实际是用内存作显存使用,所以注意节点的内存大小(要留够节点正常运行所
vGPU实现方案
4paradigm提供了k8s-device-plugin,该插件基于NVIDIA官方插件(NVIDIA/k8s-device-plugin),在保留官方功能的基础上,实现了对物理GPU进行切分,并对显存和计算单元进行限制,从而模拟出多张小的vGPU卡。在k8s集群中,基于这些切分后的vGPU进行调度,使不同的容器可以安全的共享同一张物理GPU,提高GPU的利用率。此外,插件还可以对显存做虚拟化处理(使用到的显存可以超过物理上的显存),运行一些超大显存需求的任务,或提高共享的任务数。
部署
需要在节点上将nvidia runtime做为你的docker runtime预设值。我们将编辑docker daemon的配置文件,此文件通常在/etc/docker/daemon.json路径:
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
}
"default-shm-size": "2G"
}
虚拟化之前,在GPU节点启动一个GPU应用pod,通过nvidia-smi
查看GPU分配及使用情况。
pod资源请求:
Limits:
cpu: 4
memory: 16Gi
nvidia.com/gpu: 1
Requests:
cpu: 2
memory: 8Gi
nvidia.com/gpu: 1
pod内执行nvidia-smi
:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.91.03 Driver Version: 460.91.03 CUDA Version: 11.3 |
|-------------------------------+----------------------+----------------------+
| 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 A10 On | 00000000:00:08.0 Off | Off |
| 0% 53C P0 71W / 150W | 1475MiB / 24258MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
+-----------------------------------------------------------------------------+
可以看到此时占用A10节点整张卡显存 24Gi 。
关闭NVIDIA官方插件
关闭NVIDIA官方插件(NVIDIA/k8s-device-plugin)。
把nvidia-device-plugin ds描述文件移除即可,为安全可以移动到其它目录,如下移动到家目录做备份保存。
[root@gpua10_1 ~]# mv /etc/kubernetes/manifests/nvidia-device-plugin.yml .
如上操作后,原来的nvidia pod:
(base) ➜ ~ kubectl -n kube-system get po |grep nvidia
nvidia-device-plugin-gpua10_1 1/1 Running 0 5d
移除ds描述文件后,以上pod会消失。
启动4paradigm新的k8s-device-plugin
ds文件如下:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nvidia-device-plugin-daemonset
namespace: kube-system
spec:
selector:
matchLabels:
name: nvidia-device-plugin-ds
updateStrategy:
type: RollingUpdate
template:
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ""
labels:
name: nvidia-device-plugin-ds
spec:
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
priorityClassName: "system-node-critical"
containers:
- image: 4pdosc/k8s-device-plugin:latest
# - image: m7-ieg-pico-test01:5000/k8s-device-plugin-test:v0.9.0-ubuntu20.04
imagePullPolicy: Always
name: nvidia-device-plugin-ctr
args: ["--fail-on-init-error=false", "--device-split-count=6", "--device-memory-scaling=2", "--device-cores-scaling=1"]
# args: ["--fail-on-init-error=false", "--device-split-count=3", "--device-memory-scaling=3", "--device-cores-scaling=1"]
env:
- name: PCIBUSFILE
value: "/usr/local/vgpu/pciinfo.vgpu"
- name: NVIDIA_MIG_MONITOR_DEVICES
value: all
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
add: ["SYS_ADMIN"]
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
- name: vgpu-dir
mountPath: /usr/local/vgpu
- mountPath: /tmp
name: hosttmp
volumes:
- name: device-plugin
hostPath:
path: /var/lib/kubelet/device-plugins
- name: vgpu-dir
hostPath:
path: /usr/local/vgpu
- hostPath:
path: /tmp
name: hosttmp
nodeSelector:
nvidia-vgpu: "on"
fail-on-init-error
: 布尔类型, 预设值是true。当这个参数被设置为true时,如果装置插件在初始化过程遇到错误时程序会返回失败,当这个参数被设置为false时,遇到错误它会打印信息并且持续阻塞插件。持续阻塞插件能让装置插件即使部署在没有GPU的节点(也不应该有GPU)也不会抛出错误。这样你在部署装置插件在你的集群时就不需要考虑节点是否有GPU,不会遇到报错的问题。然而,这么做的缺点是如果GPU节点的装置插件因为一些原因执行失败,将不容易察觉。现在预设值为当初始化遇到错误时程序返回失败,这个做法应该被所有全新的部署采纳。device-split-count
: 整数类型,预设值是2。NVIDIA装置的分割数。对于一个总共包含N张NVIDIA GPU的Kubernetes集群,如果我们将device-split-count参数配置为K,这个Kubernetes集群将有K * N个可分配的vGPU资源。注意,我们不建议将NVIDIA 1080 ti/NVIDIA 2080 ti device-split-count参数配置超过5,将NVIDIA T4配置超过7,将NVIDIA A100配置超过15。device-memory-scaling
: 浮点数类型,预设值是1。NVIDIA装置显存使用比例,可以大于1(启用虚拟显存,实验功能)。对于有M显存大小的NVIDIA GPU,如果我们配置device-memory-scaling参数为S,在部署了我们装置插件的Kubenetes集群中,这张GPU分出的vGPU将总共包含 S * M显存。每张vGPU的显存大小也受device-split-count参数影响。在先前的例子中,如果device-split-count参数配置为K,那每一张vGPU最后会取得 S * M / K 大小的显存。device-cores-scaling
: 浮点数类型,预设值与device-split-count数值相同。NVIDIA装置算力使用比例,可以大于1。如果device-cores-scaling参数配置为S device-split-count参数配置为K,那每一张vGPU对应的一段时间内 SM 利用率平均上限为S / K。属于同一张物理GPU上的所有vGPU SM利用率总和不超过1。enable-legacy-preferred
: 布尔类型,预设值是false。对于不支持 PreferredAllocation 的kubelet(<1.9)可以设置为true,以更好的选择合适的设备,开启时,本插件需要有对pod的读取权限,可参看 legacy-preferred-nvidia-device-plugin.yml。对于 kubelet >= 1.9 时,建议关闭。
如上"--device-split-count=6", "--device-memory-scaling=2"
, 表示在原显存基础上扩充二倍切成六份使用,也就是24Gi*2/6=8Gi
,虚拟化之后原GPU可以切成6份使用,每份占用显存8Gi,虚拟化之后多出的显存实际是用内存作显存使用,所以注意节点的内存大小(要留够节点正常运行所需的内存),不然会出现OOM的情况,在需要进行虚拟化的节点打上标签nvidia-vgpu: "on"即可。
部署以上ds,且节点打上标签后会出现:
(base) ➜ ~ kubectl -n kube-system get pod |grep nvidia
nvidia-device-plugin-daemonset-pm21x 1/1 Running 0 3d12h
此时pod内再次执行nvidia-smi
:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.91.03 Driver Version: 460.91.03 CUDA Version: 11.3 |
|-------------------------------+----------------------+----------------------+
| 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 A10 On | 00000000:00:0A.0 Off | 0 |
| 0% 43C P0 68W / 150W | 4375MiB / 7676MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
+-----------------------------------------------------------------------------+
可以看到pod分配了8Gi的显存。
注意
以上是在nvidia版本NVIDIA-SMI 460.91.03 Driver Version: 460.91.03 CUDA Version: 11.3
执行的操作。在升级到NVIDIA-SMI 515.105.01 Driver Version: 515.105.01 CUDA Version: 11.7
版本的时候遇到vGPU虚拟化不生效的问题,解决方案是:
- 升级libvgpu.so到最新
- 在server.go中添加
/tmp/vgpulock
文件权限。
os.MkdirAll("/tmp/vgpulock", 0777)
os.Chmod("/tmp/vgpulock", 0777)
response.Mounts = append(response.Mounts,
&pluginapi.Mount{"containerPath": "/usr/local/vgpu/libvgpu.so",
"HostPath": "/usr/local/vgpu/libvgpu.so", ReadOnly: true},
&pluginapi.Mount{"containerPath": "/etc/ld.so.preload",
"HostPath": "/usr/local/vgpu/ld.so.preload", ReadOnly: true},
&pluginapi.Mount{"containerPath": "/usr/local/vgpu/pciinfo.vgpu",
"HostPath": os.Getenv("PCIBUSFILE"), ReadOnly: true},
&pluginapi.Mount{"containerPath": "/tmp/vgpulock",
"HostPath": "/tmp/vgpulock", ReadOnly: true},
更多推荐
所有评论(0)