背景
我們知道,如果在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是否正常工作。