Kubernetes(下文简称为k8s)具有对机器的资源进行分配和使用的能力,比如k8s可以指定容器最多使用多少内存以及使用多少CPU计算资源。那么问题来了,一般来说容器就是使用CPU和内存资源,那么对于需要使用显卡的Pod,k8s也能够支持吗?答案当然是可以啦!目前k8s不仅支持容器请求GPU资源,还支持请求几块显卡的GPU资源,这使得k8s在深度学习等场景下也有了用武之地。

所以本文的主要内容就是如何在k8s集群中使用GPU资源

系列文章

本文为【Docker & Kubernetes & GPU】系列文章中的一篇:

环境配置

本文实践的服务器环境为:

  • CentOS Linux release 7.5.1804 (Core)
  • 内核版本:3.10.0-862.14.4.el7.x86_64
  • Docker-CE版本:18.06.1-ce
  • Nvidia-Docker版本:2.0.3
  • Kubernetes版本:1.12.2
  • NVIDIA/k8s-device-plugin版本:1.11
  • 每个Node节点具有两张P4显卡

1、安装k8s-device-plugin

以下内容根据官方安装指南进行简化整理,完整版请移步:https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/、https://github.com/NVIDIA/k8s-device-plugin

自从k8s 1.8版本开始,官方开始推荐使用device plugin的方式来调用GPU使用。截至目前,Nvidia和AMD都推出了相应的设备插件,使得k8s调用GPU变得容易起来。因为我们是Nvidia的显卡,所以需要安装NVIDIA GPU device plugin

在这里需要提前说明两个参数,--feature-gates="Accelerators=true"--feature-gates="DevicePlugins=true"。在很多教程中都说明若要使用GPU,需要设置Accelerators为true,而实际上该参数在1.11版本之后就弃用了。而将DevicePlugins设置为true也是在1.9版本之前需要做的事情,在1.10版本之后默认就为true。所以对于我们来说,因为使用的是1.12版本,这两个feature-gates都不需要做设置

具体的feature-gates说明可以参见该链接

前期准备

运行NVIDIA GPU device plugin需要所有Node节点满足以下条件(如果允许Master节点部署Pod,那么Master节点也需要满足):

  • 显卡驱动版本大于361.93
  • Nvidia-Docker版本大于2.0
  • 将nvidia配置为Docker默认的runtime
  • Kubernetes版本为1.11

关于支持的k8s版本不用担心,并不是局限于1.11。因为使用的API版本为beta,所以还可额外支持未来两个版本,也即1.11、1.12、1.13。详见该issue

显卡驱动以及Nvidia-Docker的准备工作在之前的《Docker安装指南以及使用GPU》介绍中已经完成,所以这里我们只需要将nvidia配置为Docker默认的runtime

修改Docker默认的runtime

编辑/etc/docker/daemon.json文件,增加"default-runtime": "nvidia"键值对,此时该文件的内容应该如下所示(registry-mirrors是之前添加的国内镜像下载地址):

1
2
3
4
5
6
7
8
9
10
{
"registry-mirrors": ["https://registry.docker-cn.com"],
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
}
}

修改Docker的配置后,切记一定要将Docker重启systemctl restart docker,否则配置修改无效

安装

在Master节点上执行

1
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.11/nvidia-device-plugin.yml

稍等一会后,使用kubectl describe nodes查看节点信息,可以看到具有GPU的Node节点中可获取的资源已包括GPU

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 安装前
Capacity:
cpu: 8
ephemeral-storage: 51474024Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 41036940Ki
pods: 110
Allocatable:
cpu: 8
ephemeral-storage: 47438460440
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 40934540Ki
pods: 110

# 安装后
Capacity:
cpu: 8
ephemeral-storage: 51474024Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 41036940Ki
nvidia.com/gpu: 2
pods: 110
Allocatable:
cpu: 8
ephemeral-storage: 47438460440
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 40934540Ki
nvidia.com/gpu: 2
pods: 110

测试

在Master节点上创建~/tf-pod.yaml文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: tf-pod
spec:
containers:
- name: tf-container
image: tensorflow/tensorflow:latest-gpu
resources:
limits:
nvidia.com/gpu: 1 # requesting 1 GPUs

执行kubectl apply -f ~/tf-pod.yaml创建Pod。使用kubectl get pod可以看到该Pod已经启动成功

1
2
NAME     READY   STATUS    RESTARTS   AGE
tf-pod 1/1 Running 0 40s

执行kubectl exec tf-pod -it -- bash进入Pod内部。测试显卡信息以及TensorFlow的GPU调用,结果如下:

可以看到成功运行。这也说明k8s完成了对GPU资源的调用

一些错误

  • 0/3 nodes are available: 3 Insufficient nvidia.com/gpu.

该错误是通过执行kubectl describe pod xxx查看调用GPU的Pod中Event部分呈现的。导致该错误的原因有很多种:比如没有GPU资源却申请,或者申请的GPU资源超过物理机。总而言之就是请求不到GPU资源

  • Failed to initialize NVML: could not load NVML library.If this is a GPU node, did you set the docker default runtime to `nvidia`?

该错误也是通过执行kubectl describe pod xxx查看调用GPU的Pod中Event部分呈现的。如果修改了Docker的配置后(增加默认runtime)没有重启Docker就直接安装device-plugin的话会导致该错误。因为这时device-plugin启动Pod所运行的容器是加载不到显卡的。所以修改Docker配置后一定要重启Docker,再进行安装

也可以用以下命令测试device-plugin在节点上是否生效

1
docker run --security-opt=no-new-privileges --cap-drop=ALL --network=none -it -v /var/lib/kubelet/device-plugins:/var/lib/kubelet/device-plugins nvidia/k8s-device-plugin:1.11

出现以下信息代表没生效

1
2
3
4
5
2018/11/08 02:58:17 Loading NVML
2018/11/08 02:58:17 Failed to initialize NVML: could not load NVML library.
2018/11/08 02:58:17 If this is a GPU node, did you set the docker default runtime to nvidia?
2018/11/08 02:58:17 You can check the prerequisites at: https://github.com/NVIDIA/k8s-device-plugin#prerequisites
2018/11/08 02:58:17 You can learn how to set the runtime at: https://github.com/NVIDIA/k8s-device-plugin#quick-start

出现以下信息代表生效了

1
2
3
4
5
6
2018/11/08 02:58:46 Loading NVML
2018/11/08 02:58:46 Fetching devices.
2018/11/08 02:58:46 Starting FS watcher.
2018/11/08 02:58:46 Starting OS watcher.
2018/11/08 02:58:46 Starting to serve on /var/lib/kubelet/device-plugins/nvidia.sock
2018/11/08 02:58:46 Registered device plugin with Kubelet
  • 安装了device-plugin后,device-plugin Pod正常启动,但是依旧无法调用GPU资源,查看Node节点信息发现GPU处数量为0

当时遇见该问题的过程为:修改Docker配置,安装device-plugin,无法启动,重启Docker,device-plugin启动正常,但GPU无法调用

原因就在于启动device-plugin的时候Docker配置没有生效,虽然后续重启Docker了,但是device-plugin的Pod没有更新,所以依旧捕获不到GPU信息。解决办法是杀掉device-plugin的Pod,让其重新生成

最主要的原因是没按顺序操作!!!灵感来源

2、多型号GPU支持

如果不同的节点具有不同的GPU型号,可以给节点打上Label,然后在调用的时候指定Node

1
2
3
# Label your nodes with the accelerator type they have.
kubectl label nodes <node-with-k80> accelerator=nvidia-tesla-k80
kubectl label nodes <node-with-p100> accelerator=nvidia-tesla-p100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
nodeSelector:
accelerator: nvidia-tesla-p100 # or nvidia-tesla-k80 etc.

3、小结

k8s能够调度GPU资源,使得k8s的应用范围又大大扩展了,尤其是在深度学习领域。不过目前k8s只支持卡级别的调度,并且显卡资源是独占,无法在多个容器之间分享,这一点在使用过程中需要特别注意。

References