随着大言语模型(LLM)年代的到来,我一向想玩玩一些开源的自托管小工具。我正在运用一台老作业站作为家庭实验室,便利的是它装置了一个老的 NVIDIA GPU。由于我正在运转一个 Kubernetes 集群,我期望将 GPU 暴露给作业负载,以便运用现有的基础设施轻松托管、调度和布置 GPU 助力的运用程序。

译自CUDA on Kubernetes。作者 Vegard S. Hagen 。

这篇文章主要是为了作为参阅材料,当我开端实际的运用程序时,期望它也能帮助其他人。

我目前在一台运转 Debian 11 的裸机单节点上运用containerd运转 Kubernetes 1.28“集群”,所以这篇文章将假定一个类似的设置,虽然我测验链接到其他设置的相关资源。

将来,当我切换到运用Proxmox或类似的虚拟化时,我或许也会更新这篇文章以新增装备。

装备

NVIDIAk8s-device-plugin的前提条件是节点上运转作业负载的 NVIDIACUDA 驱动程序和容器工具包能够正常作业。

CUDA 驱动程序

开端之前,请确保您没有任何现有的 NVIDIA 驱动程序,能够运用以下指令卸载它们:

sudo apt-get autoremove cuda* nvidia* nouveau* --purge

并重新启动核算机。

卸载图形驱动程序或许会破坏您的桌面环境。它应该会在下面的步骤中运用新的驱动程序自行修复。

在装置 GPU 驱动程序之前,咱们需求适当的内核头文件,能够经过运转以下指令获取:

sudo apt-get install linux-headers-$(uname -r)

接下来咱们添加 CUDA 驱动程序的密钥环和库房

distribution=$(. /etc/os-release;echo $ID$VERSION_ID | sed -e 's/.//g')
wget https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/cuda-keyring_1.1-1_all.deb 
sudo dpkg -i cuda-keyring_1.1-1_all.deb

这样咱们就能够轻松地运用apt-get装置驱动程序:

sudo apt-get update
sudo apt-get install cuda-drivers

重启并经过运转以下指令确保驱动程序正常作业:

nvidia-smi

然后您应该会看到有关连接的 GPU 和驱动程序版别的信息。

+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 545.23.08              Driver Version: 545.23.08    CUDA Version: 12.3     |
|-----------------------------------------+----------------------+----------------------+
| 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  NVIDIA GeForce GTX 1080        On  | 00000000:02:00.0 Off |                  N/A |
| 30%   37C    P8              12W / 180W |      1MiB /  8192MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|  No running processes found                                                           |
+---------------------------------------------------------------------------------------+

容器工具包

依照 NVIDIA 容器工具包适用于apt装置指南,咱们首要装备容器工具包软件库房

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | 
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg  && 
  curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | 
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | 
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

在装置nvidia-container-toolkit之前

sudo apt-get update
sudo apt-get install nvidia-container-toolkit

containerd 运转时环境

备份现有的containerd装备,以防接下来的步骤出错

sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.bak

然后,咱们能够根据k8s-device-plugin 自述文件手动装备containerd,或许运转

sudo nvidia-ctk runtime configure --runtime=containerd

来将nvidia-container-runtime设置为containerd的默许底层运转时环境。

NVIDIA 设备插件

装置作业的 CUDA 驱动程序、设置 NVIDIA 容器工具包和将 containerd 装备为运用 NVIDIA 运转时环境,咱们现在能够运用其 Helm chart 来运用 NVIDIA 设备插件。

helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
helm repo update
helm upgrade -i nvdp nvdp/nvidia-device-plugin 
  --namespace nvidia-device-plugin 
  --include-crds 
  --create-namespace 
  --version 0.14.3

时刻切片(可选)

NVIDIA 设备插件的默许行为是将整个 GPU 分配给单个 pod,这意味着假如有多个 pod 恳求 GPU 时刻,每次只会调度一个 pod。

为了战胜这个问题,咱们能够装备 GPU 的时刻切片,即 GPU 在 pod 之间共享。

首要创立一个ConfigMap,装备最大 10 个副本(第 14 行)来装备时刻切片。

# cm-time-slicing.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-time-slicing
  namespace: nvidia-device-plugin
data:
  time-slicing: |-
    version: v1
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 10

然后运用ConfigMap,并经过称号(第 5 行)和提供的默许装备键(第 8 行)装备nvidia-device-plugin运用它。

kubectl apply -f cm-time-slicing.yaml
helm upgrade nvdp nvdp/nvidia-device-plugin 
  --reuse-values 
  --set config.name=cm-time-slicing 
  --set config.default=time-slicing

现在您应该经过运转下面的指令看到每个节点每个 GPU 有 10 个nvidia.com/gpu的容量:

kubectl get node -o 'jsonpath={.items[*].status.capacity}' | jq
{
  ...
  "nvidia.com/gpu": "10",
  ...
}

请注意,作业负载从同一 GPU 获取副本,每个作业负载都能够拜访相同的 GPU 内存,并在同一故障域中运转,这意味着假如一个作业负载溃散,它们都会溃散。

有关装备设备插件的更多详细信息,请参阅GitHub 上的自述文件

运转作业负载

假定装备都正常,咱们现在能够测验运转一个测验作业负载,经过启动一个恳求 GPU 资源的 pod 来运用 GPU(第 11-13 行)。

apiVersion: v1
kind: Pod
metadata:
  name: cuda-vectoradd
  namespace: cuda-test
spec:
  restartPolicy: OnFailure
  containers:
  - name: cuda-vectoradd
    image: "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1-ubuntu20.04"
    resources:
      limits:
        nvidia.com/gpu: "1"
kubectl create ns cuda-test
kubectl apply -f cuda-vectoradd.yaml

假如一切顺利,作业负载的日志应该显现:

kubectl logs -n cuda-test cuda-vectoradd
[Vector addition of 50000 elements]
... Test PASSED

假如一切正常,只需在每个您想要拜访 GPU 资源的作业负载上添加nvidia.com/gpu的资源约束即可。

resources:
  limits:
    nvidia.com/gpu: "1"

查看恳求 GPU 资源的 pod 内部,咱们也会发现两个与 NVIDIA 相关的环境变量:

kubectl exec -it <pod> -- env | grep NVIDIA
NVIDIA_DRIVER_CAPABILITIES=compute,video,utility
NVIDIA_VISIBLE_DEVICES=GPU-<UUID>

这表明咱们在 pod 中有可用的 GPU 加速核算和视频编码/解码

故障扫除

假如您遇到类似的 pod 启动过错:

0/1 nodes are available: 1 Insufficient nvidia.com/gpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..

或许是您没有足够的 GPU 资源,请测验从“时刻切片”部分增加时刻切片副本数量,或许购买另一个 GPU,不管对您更合算。

我也遇到过这样的过错,即在重新启动节点后,多个长时刻运转的作业负载企图启动时发生过错。重新启动nvidia-device-pluginpod 和恳求 GPU 资源的作业负载似乎能够处理该问题。

运用 Argo CD,我添加了一个负的sync-wave注解,以确保在作业负载之前启动nvidia-device-plugin以防止此问题。

annotations:
  argocd.argoproj.io/sync-wave: "-1"

附录

我首要测验运用NVIDIA GPU Operator,我以为这是一个万能的处理方案,它能够装置设备插件以及驱动程序和容器工具包。但是,我无法让它作业,所以我挑选了不幸更多的手动办法,将设备插件、驱动程序和容器工具包作为独自的组件进行装置。

或许是我的设置问题,或许我在文档中了解错了什么。假如您有处理方案,我很乐意倾听!

总结

我正在运用Argo CD 与 Kustomize + Helm测验遵从 GitOps 最佳实践。 在撰写本文时,我的完整家庭实验室装备可在GitHub 上作为参阅

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonAnnotations:
  argocd.argoproj.io/sync-wave: "-1"
resources:
  - namespace.yaml
  - cm-time-slicing.yaml
helmCharts:
  - name: nvidia-device-plugin
    repo: https://nvidia.github.io/k8s-device-plugin
    version: 0.14.2
    releaseName: "nvidia-device-plugin"
    namespace: nvidia-device-plugin
    includeCRDs: true
    valuesFile: values.yaml
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: nvidia-device-plugin
# values.yaml
config:
  name: cm-time-slicing
  default: time-slicing
# cm-time-slicing.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-time-slicing
  namespace: nvidia-device-plugin
data:
  time-slicing: |-
    version: v1
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 10

本文在云云众生yylives.cc/)首发,欢迎我们拜访。