K8S项目实践(09): 使用 GPU Operator搭建AI算力环境
1. 引言
为了学习AI应用、算法与算力等技术,应用需跑在GPU卡上,需要在节点上安装 GPU Driver、Container Toolkit 等组件,当集群规模较大时还是比较麻烦的。
为了解决这个问题,NVIDIA 推出了 GPU Operator,GPU Operator 旨在简化在 Kubernetes 环境中使用 GPU 的过程,通过自动化的方式处理 GPU 驱动程序安装、Controller Toolkit、Device-Plugin 、监控等组件。
基本上把需要手动安装、配置的地方全部自动化处理了,极大简化了 k8s 环境中的 GPU 使用。
ps:只有 NVIDIA GPU 可以使用,其他厂家现在基本还是手动安装。
2. 规划
本文主要分享如何使用 GPU Operator 快速搭建 Kubernetes GPU 环境。
基于如下环境搭建:
查看worker节点GPU信息:
[root@k8s-worker1 ~]# lspci | grep -i nvidia
00:07.0 VGA compatible controller: NVIDIA Corporation TU104GL [Tesla T4] (rev a1)
[root@k8s-worker1 ~]#
说明该worker节点有1张NVIDIA Tesla T4的GPU卡。
3. 组件介绍
这部分主要分析下 GPU Operator 涉及到的各个组件及其作用。
NVIDIA GPU Operator总共包含如下的几个组件:
NFD(Node Feature Discovery):用于给节点打上某些标签,这些标签包括 cpu id、内核版本、操作系统版本、是不是 GPU 节点等,其中需要关注的标签是nvidia.com/gpu.present=true,如果节点存在该标签,那么说明该节点是 GPU 节点。
GFD(GPU Feature Discovery):用于收集节点的 GPU 设备属性(GPU 驱动版本、GPU型号等),并将这些属性以节点标签的方式透出。在k8s 集群中以 DaemonSet 方式部署,只有节点拥有标签nvidia.com/gpu.present=true时,DaemonSet 控制的 Pod 才会在该节点上运行。
新版本 GFD 迁移到了 NVIDIA/k8s-device-plugin
NVIDIA Driver Installer:基于容器的方式在节点上安装 NVIDIA GPU 驱动,在 k8s 集群中以 DaemonSet 方式部署,只有节点拥有标签nvidia.com/gpu.present=true时,DaemonSet 控制的 Pod 才会在该节点上运行。
NVIDIA Container Toolkit Installer:能够实现在容器中使用 GPU 设备,在 k8s 集群中以 DaemonSet 方式部署,同样的,只有节点拥有标签nvidia.com/gpu.present=true时,DaemonSet 控制的 Pod 才会在该节点上运行。
NVIDIA Device Plugin:NVIDIA Device Plugin 用于实现将 GPU 设备以 Kubernetes 扩展资源的方式供用户使用,在 k8s 集群中以 DaemonSet 方式部署,只有节点拥有标签nvidia.com/gpu.present=true时,DaemonSet 控制的 Pod 才会在该节点上运行。
DCGM Exporter:周期性的收集节点 GPU 设备的状态(当前温度、总的显存、已使用显存、使用率等)并暴露 Metrics,结合 Prometheus 和 Grafana 使用。在 k8s 集群中以DaemonSet 方式部署,只有节点拥有标签nvidia.com/gpu.present=true时,DaemonSet 控制的 Pod 才会在该节点上运行。
首先是 GFD、NFD,二者都是用于发现 Node 上的信息,并以 label 形式添加到 k8s node 对象上,特别是 GFD 会添加nvidia.com/gpu.present=true 标签表示该节点有 GPU,只有携带该标签的节点才会安装后续组件。
然后则是 Driver Installer、Container Toolkit Installer 用于安装 GPU 驱动和 container toolkit。
接下来则是 device-plugin 让 k8s 能感知到 GPU 资源信息便于调度和管理。
最后的 exporter 则是采集 GPU 监控并以 Prometheus Metrics 格式暴露,用于做 GPU 监控。
这些组件基本就把需要手动配置的东西都自动化了。
NVIDIA GPU Operator 依如下的顺序部署各个组件,并且如果前一个组件部署失败,那么其后面的组件将停止部署:
- NVIDIA Driver Installer
- NVIDIA Container Toolkit Installer
- NVIDIA Device Plugin
- DCGM Exporter
- GFD
每个组件都是以 DaemonSet 方式部署,并且只有当节点存在标签 nvidia.com/gpu.present=true 时,各 DaemonSet控制的 Pod 才会在节点上运行。
nvidia.com/gpu.deploy.driver=pre-installed
3.1. GFD & NFD
GFD:GPU Feature Discovery
NFD:Node Feature Discovery
根据名称基本能猜到这两个组件的功能,发现节点信息和 GPU 信息并以 Label 形式添加到 k8s 中的 node 对象上。
其中 NFD 添加的 label 以 feature.node.kubernetes.io 作为前缀,比如:
feature.node.kubernetes.io/cpu-cpuid.ADX=true
feature.node.kubernetes.io/system-os_release.ID=ubuntu
feature.node.kubernetes.io/system-os_release.VERSION_ID.major=22
feature.node.kubernetes.io/system-os_release.VERSION_ID.minor=04
feature.node.kubernetes.io/system-os_release.VERSION_ID=22.04
对于 GFD 则主要记录 GPU 信息
nvidia.com/cuda.runtime.major=12
nvidia.com/cuda.runtime.minor=2
nvidia.com/cuda.driver.major=535
nvidia.com/cuda.driver.minor=161
nvidia.com/gpu.product=Tesla-T4
nvidia.com/gpu.memory=15360
3.2. Driver Installer
NVIDIA 官方提供了一种基于容器安装 NVIDIA 驱动的方式,GPU Operator 安装 nvidia 驱动也是采用的这种方式。
当 NVIDIA 驱动基于容器化安装后,整个架构将演变成图中描述的样子:
Driver Installer 组件对应的 DaemonSet 就是nvidia-driver-daemonset-5.15.0-105-generic-ubuntu22.04。
该 DaemonSet 对应的镜像为
root@test:~# kgo get ds nvidia-driver-daemonset-5.15.0-105-generic-ubuntu22.04 -oyaml|grep image
image: nvcr.io/nvidia/driver:535-5.15.0-105-generic-ubuntu22.04
其中 DaemonSet 名称/镜像由几部分组件:
- nvidia-driver-daemonset 这部分为前缀
- 5.15.0-105-generic 为内核版本,使用uname -r 命令查看
- ubuntu22.04 操作系统版本,使用cat /etc/os-release 命令查看
- 535:这个是 GPU Driver 的版本号,这里表示安装 535 版本驱动,在部署时可以指定。
GPU Operator 会自动根据节点上的内核版本和操作系统生成 DaemonSet 镜像,因为是以 DaemonSet 方式运行的,所有节点上都是跑的同一个 Pod,因此要限制集群中的所有 GPU 节点操作系统和内核版本必须一致。
ps:如果提前手动在节点上安装 GPU 驱动,那么 GPU Operator 检测到之后就不会在该节点上启动 Installer Pod,这样该节点就可以不需要管操作系统和内核版本。
3.3. NVIDIA Container Toolkit Installer
该组件用于安装 NVIDIA Container Toolkit。
手动安装的时候有两个步骤:
1)安装 NVIDIA Container Toolkit
2)修改 Runtime 配置指定使用 nvidia-runtime
在整个调用链中新增 nvidia-container-runtime,以便处理 GPU 相关操作。
这个 Installer 做的操作也就是这两步:
1)将容器中NVIDIA Container Toolkit组件所涉及的命令行工具和库文件移动到/usr/local/nvidia/toolkit目录下
2)在 /usr/local/nvidia/toolkit/.config/nvidia-container-runtime创建nvidia-container-runtime的配置文件config.toml,并设置nvidia-container-cli.root的值为/run/nvidia/driver。
4. 部署
参考官方文档: operator-install-guide
4.1. 准备工作
要求:
1)GPU 节点必须运行相同的操作系统,
- 如果提前手动在节点上安装驱动的话,该节点可以使用不同的操作系统
- CPU 节点操作系统没要求,因为 gpu-operator 只会在 GPU 节点上运行 2)GPU 节点必须配置相同容器引擎,例如都是 containerd 或者都是 docker
3)如果使用了 Pod Security Admission (PSA) ,需要为 gpu-operator 标记特权模式
[root@k8s-master1 ~]# kubectl create ns gpu-operator
namespace/gpu-operator created
[root@k8s-master1 ~]# kubectl label --overwrite ns gpu-operator pod-security.kubernetes.io/enforce=privileged
namespace/gpu-operator labeled
[root@k8s-master1 ~]# kubectl get ns gpu-operator --show-labels
NAME STATUS AGE LABELS
gpu-operator Active 30s kubernetes.io/metadata.name=gpu-operator,pod-security.kubernetes.io/enforce=privileged
[root@k8s-master1 ~]#
4)集群中不要安装 NFD,如果已经安装了需要再安装 gpu-operator 时禁用 NFD 部署。
使用以下命令查看集群中是否部署 NFD
kubectl get nodes -o json | jq '.items[].metadata.labels | keys | any(startswith("feature.node.kubernetes.io"))'
如果返回 true 则说明集群中安装了 NFD。
4.2. helm部署
参考官方文档: operator-install-guide
4.2.1. 安装helm命令
如果master节点上还未安装helm命令,需安装
4.2.1.1. 下载 Helm 3 的最新版本
curl https://get.helm.sh/helm-v3.11.1-linux-amd64.tar.gz -o helm-v3.11.1-linux-amd64.tar.gz
4.2.1.2. 解压安装包
[root@k8s-master1 helm]# ls
helm-v3.11.1-linux-amd64.tar.gz
[root@k8s-master1 helm]# tar -zxvf helm-v3.11.1-linux-amd64.tar.gz
linux-amd64/
linux-amd64/helm
linux-amd64/LICENSE
linux-amd64/README.md
[root@k8s-master1 helm]#
4.2.1.3. 移动 helm 到系统的可执行路径
sudo mv linux-amd64/helm /usr/local/bin/helm`
4.2.1.4. 验证安装
[root@k8s-master1 helm]# helm version
version.BuildInfo{Version:"v3.11.1", GitCommit:"293b50c65d4d56187cd4e2f390f0ada46b4c4737", GitTreeState:"clean", GoVersion:"go1.18.10"}
[root@k8s-master1 helm]#
说明安装成功。
4.2.2. chart部署
4.2.2.1. 添加repo仓库
添加 nvidia helm 仓库并更新
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia \
&& helm repo update
4.2.2.2. 拉取chart包
helm pull nvidia/gpu-operator --version v24.9.1
将在本地生成gpu-operator-v24.9.1.tgz文件。
4.2.2.3. 准备镜像
所有节点上执行
由于有些镜像国内不能直接访问,需从国内镜像地址拉取后再修改tag。 1)拉取国内代理镜像:
crictl pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/nfd/node-feature-discovery:v0.16.6
2)修改镜像tag:
ctr -n k8s.io image tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/nfd/node-feature-discovery:v0.16.6 registry.k8s.io/nfd/node-feature-discovery:v0.16.6
4.2.2.4. 安装chart包
# 添加 nvidia helm 仓库并更新
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia \
&& helm repo update
# 以默认配置安装
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator
# 如果提前手动安装了 gpu 驱动,operator 中要禁止 gpu 安装
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--set driver.enabled=false
完成后 会启动 Pod 安装驱动,如果节点上已经安装了驱动了,那么 gpu-operaotr 就不会启动安装驱动的 Pod,通过 label 进行筛选。
- 没安装驱动的节点会打上 nvidia.com/gpu.deploy.driver=true ,表示需要安装驱动
- 已经手动安装过驱动的节点会打上nvidia.com/gpu.deploy.driver=pre-install,Daemonset 则不会在该节点上运行
当然,并不是每个操作系统+内核版本的组合,NVIDIA 都提供了对应的镜像,可以提前在 NVIDIA/driver tags 查看当前 NVIDIA 提供的驱动版本。
5. 测试
部署后,会在gpu-operator namespace 下启动相关 Pod,查看一下 Pod 的运行情况,除了一个 Completed 之外其他应该都是 Running 状态。
然后进入nvidia-device-plugin-daemonset-xxx Pod,在该 Pod 中可以执行 nvidia-smi命令,比如查看 GPU 信息:
最后再查看 node 信息
可以看出nvidia.com/gpu: “1”总量为1, 可分配也为1。
至此,说明我们的 GPU Operator 已经安装成功,K8s 也能感知到节点上的 GPU,接下来就可以在 Pod 中使用 GPU 了。
创建一个测试 Pod,申请一个 GPU:
apiVersion: v1
kind: Pod
metadata:
name: cuda-vectoradd
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vectoradd
image: "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2"
resources:
limits:
nvidia.com/gpu: 1
正常的 Pod 日志如下:
[root@k8s-master1 yaml]# kubectl logs cuda-vectoradd
[Vector addition of 50000 elements]
Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done
至此,我们已经可以在 k8s 中使用 GPU 了。
6. 原理
这部分主要分析一下 Driver Installer 和 NVIDIA Container Toolkit Installer 这两个组件是怎么实现的,大致原理。
6.1. Driver Installer
NVIDIA 官方提供了一种基于容器安装 NVIDIA 驱动的方式,GPU Operator 安装 nvidia 驱动也是采用的这种方式。
当 NVIDIA 驱动基于容器化安装后,整个架构将演变成图中描述的样子:
6.2. NVIDIA Container Toolkit Installer
该组件用于安装 NVIDIA Container Toolkit。
手动安装的时候有两个步骤:
1)安装 NVIDIA Container Toolkit
2)修改 Runtime 配置指定使用 nvidia-runtime
在整个调用链中新增 nvidia-container-runtime,以便处理 GPU 相关操作。
这个 Installer 做的操作也就是这两步:
1)将容器中NVIDIA Container Toolkit组件所涉及的命令行工具和库文件移动到/usr/local/nvidia/toolkit目录下
2)在 /usr/local/nvidia/toolkit/.config/nvidia-container-runtime创建nvidia-container-runtime的配置文件config.toml,并设置nvidia-container-cli.root的值为/run/nvidia/driver。
7. 小结
小结 本文主要分享如何使用 GPU Operator 自动化完成 GPU Driver、NVIDIA Container Toolkit、device-plugin、exporter 等组件的部署,快速实现在 k8s 环境中使用 GPU。
最后简单分析了 Driver Installer 和 NVIDIA Container Toolkit Installer 这两个组件的工作原理。
GPU Operator 极大简化了在 k8s 中使用 GPU 的繁琐过程,但是也存在一些缺点:
- Driver Installer 以 DaemonSet 方式运行的,每个节点上运行的 Pod 都一样,但是镜像由 驱动版本+内核版本+操作系统版本拼接而成,因此需要集群中所有节点操作系统一致。
- NVIDIA Container Toolkit Installer 同样是以 DaemonSet 方式运行的,另外安装时需要指定 Runtime,这也造成了集群的节点必须安装相同的 Container Runtime。
8. 参考资料
【01】GPU 环境搭建指南:使用 GPU Operator 加速 Kubernetes GPU 环境搭建


作者:mospan
微信关注:墨斯潘園
本文出处:http://mospany.github.io/2024/01/17/gpu-operator/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。