背景
我们知道,如果在Kubernetes中支持GPU设备调度,需要做如下的工作:
- 节点上安装nvidia驱动
- 节点上安装nvidia-docker
- 集群部署gpu device plugin,用于为调度到该节点的pod分配GPU设备。
除此之外,如果你需要监控集群GPU资源使用情况,你可能还需要安装DCCM exporter结合Prometheus输出GPU资源监控信息。
要安装和管理这么多的组件,对于运维人员来说压力不小。基于此,NVIDIA开源了一款叫NVIDIA GPU Operator的工具,该工具基于Operator Framework实现,用于自动化管理上面我们提到的这些组件。
NVIDIA GPU Operator有以下的组件构成:
- 安装nvidia driver的组件
- 安装nvidia container toolkit的组件
- 安装nvidia devcie plugin的组件
- 安装nvidia dcgm exporter组件
- 安装gpu feature discovery组件
本系列文章不打算一上来就开始讲NVIDIA GPU Operator,而是先把各个组件的安装详细的分析一下,然后手动安装这些组件,最后再来分析NVIDIA GPU Operator就比较简单了。
在本篇文章中,我们将介绍NVIDIA GPU Operator安装NVIDIA Device Plugin的原理。
NVIDIA Device Plugin介绍
本小节简单的介绍一下什么是NVIDIA Device Plugin(如果需要更详细的了解k8s device plugin机制,请参考网上其他文档)。在介绍NVIDIA Container Toolkit时,我们提到过,当启动docker容器时,可以通过环境变量指定容器所需的GPU,例如:
$ docker run -d --name gpu-test -e NVIDIA_VISIBLE_DEVICES=0,1 centos:7 sleep 5000
但是在Kubernetes集群中应该如何给一个应用指定使用GPU呢?可以通过给pod的容器指定环境变量NVIDIA_VISIBLE_DEVICES实现吗?可以,但是这样做有一些问题:
- 因为kubernetes集群中有很多节点,每个节点的GPU数量可能不同,假设指定pod的容器的环境变量为”“NVIDIA_VISIBLE_DEVICES=3,4”,但是pod调度到节点是随机的,最终pod所在的节点如果只有一张GPU卡,那么pod将启动失败。
- 直接在pod的容器中指定环境变量NVIDIA_VISIBLE_DEVICES,用户无法维护哪些节点的哪些GPU已经使用,哪些未使用。
Kubernetes从1.8开始支持设备插件机制,只要用户实现与设备相对应的device plugin,然后在pod提交时指定需要使用多少个设备,kubernetes就能为pod自动挂载设备并且维护节点上设备状态(即哪些设备已使用,哪些设备未使用)。
NVIDIA Device Plugin的工作原理简单的概况为:
- pod spec中的resources.limits字段写入运行该pod需要多少个GPU设备,像下面这样:
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: gpu-container-1
image: centos:7
resources:
limits:
nvidia.com/gpu: 2 # 代表为该Pod申请了2个GPU。
- 每个节点的kubelet组件维护该节点的GPU设备状态(哪些已用,哪些未用)并定时报告给调度器,调度器知道每一个节点有多少张GPU卡可用。
- 调度器为pod选择节点时,从符合条件的节点中选择一个节点。
- 当pod调度到节点上后,kubelet组件为pod分配GPU设备ID,并将这些ID作为参数传递给NVIDIA Device Plugin
- NVIDIA Device Plugin将分配给该pod的容器的GPU设备ID写入到容器的环境变量NVIDIA_VISIBLE_DEVICES中,然后将信息返回给kubelet。
- kubelet启动容器。
- NVIDIA Container Toolkit检测容器的spec中存在环境变量NVIDIA_VISIBLE_DEVICES,然后根据环境变量的值将GPU设备挂载到容器中。
在集群中部署NVIDIA Device Plugin
接下来演示一下怎样在集群中部署nvidia device plugin。
前提条件
在进行操作之前,请确认下面的条件是否满足:
- 集群的版本 > 1.8。
- 集群中的GPU节点已经安装了GPU驱动,如果没有安装驱动,请参考本系列文件中关于NVIDIA驱动的安装。
- 集群中的GPU节点已经安装NVIDIA Container Toolkit,如果没有安装,请参考本系列文件中关于NVIDIA Container Toolkit的安装。
安装步骤
1.下载gpu-operator源码。
$ git clone -b 1.6.2 https://github.com/NVIDIA/gpu-operator.git
$ cd gpu-operator
$ export GPU_OPERATOR=$(pwd)
2.确认节点已经打了标签nvidia.com/gpu.present=true。
$ kubectl get nodes -L nvidia.com/gpu.present
NAME STATUS ROLES AGE VERSION GPU.PRESENT
cn-beijing.192.168.8.44 Ready <none> 13d v1.16.9-aliyun.1 true
cn-beijing.192.168.8.45 Ready <none> 13d v1.16.9-aliyun.1 true
cn-beijing.192.168.8.46 Ready <none> 13d v1.16.9-aliyun.1 true
cn-beijing.192.168.9.159 Ready master 13d v1.16.9-aliyun.1
cn-beijing.192.168.9.160 Ready master 13d v1.16.9-aliyun.1
cn-beijing.192.168.9.161 Ready master 13d v1.16.9-aliyun.1
3.修改assets/state-device-plugin/0300_rolebinding.yaml,注释两个字段,否则无法提交:
- 将userNames这一行和其后面的一行注释。
#userNames:
#- system:serviceaccount:gpu-operator:nvidia-device-plugin
- 将roleRef.namespace这一行注释。
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: nvidia-device-plugin
# namespace: gpu-operator-resources
4.修改assets/state-device-plugin/0400_device_plugin.yml,填入正确的镜像。
- 更改container nvidia-device-plugin-ctr的镜像为nvcr.io/nvidia/k8s-device-plugin:v0.8.2-ubi8。
containers:
- image: "nvcr.io/nvidia/k8s-device-plugin:v0.8.2-ubi8"
name: nvidia-device-plugin-ctr
securityContext:
privileged: true
- 更改initContainer toolkit-validation的镜像为nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2。
initContainers:
- name: toolkit-validation
image: "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2"
command: ['sh', '-c']
args: ["/tmp/vectorAdd"]
securityContext:
privileged: true
5.部署device plugin。
$ kubectl apply -f assets/state-device-plugin
6.查看pod是否已经处于running。
$ kubectl get po -n gpu-operator-resources -l app=nvidia-device-plugin-daemonset
NAME READY STATUS RESTARTS AGE
nvidia-device-plugin-daemonset-kllvw 1/1 Running 0 78s
nvidia-device-plugin-daemonset-lxdl6 1/1 Running 0 78s
nvidia-device-plugin-daemonset-pwqb4 1/1 Running 0 78s
7.查看pod日志。
$ kubectl logs nvidia-device-plugin-daemonset-kllvw -n gpu-operator-resources --tail=20
2021/03/26 07:11:02 Loading NVML
2021/03/26 07:11:02 Starting FS watcher.
2021/03/26 07:11:02 Starting OS watcher.
2021/03/26 07:11:02 Retreiving plugins.
2021/03/26 07:11:02 Starting GRPC server for 'nvidia.com/gpu'
2021/03/26 07:11:02 Starting to serve 'nvidia.com/gpu' on /var/lib/kubelet/device-plugins/nvidia-gpu.sock
2021/03/26 07:11:02 Registered device plugin for 'nvidia.com/gpu' with Kubelet
验证
为了验证集群节点的GPU是否可用,可以提交一个tensorfolw任务(该任务申请了一个GPU,即nvidia.com/gpu: 1),任务的yaml如下:
$ cat /tmp/gpu-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-gpu
labels:
test-gpu: "true"
spec:
containers:
- name: training
image: registry.cn-beijing.aliyuncs.com/ai-samples/tensorflow:1.5.0-devel-gpu
command:
- python
- tensorflow-sample-code/tfjob/docker/mnist/main.py
- --max_steps=300
- --data_dir=tensorflow-sample-code/data
resources:
limits:
nvidia.com/gpu: 1
workingDir: /root
restartPolicy: Never
1.提交任务。
$ kubectl apply -f /tmp/gpu-test.yaml
2.查看pod是否处于Running。
$ kubectl get po -l test-gpu=true
NAME READY STATUS RESTARTS AGE
test-gpu 1/1 Running 0 2m54s
3.查看pod日志。
$ kubectl logs test-gpu --tail 10
Accuracy at step 220: 0.9288
Accuracy at step 230: 0.936
Accuracy at step 240: 0.9393
Accuracy at step 250: 0.9405
Accuracy at step 260: 0.9409
Accuracy at step 270: 0.9428
Accuracy at step 280: 0.9399
Accuracy at step 290: 0.9408
Adding run metadata for 299
Total Train-accuracy=0.9408
说明各个组件(nvidia driver,nvidia container toolkit, nvidia device plugin)能够正常工作。
总结
本篇文章简单介绍了nvidia device plugin并尝试在k8s集群中以daemonset方式部署nvidia device plugin,最后通过运行一个tensorflow任务验证nvidia device plugin是否正常工作。