背景
Kubernetes 1.26开始引入DRA( Dynamic Resource Allocation,动态资源分配),用于解决Kubernetes现有Device Plugin机制的不足。相比于现有的Device Plugin机制,DRA更加开放和自主,能够满足一些复杂的使用场景。
NVIDIA、Intel这些设备厂商也基于DRA开放自己下一代Device Plugin,以期满足更复杂的业务场景。
NVIDIA Resource Driver介绍
NVIDIA Resource Driver是基于K8s DRA机制实现,项目地址为:https://gitlab.com/nvidia/cloud-native/k8s-dra-driver。目前支持如下的一些功能:
-
支持独占GPU调度:一个Pod中容器独占使用一张或多张GPU卡。
-
支持共享GPU调度:多个Pod运行在一张GPU卡上。
-
支持MPS:允许Pod使用GPU MPS能力。
-
支持Time-Slicing:允许Pod使用GPU Time-Slicing能力。
目前该项目还不是一个生产可用的状态,所以不建议将该组件部署在生产环境中,仅作为学习和测试环境使用。
前提条件
在体验NVIDIA Resource Driver之前,有如下的一些条件需要满足:
-
K8S版本>= 1.27.0,目前NVIDIA Resource Driver使用DRA API版本为v1alpha2,这个API版本当前仅在1.27中才支持。
-
Container >= 1.7.0,DRA机制需要Containerd CDI功能参与,目前CDI能力仅在Containerd 1.7.0及其以上才支持。
-
实验环境:一台带GPU设备的物理机(或者虚拟机、云上机器都行),已安装操作系统,本次实验使用的操作系统为CentOS 7.9。
步骤
为了快速体验NVIDIA Resource Driver,本篇文章将借助minikube快速搭建一个v1.27.3的K8s集群,然后在集群中部署NVIDIA Resource Driver。
安装NVIDIA驱动
安装驱动依赖包
在节点上使用如下命令安装驱动依赖包。
yum install -y gcc dracut kernel-devel-$(uname -r)
禁用nouveau。
mkdir -pv /etc/modprobe.d/
echo 'blacklist nouveau' >/etc/modprobe.d/blacklist-nouveau.conf
echo 'options nouveau modeset=0' >>/etc/modprobe.d/blacklist-nouveau.conf
安装NVIDIA驱动
在NVIDIA官方界面下面驱动,这里以515.105.01为例,得到的驱动文件如下。
NVIDIA-Linux-x86_64-515.105.01.run
安装驱动文件。
sh NVIDIA-Linux-x86_64-515.105.01.run -a -s -q
安装完成后执行nvidia-smi。
nvidia-smi
输出如下。
Mon Jul 10 12:06:09 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 515.105.01 Driver Version: 515.105.01 CUDA Version: 11.7 |
|-------------------------------+----------------------+----------------------+
| 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 34C P0 40W / 300W | 0MiB / 32768MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
| 1 Tesla V100-SXM2... On | 00000000:00:08.0 Off | 0 |
| N/A 35C P0 40W / 300W | 0MiB / 32768MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
| 2 Tesla V100-SXM2... On | 00000000:00:09.0 Off | 0 |
| N/A 35C P0 42W / 300W | 0MiB / 32768MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
| 3 Tesla V100-SXM2... On | 00000000:00:0A.0 Off | 0 |
| N/A 33C P0 39W / 300W | 0MiB / 32768MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
可以看到,本次示例的节点有4张GPU卡。
目前NVIDIA Resource Driver要求NVIDIA驱动的安装路径为/run/nvidia/driver(当前使用CDI机制生成NVIDIA CDI设备的限制,后续NVIDIA将解除这个限制),所以将节点的根目录挂载到/run/nvidia/driver。
mkdir -p /run/nvidia
ln -sv / /run/nvidia/driver
准备Container Runtime环境
在利用minikube构建集群之前,需要配置Container Runtime环境。
安装runc
登录实验环境,下载runc二进制文件。
wget https://github.com/opencontainers/runc/releases/download/v1.1.7/runc.amd64
将runc二进制文件存放到/usr/bin。
cp runc.amd64 /usr/bin/runc
添加执行权限。
chmod +x /usr/bin/runc
安装cni
登录实验环境,下载cni组件安装包。
wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
解压并安装cni。
mkdir -pv /opt/cni/bin
tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.3.0.tgz
安装crictl
登录实验环境,下载crictl。
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.27.0/crictl-v1.27.0-linux-amd64.tar.gz
解压并安装到/usr/bin。
tar -xf crictl-v1.27.0-linux-amd64.tar.gz
cp crictl /usr/bin
添加配置文件。
echo 'runtime-endpoint: unix:///var/run/containerd/containerd.sock' > /etc/crictl.yaml
安装docker-cli
安装docker-cli的这一步主要是让minikube能够正常创建集群,没有安装docker-cli,minikube创建集群会报错。
安装yum工具。
yum install -y yum-utils
配置docker yum源。
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安装docker client。
yum install -y docker-ce-cli
安装containerd
下载containerd安装包。
wget https://github.com/containerd/containerd/releases/download/v1.7.2/containerd-1.7.2-linux-amd64.tar.gz
解压containerd安装包,并安装到/usr/bin。
tar -xf containerd-1.7.2-linux-amd64.tar.gz
cp bin/containerd /usr/bin
创建配置文件。
containerd config default | sudo tee /etc/containerd/config.toml
更改配置文件某些配置。
sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml
在配置文件中添加CDI配置。
[plugins."io.containerd.grpc.v1.cri"]
enable_cdi = true
cdi_spec_dirs = ["/etc/cdi", "/var/run/cdi"]
创建cdi相关目录。
mkdir -pv /etc/cdi /var/run/cdi
下载systemd风格启动脚本。
curl -L https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -o /etc/systemd/system/containerd.service
启动和开机自启containerd。
systemctl daemon-reload
systemctl enable --now containerd
查看containerd状态。
systemctl status containerd
类似有如下的输出。
● containerd.service - containerd container runtime
Loaded: loaded (/etc/systemd/system/containerd.service; enabled; vendor preset: disabled)
Active: active (running) since 三 2023-07-05 20:00:29 CST; 4 days ago
Docs: https://containerd.io
Main PID: 1763365 (containerd)
Tasks: 154
Memory: 144.8M
CGroup: /system.slice/containerd.service
├─1763365 /usr/bin/containerd
安装minikube
下载minikube。
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
安装到/usr/local/bin。
cp minikube-linux-amd64 /usr/local/bin/minikube
添加执行权限。
chmod +x /usr/local/bin/minikube
安装kubectl
下载kubectl。
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
安装到/usr/local/bin。
cp kubectl /usr/local/bin/kubectl
添加执行权限。
chmod +x /usr/local/bin/kubectl
安装helm
安装NVIDIA Resource Driver需要使用helm。
首先下载helm。
curl -LO https://get.helm.sh/helm-v3.12.1-linux-amd64.tar.gz
解压。
tar -xf helm-v3.12.1-linux-amd64.tar.gz
安装到/usr/local/bin。
cp linux-amd64/helm /usr/local/bin
创建k8s集群
使用如下命令创建k8s集群。
minikube start \
--force \
--driver=none \
--force-systemd=true \
--container-runtime=containerd \
--cni=flannel \
--kubernetes-version=v1.27.3 \
--extra-config=apiserver.runtime-config=resource.k8s.io/v1alpha2 \
--feature-gates=DynamicResourceAllocation=true
集群创建成功后,查看各个组件状态是否都处于running。
$ kubectl get po -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5d78c9869d-svbhf 1/1 Running 0 4d14h
etcd-izj6ce3r1zhvbsrk3ww4cpz 1/1 Running 3 4d14h
kube-apiserver-izj6ce3r1zhvbsrk3ww4cpz 1/1 Running 3 4d14h
kube-controller-manager-izj6ce3r1zhvbsrk3ww4cpz 1/1 Running 3 4d14h
kube-proxy-rgb4c 1/1 Running 0 4d14h
kube-scheduler-izj6ce3r1zhvbsrk3ww4cpz 1/1 Running 0 4d14h
storage-provisioner 1/1 Running 0 4d14h
查看集群节点。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
izj6ce3r1zhvbsrk3ww4cpz Ready control-plane 4d15h v1.27.3
安装NVIDIA Resource Driver
下载NVIDIA Resource Driver项目。
git clone https://gitlab.com/nvidia/cloud-native/k8s-dra-driver.git
修改deployments/helm/k8s-dra-driver/templates/kubeletplugin.yaml中环境变量LD_LIBRARY_PATH的值(/run/nvidia/driver/usr/lib/x86_64-linux-gnu这个路径存在问题),修改前的效果如下。
- name: LD_LIBRARY_PATH
value: /usr/lib64/:/run/nvidia/driver/usr/lib/x86_64-linux-gnu
修改后的效果如下。
- name: LD_LIBRARY_PATH
value: /usr/lib64/:/run/nvidia/driver/usr/lib:/run/nvidia/driver/usr/lib64
安装nvidia resource driver。
$ helm install --create-namespace --namespace nvidia-dra-driver nvidia-dra deployments/helm/k8s-dra-driver
使用minikube搭建的集群只有一个节点,那就是当前操作的节点,给这个节点打上标签。
$ kubectl label nodes <NODE_NAME> nvidia.com/dra.kubelet-plugin=true
$ kubectl label nodes <NODE_NAME> nvidia.com/dra.controller=true
等待dra组件处于running。
$ kubectl get po -n nvidia-dra-driver
NAME READY STATUS RESTARTS AGE
nvidia-k8s-dra-driver-controller-7948496bcb-2ff68 1/1 Running 0 34m
nvidia-k8s-dra-driver-kubelet-plugin-z6lp5 1/1 Running 0 10m
体验NVIDIA Resource Driver
NVIDIA Resource Driver项目的demo目录下有一些示例yaml,这里选择以gpu-test2.yaml为例演示。这个例子演示了如何让一个pod两个容器共同使用1张GPU卡。该yaml文件内容如下:
# One pod, two containers
# Each asking for shared access to a single GPU
---
apiVersion: v1
kind: Namespace
metadata:
name: gpu-test2
---
apiVersion: resource.k8s.io/v1alpha2
kind: ResourceClaimTemplate
metadata:
namespace: gpu-test2
name: gpu.nvidia.com
spec:
spec:
resourceClassName: gpu.nvidia.com
---
apiVersion: v1
kind: Pod
metadata:
namespace: gpu-test2
name: pod
spec:
containers:
- name: ctr0
image: ubuntu:22.04
command: ["bash", "-c"]
args: ["nvidia-smi -L; sleep 9999"]
resources:
claims:
- name: shared-gpu
- name: ctr1
image: ubuntu:22.04
command: ["bash", "-c"]
args: ["nvidia-smi -L; sleep 9999"]
resources:
claims:
- name: shared-gpu
resourceClaims:
- name: shared-gpu
source:
resourceClaimTemplateName: gpu.nvidia.com
使用kubectl提交Pod。
kubectl apply -f demo/gpu-test2.yaml
查看Pod是否处于Running。
$ kubectl get po -n gpu-test2
NAME READY STATUS RESTARTS AGE
pod 2/2 Running 0 109m
查看pod两个容器使用的GPU卡是否为同一张卡。进入第一个容器执行nvidia-smi。
kubectl exec -ti pod -n gpu-test2 -c ctr0 -- nvidia-smi -L
GPU 0: Tesla V100-SXM2-32GB (UUID: GPU-16d07e34-6b1e-0d26-73ef-3635fd7ed60e)
进入第二个容器执行nvidia-smi。
kubectl exec -ti pod -n gpu-test2 -c ctr1 -- nvidia-smi -L
GPU 0: Tesla V100-SXM2-32GB (UUID: GPU-16d07e34-6b1e-0d26-73ef-3635fd7ed60e)
可以看到两个容器使用的GPU是同一张卡。
至于NVIDIA Resource Driver的其他功能的体验,可以参考demo目录的示例yaml文件。