前言: 在前面两个章节中已经介绍了如何构建GPU的基础环境以及使用Docker方式来优雅的运行GPU应用,单纯的使用Docker这种方式是无法满足大规模的应用调度和管理的,对于集群调度以及容器化管理方面,我们也采用了业界比较知名的容器编排调度管理工具Kubernetes,本篇文章简单介绍GPU业务容器在Kubernetes上的运行。
GPU环境下玩转Docker(一)
GPU环境下玩转Docker(二)
使用Kubernetes调度GPU容器
1. 准备工作
k8s当前已经支持GPU的资源调度了,详情可查看中文文档:k8s调度GPU。
这里介绍一个简单的实例。
注意:官方网站中也提到,当前k8s调度GPU也还是处于实验性阶段,在测试K8S调度GPU之前需要做以下相关工作。
-
Kubernetes 节点必须预先安装好 NVIDIA 驱动,否则,Kubelet 将检测不到可用的GPU信息;如果节点的 Capacity 属性中没有出现 NIVIDA GPU 的数量,有可能是驱动没有安装或者安装失败,请尝试重新安装
-
在整个 Kubernetes 系统中,feature-gates 里面特定的 alpha 特性参数 Accelerators 必须设置为 true:–feature-gates=“Accelerators=true”
-
Kuberntes 节点必须使用 docker 引擎作为容器的运行引擎
以上工作完成后,节点会自动发现主机上的NVIDIA GPU机器,并将其作为可调度资源暴露。
注意1:为了防止错误调度,需要给GPU机器去做额外的标签
注意2:kubelet程序在启动时必须加载--allow-privileged=true参数,以对所有的GPU容器设置特权模式来识别宿主机资源
kubelet启动参数
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
# cat /usr/lib/systemd/system/kubelet.service | grep -v ^#
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=/export/lib/kubelet
EnvironmentFile=-/export/kubernetes/config
EnvironmentFile=-/export/kubernetes/kubelet
ExecStart=/usr/bin/kubelet \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBELET_API_SERVER \
$KUBELET_ADDRESS \
$KUBELET_PORT \
$KUBELET_HOSTNAME \
$KUBE_ALLOW_PRIV \
$KUBELET_POD_INFRA_CONTAINER \
$KUBELET_ARGS \
Restart=on-failure
# cat /export/kubernetes/kubelet | grep -v ^#
KUBELET_ADDRESS="--address=10.0.0.1"
KUBELET_HOSTNAME="--hostname-override=10.0.0.1"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=idockerhub.xxb.com/k8s/pause"
KUBELET_ARGS="--cgroup-driver=cgroupfs \
--cluster-dns=10.254.0.2 \
--experimental-bootstrap-kubeconfig=/export/kubernetes/bootstrap.kubeconfig \
--kubeconfig=/export/kubernetes/kubelet.kubeconfig \
--require-kubeconfig \
--cert-dir=/export/kubernetes/ssl \
--cluster-domain=cluster.local. \
--hairpin-mode promiscuous-bridge \
--serialize-image-pulls=false \
--feature-gates='Accelerators=true'"
# cat /export/kubernetes/config | grep -v ^#
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow-privileged=true"
KUBE_MASTER="--master=http://10.0.0.2:8080"
#/usr/bin/kubelet --logtostderr=true --v=0 --address=10.0.0.1 --hostname-override=10.0.0.1 --allow-privileged=true --pod-infra-container-image=idockerhub.xxb.com/k8s/pause --cgroup-driver=cgroupfs --cluster-dns=10.254.0.2 --experimental-bootstrap-kubeconfig=/export/kubernetes/bootstrap.kubeconfig --kubeconfig=/export/kubernetes/kubelet.kubeconfig --require-kubeconfig --cert-dir=/export/kubernetes/ssl --cluster-domain=cluster.local. --hairpin-mode promiscuous-bridge --serialize-image-pulls=false --feature-gates=Accelerators=true Restart=on-failure
|
1
2
3
4
5
6
7
8
9
10
11
|
# /usr/local/bin/kubectl -s http://10.0.0.2:8080 label nodes 10.0.0.1 type=gpu
# /usr/local/bin/kubectl -s http://10.0.0.2:8080 get nodes --show-labels
NAME STATUS AGE VERSION LABELS
10.0.0.2 Ready 2d v1.6.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.0.2
10.0.0.1 Ready 4h v1.6.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.0.1,type=gpu
# /usr/local/bin/kubectl -s http://10.0.0.2:8080 get node -l type=gpu
NAME STATUS AGE VERSION
10.0.0.1 Ready 4h v1.6.2
|
2. 测试用例
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
34
35
36
37
38
39
40
41
42
43
44
45
46
|
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-gpu
spec:
replicas: 1
template:
metadata:
labels:
name: test-gpu
spec:
containers:
- name: test-gpu
image: idockerhub.xxb.com/k8s/tensorflow/tensorflow:latest-gpu
ports:
- containerPort: 8888
resources:
limits:
alpha.kubernetes.io/nvidia-gpu: 2
volumeMounts:
- mountPath: /usr/local/nvidia
name: nvidia-driver
- mountPath: /dev/nvidia0
name: nvidia0
- mountPath: /dev/nvidia-uvm
name: nvidia-uvm
- mountPath: /dev/nvidia-uvm-tools
name: nvidia-uvm-tools
- mountPath: /dev/nvidiactl
name: nvidiactl
volumes:
- name: nvidia-driver
hostPath:
path: /var/lib/nvidia-docker/volumes/nvidia_driver/375.39
- name: nvidia0
hostPath:
path: /dev/nvidia0
- name: nvidia-uvm
hostPath:
path: /dev/nvidia-uvm
- name: nvidia-uvm-tools
hostPath:
path: /dev/nvidia-uvm-tools
- name: nvidiactl
hostPath:
path: /dev/nvidiactl
|
注意1:k8s调度gpudocker容器需要和业务方进行强关联,所以对于cuda,cudnn,nvidia-docker的版本要求是非常高的。
注意2:k8s调度的gpu其实是独享的,也就是上面分配了2个gpu设备,那么该宿主机上将最多只能创建2个卡的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
|
# ll /var/lib/nvidia-docker/volumes/nvidia_driver/
total 0
drwxr-xr-x 5 nvidia-docker nvidia-docker 38 Oct 18 21:38 375.39
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f3a3f8dd30ca idockerhub.xxb.com/jdjr/tensorflow-gpu@sha256:7844f390a9d5ff369c7756a52ca65dae095087c736935e9310e12c7caa7c73dd "/run_jupyter.sh --al" 2 days ago Up 2 days k8s_test-gpu_test-gpu-1430106381-9n8hb_default_97177dcf-b7da-11e7-b982-ecf4bbc19ea8_0
7650dce5a686 idockerhub.xxb.com/k8s/pause "/pod" 2 days ago Up 2 days k8s_POD_test-gpu-1430106381-9n8hb_default_97177dcf-b7da-11e7-b982-ecf4bbc19ea8_0
# 尝试创建一个4卡的gpu设备的容器
# docker exec -it 0cce1f2a59dd ls -la /dev/nvidia*
crw-rw-rw- 1 root root 244, 0 Jul 19 05:14 /dev/nvidia-uvm
crw-rw-rw- 1 root root 244, 1 Jul 19 05:14 /dev/nvidia-uvm-tools
crw-rw-rw- 1 root root 195, 0 Jun 20 02:54 /dev/nvidia0
crw-rw-rw- 1 root root 195, 1 Oct 23 10:06 /dev/nvidia1
crw-rw-rw- 1 root root 195, 2 Oct 23 10:06 /dev/nvidia2
crw-rw-rw- 1 root root 195, 3 Oct 23 10:06 /dev/nvidia3
crw-rw-rw- 1 root root 195, 255 Jun 20 02:54 /dev/nvidiactl
尝试运算服务后的结果:
2017-10-23 10:14:25.172617: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1045] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla M40 24GB, pci bus id: 0000:06:00.0)
|
通过上述测试用例,就可以跑通官方tensorflow gpu镜像。
NOTES:
GPUs 只能通过limits选项指定
GPUs 是严格隔离的,不同容器之间不能共享
每个容器可以请求一个或者多个GPUS
GPUs 只能正整数级请求
使用K8S调度GPU容器必须开启privileged模式(识别宿主机设备)
Author
BGBiao
LastMod
2020-11-01
License
原创文章,如需转载请注明文章作者和出处。谢谢!