当前位置:主页 > 查看内容

NVIDIA GPU Operator分析二:NVIDIA Container Toolkit安装

发布时间:2021-05-19 00:00| 位朋友查看

简介:背景 我们知道 如果在Kubernetes中支持GPU设备调度 需要做如下的工作 节点上安装nvidia驱动节点上安装nvidia-docker集群部署gpu?device?plugin 用于为调度到该节点的pod分配GPU设备。 除此之外 如果你需要监控集群GPU资源使用情况 你可能还需要安装 DCCM?exp……
背景

我们知道 如果在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镜像 直接使用官方提供的即可。


本文转自网络,原文链接:https://developer.aliyun.com/article/784150
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文


随机推荐