我们知道 如果在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?Container?Toolkit组件的原理。
NVIDIA?Container?Toolkit简介如果您需要在容器中使用NVIDIA?GPU设备 那么NVIDIA?Container?Toolkit是必不可少的组件。它的主要作用是将NVIDIA?GPU设备挂载到容器中。
支持docker的NVIDIA?Container?Tookit由如下的组件构成
nvidia-docker2nvidia-container-runtimenvidia-container-toolkitlibnvidia-container下面这幅架构图来自NVIDIA官网 详细介绍了各个组件的关系。
libnvidia-container介绍libnvidia-container提供了一个库和简单的CLI工具 以实现在容器当中支持使用GPU设备的目标。
nvidia-container-toolkit介绍nvidia-container-toolkit是一个实现了runC?prestart?hook接口的脚本 该脚本在runC创建一个容器之后 启动该容器之前调用 其主要作用就是修改与容器相关联的config.json 注入一些在容器中使用NVIDIA?GPU设备所需要的一些信息 比如 需要挂载哪些GPU设备到容器当中 。
nvidia-container-runtime介绍nvidia-container-runtime主要用于将容器runC?spec作为输入 然后将nvidia-container-toolkit脚本作为一个prestart?hook注入到runC?spec中 将修改后的runC?spec交给runC处理。
nvidia-docker2介绍nvidia-docker2只适用于docker 其主要作用是可以通过环境变量指定容器需要使用节点上哪些GPU。
基于容器安装NVIDIA?Container?Toolkit基于容器安装NVIDIA?Container?Toolkit所涉及的脚本请参考项目container-config 项目所涉及的代码在src目录下 有几个脚本需要说明一下
run.go 容器启动时的执行脚本 入口 由go语言编写 即容器中/work/nvidia-toolkit这个二进制文件。toolkit.sh 用于安装NVIDIA?Container?Toolkit /work/nvidia-toolkit将会调用该脚本。docker.go 由go语言编写 该源文件将会编译成容器中/work/docker二进制文件 用户更新节点/etc/docker/daemon.json并重启docker进程使配置生效 /work/docker同样也会被/work/nvidia-toolkit调用。1.镜像构建以Dockerfile.ubi8为例进行分析 比较重要的是如下的这两步
将以.go文件编译成二进制文件。RUN?go?build?-o?nvidia-toolkit?run.go RUN?go?build?-o?containerd?containerd.go RUN?go?build?-o?crio?crio.go RUN?go?build?-o?docker?docker.go RUN?go?build?-o?toolkit?toolkit.go RUN?rm?-rf?go.*? ?\ ????rm?-rf?*.go? ?\ ????rm?-rf?vendor安装包libnvidia-container1 libnvidia-container-tools nvidia-container-toolkit ?nvidia-container-runtime 然后容器启动时 将这些包中的二进制文件拷贝到宿主机上。
RUN?/bin/bash?-c? ?\ ????yum?install?-y?procps?\ ????libnvidia-container1-\${LIBNVIDIA_CONTAINER_VERSION/-/-0.1.}?\ ????libnvidia-container-tools-\${LIBNVIDIA_CONTAINER_VERSION/-/-0.1.}?\ ????nvidia-container-toolkit-\${NVIDIA_CONTAINER_TOOLKIT_VERSION/-/-0.1.}?\ ????nvidia-container-runtime-\${NVIDIA_CONTAINER_RUNTIME_VERSION/-/-0.1.}2.挂载相关目录到容器
在启动容器时 需要将节点上如下的目录挂载到容器中。
/etc/docker? ?/etc/docker 仅对docker有效 对containerd需要挂载/etc/containerd 因为需要修改节点/etc/docker/daemon.json这个文件 然后重启docker服务 NVIDIA?Container?Toolkit才会生效。/run/nvidia? ?/run/nvidia:?前面我们介绍过 基于容器安装NVIDIA驱动后 节点的/run/nvidia/driver其实是整个driver的rootfs NVIDIA?Container?Toolkit需要使用到该目录。/usr/local/nvidia? ?/usr/local/nvidia NVIDIA?Container?Toolkit将会安装到节点的/usr/local/nvidia目录下 所以该目录需要挂载。/var/run? ?/var/run 重启docker的过程中 需要用到/var/run/docker.sock这个文件 所以该目录需要挂载。3.安装过程说明整个安装过程由nvidia-toolkit这个工具完成 容器的启动命令为
nvidia-toolkit?/usr/local/nvidia
其中 /usr/local/nvidia为NVIDIA?Container?Toolkit的安装路径。
nvidia-toolkit 由run.go编译而成 这个工具主要做如下的几件事
将自身的进程ID写入/run/nvidia/toolkit.pid中 并启动goroutine捕获系统信号 执行退出清理操作 删除已安装的NVIDIA?Container?Toolkit 当容器被kill时 会执行卸载NVIDIA?Container?Toolkit操作 。如果节点上的/usr/local/nvidia/toolkit存在 表明该节点已经安装了NVIDIA?Container?Toolkit 那么删除/usr/local/nvidia/toolkit这个目录 重新安装。调用toolkit.sh执行安装操作。调用/work/docker 由docker.go编译而成 命令修改docker的配置 /etc/docker/daemon.json 并重启docker。处于sleep状态 等待系统信号。//?Run?runs?the?core?logic?of?the?CLI func?Run(c?*cli.Context)?error?{ err?: ?VerifyFlags() if?err?! ?nil?{ return?fmt.Errorf( unable?to?verify?flags:?%v ,?err) ??//?1.初始化 主要是将自身进程id写入/run/nvidia/toolkit.pid 并启动goroutine捕获系统信号 err? ?Initialize() if?err?! ?nil?{ return?fmt.Errorf( unable?to?initialize:?%v ,?err) defer?Shutdown() ??//?2.调用toolkit.sh执行安装操作 err? ?InstallToolkit() if?err?! ?nil?{ return?fmt.Errorf( unable?to?install?toolkit:?%v ,?err) ??//?修改runtime配置 以docker为例 主要是修改/etc/docker/daemon.json这个文件 err? ?SetupRuntime() if?err?! ?nil?{ return?fmt.Errorf( unable?to?setup?runtime:?%v ,?err) if?!noDaemonFlag?{ err? ?WaitForSignal() if?err?! ?nil?{ return?fmt.Errorf( unable?to?wait?for?signal:?%v ,?err) err? ?CleanupRuntime() if?err?! ?nil?{ return?fmt.Errorf( unable?to?cleanup?runtime:?%v ,?err) return?nil }
toolkit.sh这个脚本主要完成如下的一些事件
将容器中NVIDIA?Container?Toolkit组件所涉及的命令行工具和库文件移动到/usr/local/nvidia/toolkit目录下$??ls?/usr/local/nvidia/toolkit/ libnvidia-container.so.1??????nvidia-container-cli.real??????nvidia-container-runtime.real libnvidia-container.so.1.3.2??nvidia-container-runtime ?????nvidia-container-toolkit nvidia-container-cli ??????nvidia-container-runtime-hook??nvidia-container-toolkit.real在?/usr/local/nvidia/toolkit/.config/nvidia-container-runtime创建nvidia-container-runtime的配置文件config.toml 并设置nvidia-container-cli.root的值为/run/nvidia/driver。
$?cat?/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml disable-require? ?false #swarm-resource? ? DOCKER_RESOURCE_GPU #accept-nvidia-visible-devices-envvar-when-unprivileged? ?true #accept-nvidia-visible-devices-as-volume-mounts? ?false [nvidia-container-cli] root? ? /run/nvidia/driver #path? ? /usr/bin/nvidia-container-cli environment? ?[] #debug? ? /var/log/nvidia-container-toolkit.log #ldcache? ? /etc/ld.so.cache load-kmods? ?true #no-cgroups? ?false #user? ? root:video ldconfig? ? /sbin/ldconfig [nvidia-container-runtime] #debug? ? /var/log/nvidia-container-runtime.log
整个安装过程还是比较清晰的 如果需要了解更详细的内容 可以去读一下这些脚本。
/work/docker 针对Runtime为docker 主要是修改/etc/docker/daemon.json的Runtime为nvidia 并重启docker。
???? runtimes :?{ ???????? nvidia :?{ ???????????? args :?[], ???????????? path :? /usr/local/nvidia/toolkit/nvidia-container-runtime ????????} ????},在K8s集群的节点上安装NVIDIA?Container?Toolkit
这一部分将演示如何基于容器的方式为k8s集群的节点安装NVIDIA?Container?Toolkit。
前提条件在执行安装操作前 你需要确认以下的条件是否满足
k8s集群节点上不能安装NVIDIA?Container?Toolkit 如果已经安装 需要卸载掉。k8s集群节点已经通过基于容器的方式安装了NVIDIA驱动 如果还没安装驱动 请参考本系列之前的文章《NVIDIA?GPU?Operator分析一 NVIDIA?Driver安装》。本次演示的集群节点的操作系统为Centos7.7 如果是其他类型的操作 不保证安装步骤是否可用。安装步骤1.下载gpu-opeator源码。
$?git?clone?-b?1.6.2?https://github.com/NVIDIA/gpu-operator.git $?cd?gpu-operator $?export?GPU_OPERATOR $(pwd)?
2.确认节点NVIDIA驱动已经安装。
$?kubectl?get?po?-n?gpu-operator-resources?-l?app nvidia-driver-daemonset NAME????????????????????????????READY???STATUS????RESTARTS???AGE nvidia-driver-daemonset-4kxr2???1/1?????Running???0??????????10m nvidia-driver-daemonset-8fdqz???1/1?????Running???0??????????10m nvidia-driver-daemonset-ng6jn???1/1?????Running???0??????????10m #?进入容器执行nvidia-smi 检查能否执行成功。 $?kubectl?exec?-ti?nvidia-driver-daemonset-4kxr2?-n?gpu-operator-resources?--?nvidia-smi Thu?Mar?25?12:29:43?2021 ----------------------------------------------------------------------------- |?NVIDIA-SMI?450.102.04???Driver?Version:?450.102.04???CUDA?Version:?11.0?????| |------------------------------- ---------------------- ---------------------- |?GPU??Name????????Persistence-M|?Bus-Id????????Disp.A?|?Volatile?Uncorr.?ECC?| |?Fan??Temp??Perf??Pwr:Usage/Cap|?????????Memory-Usage?|?GPU-Util??Compute?M.?| |???????????????????????????????|??????????????????????|???????????????MIG?M.?| |???0??Tesla?V100-SXM2...??On???|?00000000:00:07.0?Off?|????????????????????0?| |?N/A???29C????P0????24W?/?300W?|??????0MiB?/?16160MiB?|??????0%??????Default?| |???????????????????????????????|??????????????????????|??????????????????N/A?| ------------------------------- ---------------------- ---------------------- ----------------------------------------------------------------------------- |?Processes:??????????????????????????????????????????????????????????????????| |??GPU???GI???CI????????PID???Type???Process?name??????????????????GPU?Memory?| |????????ID???ID???????????????????????????????????????????????????Usage??????| |??No?running?processes?found?????????????????????????????????????????????????| -----------------------------------------------------------------------------
3.确认节点已经打了标签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
4.修改assets/state-container-toolkit/0300_rolebinding.yaml 注释两个字段 否则无法提交
将userNames这一行和其后面的一行注释。#userNames: #-?system:serviceaccount:gpu-operator:nvidia-container-toolkit?将roleRef.namespace这一行注释。
roleRef: ??apiGroup:?rbac.authorization.k8s.io ??kind:?Role ??name:?nvidia-container-toolkit ??#namespace:?gpu-operator-resources
5.修改assets/state-container-toolkit/0400_container_toolkit.yml这个文件。对于1.6.2这个版本 直接提交这个yaml会失败 所以需要做如下修改
apiVersion:?apps/v1 kind:?DaemonSet metadata: ??labels: ????app:?nvidia-container-toolkit-daemonset ??name:?nvidia-container-toolkit-daemonset ??namespace:?gpu-operator-resources ??annotations: ????openshift.io/scc:?hostmount-anyuid spec: ??selector: ????matchLabels: ??????app:?nvidia-container-toolkit-daemonset ??template: ????metadata: ??????#?Mark?this?pod?as?a?critical?add-on;?when?enabled,?the?critical?add-on?scheduler ??????#?reserves?resources?for?critical?add-on?pods?so?that?they?can?be?rescheduled?after ??????#?a?failure.??This?annotation?works?in?tandem?with?the?toleration?below. ??????annotations: ????????scheduler.alpha.kubernetes.io/critical-pod:? ??????labels: ????????app:?nvidia-container-toolkit-daemonset ????spec: ??????tolerations: ??????#?Allow?this?pod?to?be?rescheduled?while?the?node?is?in? critical?add-ons?only ?mode. ??????#?This,?along?with?the?annotation?above?marks?this?pod?as?a?critical?add-on. ??????-?key:?CriticalAddonsOnly ????????operator:?Exists ??????-?key:?nvidia.com/gpu ????????operator:?Exists ????????effect:?NoSchedule ??????serviceAccount:?nvidia-container-toolkit ??????hostPID:?true ??????initContainers: ??????-?name:?driver-validation ????????#?替换initContainer的IMAGE ????????image:? nvcr.io/nvidia/cuda sha256:ed723a1339cddd75eb9f2be2f3476edf497a1b189c10c9bf9eb8da4a16a51a59 ????????imagePullPolicy:?IfNotPresent ????????command:?[ sh ,? -c ] ????????args:?[ export?SYS_LIBRARY_PATH $(ldconfig?-v?2 /dev/null?|?grep?-v? ^[[:space:]] ?|?cut?-d : ?-f1?|?tr? [[:space:]] ? : ?\ ????????export?NVIDIA_LIBRARY_PATH /run/nvidia/driver/usr/lib/x86_64-linux-gnu/:/run/nvidia/driver/usr/lib64;?\ ????????export?LD_LIBRARY_PATH ${SYS_LIBRARY_PATH}:${NVIDIA_LIBRARY_PATH};?echo?${LD_LIBRARY_PATH};?\ ????????export?PATH /run/nvidia/driver/usr/bin/:${PATH};?\ ????????until?nvidia-smi;?do?echo?waiting?for?nvidia?drivers?to?be?loaded;?sleep?5;?done ] ????????securityContext: ??????????privileged:?true ??????????seLinuxOptions: ????????????level:? s0 ????????volumeMounts: ??????????-?name:?nvidia-install-path ????????????mountPath:?/run/nvidia ????????????mountPropagation:?Bidirectional ??????containers: ????????#?替换container的IMAGE ??????-?image:? nvcr.io/nvidia/k8s/container-toolkit:1.4.3-ubi8 ????????args:?[ /usr/local/nvidia ] ????????env: ????????-?name:?RUNTIME_ARGS ??????????value:? ????????imagePullPolicy:?IfNotPresent ????????name:?nvidia-container-toolkit-ctr ????????securityContext: ??????????privileged:?true ??????????seLinuxOptions: ????????????level:? s0 ????????#?需要添加挂载点/etc/docker,/var/run???? ????????volumeMounts: ??????????-?name:?docker-config ????????????mountPath:?/etc/docker ??????????-?name:?docker-socket ????????????mountPath:?/var/run ??????????-?name:?nvidia-install-path ????????????mountPath:?/run/nvidia ????????????mountPropagation:?Bidirectional ??????????-?name:?nvidia-local ????????????mountPath:?/usr/local/nvidia ??????????-?name:?crio-hooks ????????????mountPath:?/usr/share/containers/oci/hooks.d ??????volumes: ????????-?name:?nvidia-install-path ??????????hostPath: ????????????path:?/run/nvidia ????????#?将节点的/etc/docker挂载到容器的/etc/docker???? ????????-?name:?docker-config ??????????hostPath: ????????????path:?/etc/docker ????????#?将节点的/var/run挂载到容器的/var/run ????????-?name:?docker-socket ??????????hostPath: ????????????path:?/var/run ????????-?name:?nvidia-local ??????????hostPath: ????????????path:?/usr/local/nvidia ????????-?name:?crio-hooks ??????????hostPath: ????????????path:?/run/containers/oci/hooks.d ??????nodeSelector: ????????nvidia.com/gpu.present:? true
6.提交应用。
$?kubectl?apply?-f?assets/state-container-toolkit
7.检查所有pod是否处于running。
$?kubectl?get?po?-n?gpu-operator-resources?-l?app nvidia-container-toolkit-daemonset NAME???????????????????????????????????????READY???STATUS????RESTARTS???AGE nvidia-container-toolkit-daemonset-6dksq???1/1?????Running???0??????????14h nvidia-container-toolkit-daemonset-q7f2l???1/1?????Running???0??????????14h nvidia-container-toolkit-daemonset-rv79v???1/1?????Running???0??????????14h
8.查看pod日志。
$?kubectl?logs?nvidia-container-toolkit-daemonset-6dksq?-n?gpu-operator-resources?--tail?10 time 2021-03-25T12:25:55Z ?level info?msg Parsing?arguments:?[/usr/local/nvidia/toolkit] time 2021-03-25T12:25:55Z ?level info?msg Successfully?parsed?arguments time 2021-03-25T12:25:55Z ?level info?msg Loading?config:?/etc/docker/daemon.json time 2021-03-25T12:25:55Z ?level info?msg Successfully?loaded?config time 2021-03-25T12:25:55Z ?level info?msg Flushing?config time 2021-03-25T12:25:55Z ?level info?msg Successfully?flushed?config time 2021-03-25T12:25:55Z ?level info?msg Sending?SIGHUP?signal?to?docker time 2021-03-25T12:25:55Z ?level info?msg Successfully?signaled?docker time 2021-03-25T12:25:55Z ?level info?msg Completed? setup ?for?docker time 2021-03-25T12:25:55Z ?level info?msg Waiting?for?signal
可以看到 nvidia-container-toolkit已经安装成功了。
缺点目前 基于容器安装NVIDIA?Container?Toolkit组件有如下的一个缺点 集群中所有节点都必须安装同一种Runtime 而且如果Runtime不是Docker 还需要特别指定。如果一个集群某些节点的Runtime为Docker 而另一些节点的Runtime为Containerd这种情况是不允许的。
总结本篇文章说明了在k8s集群中基于容器安装NVIDIA?Container?Toolkit的原理。与安装驱动不同的是 一般情况下 我们无需定制安装NVIDIA?Container?Toolkit组件的docker镜像 直接使用官方提供的即可。
人脸识别 是目前商业应用最成熟、最广泛的人工智能技术之一,成为开发者、企业接...
大数据市场如今正在呈爆炸式增长。根据调研机构Markets and Markets公司的调查,...
阿里巴巴、腾讯、支付宝、网易、IBM、谷歌、京东、 百度、滴滴等一线互联网公司...
案例背景 永安稻香小镇的体验式数字农业基地是余杭街道依托“阿里以西10分钟”的...
公司介绍 长沙营智信息技术有限公司旗下易撰网,2017年10月份上线以来,基于数据...
【51CTO.com快译】不知道您是否听说过软件架构师最讨厌意大利面这个梗?它是指软...
大家在开发Python的过程中,一定会遇到很多反斜杠的问题,很多人被反斜杠的数量...
操作场景 您可以删除不需要的私有镜像。 删除私有镜像后,将无法找回,请谨慎操...
本月DataWorks产品月刊为您带来 产品活动 1.参与阿里云DataWorks问卷调研 (Aliyu...
【51CTO.com快译】 数据分析是对数据进行判断、细化、更改和建模的过程,目的是...