最近和AI团队一起做项目,需要将机器学习的项目部署进k8s。因为要使用节点的GPU资源,普通部署的k8s集群不能用了,因为docker只能对CPU和内存而不能对GPU资源进行共享和隔离。这一节我们就一起来看看怎么部署一个能分配GPU资源的k8s集群。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

操作环境

  • master(CPU) - ubuntu 18.04
  • gpu-node(GPU) - ubuntu 18.04

利用kubeadm安装

基本上跟着《【Kubernetes 003】Centos7通过Kubeadm安装Kubernetes1.15详解(附一键安装脚本)》进行安装即可成功创建集群。Centos和Ubuntu的命令稍微有些不同,但是大同小异,不影响整体步骤。

但是如果想在GPU节点的pod内使用GPU资源,还得对GPU节点做一些特殊的设定。所以请在将GPU节点加入master之前,对GPU节点完成下述操作

GPU节点安装准备

查看Linux的GPU信息

先更新本地数据库,然后获取GPU的详细信息

sudo update-pciids
sudo lspci | grep VGA -A 10

如果lspci命令没有找到,先通过下面命令安装

sudo yum install pciutils

我这边机器信息如下

root@gpu-node:~# lspci -v | grep VGA -A 10
01:00.0 VGA compatible controller: NVIDIA Corporation TU104 [GeForce RTX 2070 SUPER] (rev a1) (prog-if 00 [VGA controller])
	Subsystem: Micro-Star International Co., Ltd. [MSI] Device c729
	Flags: bus master, fast devsel, latency 0, IRQ 130
	Memory at a3000000 (32-bit, non-prefetchable) [size=16M]
	Memory at 90000000 (64-bit, prefetchable) [size=256M]
	Memory at a0000000 (64-bit, prefetchable) [size=32M]
	I/O ports at 3000 [size=128]
	[virtual] Expansion ROM at 000c0000 [disabled] [size=128K]
	Capabilities: [60] Power Management version 3
	Capabilities: [68] MSI: Enable+ Count=1/1 Maskable- 64bit+
	Capabilities: [78] Express Legacy Endpoint, MSI 00

如果跟我的机器一样是nvidia的显卡,根据显示的型号去查询支持的最新driver,安装完driver就会有管理工具nvidia-smi

nvidia显卡持续查看使用情况,每秒更新一次

watch -n 1 nvidia-smi

我这边的GPU和driver信息如下

root@gpu-node:~# nvidia-smi
Tue May 19 10:58:25 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 435.21       Driver Version: 435.21       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 207...  Off  | 00000000:01:00.0 Off |                  N/A |
| 24%   28C    P8    11W / 215W |     19MiB /  7982MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1765      G   /usr/lib/xorg/Xorg                             9MiB |
|    0      1799      G   /usr/bin/gnome-shell                           8MiB |
+-----------------------------------------------------------------------------+
  • Nvidia GeForce RTX 2070 SUPER
  • Driver版本 435.21
  • 显存大小 7982MiB

安装nvidia-docker2

下面对原生docker进行改进,使其能使用GPU资源。不同厂商有自己的解决方法,Nvidia官方推出了nvidia-docker来对原生进行改善,目前是2.x版本,说明看这里

首先确保安装了nvidia driver以及原生docker,然后跟着这里的方法安装repo,命令如下

curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
  sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update

之后就可以搜索到了,这里是2.3版本

root@gpu-node:~# apt search nvidia-docker
Sorting... Done
Full Text Search... Done
nvidia-docker2/bionic 2.3.0-1 all
  nvidia-docker CLI wrapper

因为后面还要安装别的插件,有兼容性问题,请确保版本在2.0以上

然后安装nvidia-docker2

sudo apt-get install -y nvidia-docker2

在最后会问你要如何操作/etc/docker/daemon.json这个文件

Configuration file '/etc/docker/daemon.json'
 ==> File on system created by you or by a script.
 ==> File also in package provided by package maintainer.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** daemon.json (Y/I/N/O/D/Z) [default=N] ?

这里我们直接选择Y让安装程序去覆盖现有文件,如下

root@gpu-node:~# cat /etc/docker/daemon.json
{
    "runtimes": {
        "nvidia": {
            "path": "nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

可以看到为docker添加了一个新的runtime

重启一下docker服务

service docker restart

这样在机器上就多了一个docker的runtime,在跑docker的时候可以指定runtime

docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi

或者用下面的方法一劳永逸直接将nvidia指定为默认的runtime,代替runc

# Update the default configuration and restart
pushd $(mktemp -d)
(sudo cat /etc/docker/daemon.json 2>/dev/null || echo '{}') | \
    jq '. + {"default-runtime": "nvidia"}' | \
    tee tmp.json
sudo mv tmp.json /etc/docker/daemon.json
popd
sudo systemctl restart docker

# No need for nvidia-docker or --engine=nvidia
docker run --rm -it nvidia/cuda nvidia-smi

通过下面的命令查看是否成功

docker info | grep nvidia

如果出现下面的信息表示已经成功将nvidia变为默认的docker runtime,代表容器可以使用GPU资源了

root@gpu-node:~# docker info | grep nvidia
WARNING: No swap limit support
Runtimes: nvidia runc
Default Runtime: nvidia

GPU节点加入集群

完成上述步骤,按照master的log信息将GPU节点加入集群,之后就会看到有两个node

root@control-plane-1:~# kubectl get node
NAME              STATUS   ROLES    AGE   VERSION
control-plane-1   Ready    master   80m   v1.15.10
gpu-node          Ready    <none>   53m   v1.15.10

我这里对master设置了taint,不允许pod被调度到master上

root@control-plane-1:~# kubectl describe node control-plane-1 | grep Taints
Taints:             node-role.kubernetes.io/master:NoSchedule

安装插件

虽然容器可以使用GPU资源了,此时如果试着去跑官方给的GPU的测试deployment,会发现所有pod都在pending状态,查看pod状态会出现如下报错

Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  48s (x18 over 24m)  default-scheduler  0/2 nodes are available: 2 Insufficient nvidia.com/gpu.

根据k8s官方文档对于GPU分配的说明,必须要安装另一个第三方的Nvidia插件才可以完成。

官方给出的插件对于GPU的分配不够智能和灵活,我们采用Aliyun的一个extender

自带插件的说明,不建议使用
http://www.podman.cn/?/article/7

阿里云的extender说明,颗粒更细
https://blog.spider.im/post/gpu-share-in-k8s/

步骤有点多,直接按照官方安装指导书一步步来。

部署extender

cd /etc/kubernetes/
curl -O https://raw.githubusercontent.com/AliyunContainerService/gpushare-scheduler-extender/master/config/scheduler-policy-config.json
cd /tmp/
curl -O https://raw.githubusercontent.com/AliyunContainerService/gpushare-scheduler-extender/master/config/gpushare-schd-extender.yaml
kubectl create -f gpushare-schd-extender.yaml

修改scheduler配置

目标是将/etc/kubernetes/scheduler-policy-config.json加入到/etc/kubernetes/manifests/kube-scheduler.yaml配置中。需要注意的是需要先在/etc/kubernetes/manifests/目录外生成配置文件再复制到目录内,触发静态pod的自动更新

首先复制配置文件

sudo cp /etc/kubernetes/manifests/kube-scheduler.yaml /tmp

然后模仿这个示例添加下面的两部分内容

- --policy-config-file=/etc/kubernetes/scheduler-policy-config.json
- mountPath: /etc/kubernetes/scheduler-policy-config.json
  name: scheduler-policy-config
  readOnly: true
- hostPath:
      path: /etc/kubernetes/scheduler-policy-config.json
      type: FileOrCreate
  name: scheduler-policy-config

然后复制回去

sudo cp /tmp/kube-scheduler.yaml /etc/kubernetes/manifests/kube-scheduler.yaml

检查发现shceduler的pod重启了表示修改生效

配置Device Plugin

注意如果这里已经配置了官方的Device Plugin例如nvidia-device-plugin,需要先删除

wget https://raw.githubusercontent.com/AliyunContainerService/gpushare-device-plugin/master/device-plugin-rbac.yaml
kubectl create -f device-plugin-rbac.yaml
wget https://raw.githubusercontent.com/AliyunContainerService/gpushare-device-plugin/master/device-plugin-ds.yaml
kubectl create -f device-plugin-ds.yaml

将GPU节点打上label

kubectl label node <target_node> gpushare=true

安装kubectl的扩展

cd /usr/bin/
wget https://github.com/AliyunContainerService/gpushare-device-plugin/releases/download/v0.3.0/kubectl-inspect-gpushare
chmod u+x /usr/bin/kubectl-inspect-gpushare

之后就可以用下面的命令查看GPU的分配情况了

root@control-plane-1:~# kubectl-inspect-gpushare
NAME      IPADDRESS      GPU0(Allocated/Total)  GPU Memory(GiB)
gpu-node  172.29.57.202  0/7                    0/7
---------------------------------------------------
Allocated/Total GPU Memory In Cluster:
0/7 (0%)  

我这里有7G的显存,所以显示为7。更详细的信息可以查看kubectl-inspect-gpushare -d

GPU分配测试

想要申请GPU,只需要声明aliyun.com/gpu-mem即可,单位是GiB。

例如如下测试文件deployment.yaml起两个pod,每个pod生成1GiB的GPU

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nvidia-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      name: nvidia-gpu-deploy
  template:
    metadata:
      labels:
        name: nvidia-gpu-deploy
    spec:
      containers:
      - name: cuda-container
        image: ubuntu
        command: ["sleep"]
        args: ["100000"]
        resources:
          limits:
            aliyun.com/gpu-mem: 1

等两个pod起来以后查看分配情况

root@control-plane-1:~/nvidia-test# kubectl-inspect-gpushare -d

NAME:       gpu-node
IPADDRESS:  172.29.57.202

NAME                                NAMESPACE  GPU0(Allocated)  
nvidia-deployment-5f4bbd9457-h4rfk  default    1                
nvidia-deployment-5f4bbd9457-wqx8v  default    1                
Allocated :                         2 (28%)    
Total :                             7          
------------------------------------------------------------------------------------------------------


Allocated/Total GPU Memory In Cluster:  2/7 (28%)  

删除kubeadm创建的集群

如果因为某些原因要销毁整个集群也很容易,因为是用kubeadm搭建的,只需要用下面的命令就可以

kubeadm reset

总结

在k8s中部署GPU机器学习程序已经成为一种趋势,虽然原生docker对GPU的支持没跟上,但是厂商的支持还是很多的,可以多去看看Nvidia的文档,很详细。

Logo

开源、云原生的融合云平台

更多推荐