GPU Operator 与 KubeVirt#

简介#

KubeVirt 是 Kubernetes 的虚拟机管理附加组件,允许您在 Kubernetes 集群中运行和管理虚拟机。它消除了为虚拟机和容器工作负载管理单独集群的需求,因为两者现在可以在单个 Kubernetes 集群中共存。

到目前为止,GPU Operator 仅为运行 GPU 加速容器配置工作节点。现在,GPU Operator 也可用于为运行 GPU 加速虚拟机配置工作节点。

运行带有 GPU 的容器和虚拟机所需的先决条件不同,主要区别在于所需的驱动程序。例如,容器需要数据中心驱动程序,GPU 直通需要 vfio-pci 驱动程序,而创建 vGPU 设备需要NVIDIA vGPU Manager

现在可以配置 GPU Operator,以便根据配置在这些节点上运行的 GPU 工作负载,在工作节点上部署不同的软件组件。考虑以下示例。

节点 A 配置为运行容器。
节点 B 配置为运行带有直通 GPU 的虚拟机。
节点 C 配置为运行带有 vGPU 的虚拟机。

节点 A 接收以下软件组件

  • NVIDIA Datacenter Driver - 用于安装驱动程序

  • NVIDIA Container Toolkit - 用于确保容器可以正确访问 GPU

  • NVIDIA Kubernetes Device Plugin - 用于发现和向 kubelet 通告 GPU 资源

  • NVIDIA DCGM DCGM Exporter - 用于监控 GPU

节点 B 接收以下软件组件

  • VFIO Manager - 用于加载 vfio-pci 并将其绑定到节点上的所有 GPU

  • Sandbox Device Plugin - 用于发现和向 kubelet 通告直通 GPU

节点 C 接收以下软件组件

  • NVIDIA vGPU Manager - 用于安装驱动程序

  • NVIDIA vGPU Device Manager - 用于在节点上创建 vGPU 设备

  • Sandbox Device Plugin - 用于发现和向 kubelet 通告 vGPU 设备

假设、约束和依赖项#

  • 一个 GPU 工作节点可以运行特定类型的 GPU 工作负载 - 容器、带有 GPU 直通的虚拟机或带有 vGPU 的虚拟机 - 但不能运行它们的任何组合。

  • 集群管理员或开发人员预先了解其集群,并且可以正确标记节点以指示它们将运行的 GPU 工作负载类型。

  • 运行 GPU 加速虚拟机(带有 pGPU 或 vGPU)的工作节点假定为裸机。

  • GPU Operator 不会自动在附加了 GPU/vGPU 的 KubeVirt 虚拟机内部安装 NVIDIA 驱动程序。

  • 用户必须手动将所有直通 GPU 和 vGPU 资源添加到 KubeVirt CR 中的 permittedDevices 列表,然后才能将它们分配给 KubeVirt 虚拟机。有关更多信息,请参阅KubeVirt 文档

  • 不支持 MIG 支持的 vGPU。

先决条件#

  • 虚拟化和 IOMMU 扩展(Intel VT-d 或 AMD IOMMU)在 BIOS 中启用。

  • 主机使用内核命令行上的 intel_iommu=onamd_iommu=on 启动。

  • 如果计划使用 NVIDIA vGPU,如果您的 GPU 基于 NVIDIA Ampere 架构或更高版本,则必须在 BIOS 中启用 SR-IOV。请参阅NVIDIA vGPU 文档,以确保您已满足使用 NVIDIA vGPU 的所有先决条件。

  • KubeVirt 已安装在集群中。

  • 从 KubeVirt v0.58.2 和 v0.59.1 开始,设置 DisableMDEVConfiguration 功能门

    $ kubectl patch kubevirt -n kubevirt kubevirt  --type='json' \
        -p='[{"op": "add", "path": "/spec/configuration/developerConfiguration/featureGates/-", "value": "DisableMDEVConfiguration" }]'
    

    示例输出

    kubevirt.kubevirt.io/kubevirt patched
    

快速入门#

将 GPU Operator 与 KubeVirt 结合使用的高级工作流程如下

  1. 确保禁用中介设备配置功能门已设置。

  2. 根据工作节点将运行的 GPU 工作负载标记工作节点。

  3. 安装 GPU Operator 并设置 sandboxWorkloads.enabled=true

如果使用 NVIDIA vGPU,则需要其他步骤,将在后续章节中介绍

标记工作节点#

使用以下命令向工作节点添加标签

$ kubectl label node <node-name> --overwrite nvidia.com/gpu.workload.config=vm-vgpu

您可以为标签分配以下值 - containervm-passthroughvm-vgpu。GPU Operator 在确定要在每个工作节点上部署哪些操作数时,会使用此标签的值。

如果节点标签 nvidia.com/gpu.workload.config 在节点上不存在,则 GPU Operator 将假定默认 GPU 工作负载配置 container,并将部署支持此工作负载类型所需的软件组件。要覆盖默认 GPU 工作负载配置,请在 ClusterPolicy 中设置以下值:sandboxWorkloads.defaultWorkload=<config>

安装 GPU Operator#

根据您是否计划使用 NVIDIA vGPU,按照以下子章节之一安装 GPU Operator。

通常,ClusterPolicy 中的标志 sandboxWorkloads.enabled 控制 GPU Operator 是否可以为虚拟机工作负载以及容器工作负载配置 GPU 工作节点。默认情况下禁用此标志,这意味着所有节点都配置了相同的软件以启用容器工作负载,并且不使用 nvidia.com/gpu.workload.config 节点标签。

注意

术语 sandboxing 指的是在单独隔离的环境中运行软件,通常是为了增加安全性(即虚拟机)。我们使用术语 sandbox workloads 来表示在虚拟机中运行的工作负载,而与所使用的虚拟化技术无关。

安装 GPU Operator(不带 NVIDIA vGPU)#

安装 GPU Operator,启用 sandboxWorkloads

$ helm install --wait --generate-name \
      -n gpu-operator --create-namespace \
      nvidia/gpu-operator \
      --version=v24.9.2 \
      --set sandboxWorkloads.enabled=true

安装 GPU Operator(带 NVIDIA vGPU)#

构建私有 NVIDIA vGPU Manager 容器镜像并推送到私有注册表。按照本节中提供的步骤操作。

为 GPU Operator 创建命名空间

$ kubectl create namespace gpu-operator

创建 ImagePullSecret 以访问 NVIDIA vGPU Manager 镜像

$ kubectl create secret docker-registry ${REGISTRY_SECRET_NAME} \
  --docker-server=${PRIVATE_REGISTRY} --docker-username=<username> \
  --docker-password=<password> \
  --docker-email=<email-id> -n gpu-operator

安装启用了 sandboxWorkloadsvgpuManager 的 GPU Operator,并指定先前构建的 NVIDIA vGPU Manager 镜像

$ helm install --wait --generate-name \
      -n gpu-operator --create-namespace \
      nvidia/gpu-operator \
      --version=v24.9.2 \
      --set sandboxWorkloads.enabled=true \
      --set vgpuManager.enabled=true \
      --set vgpuManager.repository=<path to private repository> \
      --set vgpuManager.image=vgpu-manager \
      --set vgpuManager.version=<driver version> \
      --set vgpuManager.imagePullSecrets={${REGISTRY_SECRET_NAME}}

由 GPU Operator 部署的 vGPU 设备管理器会自动创建 vGPU 设备,这些设备可以分配给 KubeVirt 虚拟机。在没有其他配置的情况下,GPU Operator 会在所有 GPU 上创建一组默认设备。要了解有关 vGPU 设备管理器以及如何配置在集群中创建哪些类型的 vGPU 设备的更多信息,请参阅vGPU 设备配置

向 KubeVirt CR 添加 GPU 资源#

更新 KubeVirt 自定义资源,以便允许集群中的所有 GPU 和 vGPU 设备,并且可以将它们分配给虚拟机。

以下示例显示如何允许 A10 GPU 设备和 A10-24Q vGPU 设备。

  1. 确定 GPU 设备的资源名称

    $ kubectl get node cnt-server-2 -o json | jq '.status.allocatable | with_entries(select(.key | startswith("nvidia.com/"))) | with_entries(select(.value != "0"))'
    

    示例输出

    {
      "nvidia.com/NVIDIA_A10-12Q": "4"
    }
    
  2. 确定 GPU 的 PCI 设备 ID。

    • 您可以在PCI ID 数据库中按设备名称搜索。

    • 如果您具有对节点的host访问权限,则可以使用类似于以下示例的命令列出 NVIDIA GPU 设备

      $ lspci -nnk -d 10de:
      

      示例输出

      65:00.0 3D controller [0302]: NVIDIA Corporation GA102GL [A10] [10de:2236] (rev a1)
              Subsystem: NVIDIA Corporation GA102GL [A10] [10de:1482]
              Kernel modules: nvidiafb, nouveau
      
  3. 修改 KubeVirt 自定义资源,如下面的部分示例所示

    ...
    spec:
      configuration:
        developerConfiguration:
          featureGates:
          - GPU
          - DisableMDEVConfiguration
        permittedHostDevices:
          pciHostDevices:
          - externalResourceProvider: true
            pciVendorSelector: 10DE:2236
            resourceName: nvidia.com/GA102GL_A10
          mediatedDevices:
          - externalResourceProvider: true
            mdevNameSelector: NVIDIA A10-24Q
            resourceName: nvidia.com/NVIDIA_A10-24Q
    ...
    

    按如下方式替换 YAML 中的值

    • pciDeviceSelectorresourceNamepciHostDevices 下,以对应于您的 GPU 型号。

    • mdevNameSelectorresourceNamemediatedDevices 下,以对应于您的 vGPU 类型。

    • 设置 externalResourceProvider=true 以指示此资源由外部设备插件提供,在本例中是由 GPU Operator 部署的 sandbox-device-plugin

有关配置选项的更多信息,请参阅KubeVirt 用户指南

创建带有 GPU 的虚拟机#

在 GPU Operator 完成在工作节点上部署沙箱设备插件和 VFIO 管理器 Pod,并将 GPU 资源添加到 KubeVirt 允许列表后,您可以通过编辑 VirtualMachineInstance 清单中的 spec.domain.devices.gpus 字段,将 GPU 分配给虚拟机。

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstance
...
spec:
  domain:
    devices:
      gpus:
      - deviceName: nvidia.com/GA102GL_A10
        name: gpu1
...
  • deviceName 是表示设备的资源名称。

  • name 是用于标识虚拟机中设备的名称

vGPU 设备配置#

vGPU 设备管理器协助在 GPU 工作节点上创建 vGPU 设备。vGPU 设备管理器允许管理员声明性地定义一组他们希望应用于节点上 GPU 的可能的 vGPU 设备配置。在运行时,他们然后将 vGPU 设备管理器指向其中一个配置,而 vGPU 设备管理器负责应用它。配置文件创建为 ConfigMap,并在所有工作节点之间共享。在运行时,可以使用节点标签 nvidia.com/vgpu.config 来决定在任何给定时间实际将这些配置中的哪一个应用于节点。如果节点未标记,则将使用 default 配置。有关此组件及其配置方式的更多信息,请参阅项目 README

默认情况下,GPU Operator 为 vGPU 设备管理器部署 ConfigMap,其中包含 NVIDIA vGPU 支持的所有vGPU 类型的命名配置。用户可以通过应用 nvidia.com/vgpu.config 节点标签为工作节点选择特定配置。例如,用 nvidia.com/vgpu.config=A10-8Q 标记节点将在节点上的所有 A10 GPU 上创建 3 个类型为 A10-8Q 的 vGPU 设备(注意:3 是每个 GPU 可以创建的 A10-8Q 设备的最大数量)。如果节点未标记,则将应用 default 配置。default 配置将在所有 GPU 上创建 Q 系列 vGPU 设备,其中每个 vGPU 设备的帧缓冲区内存量是 GPU 总内存的一半。例如,default 配置将在所有 A10 GPU 上创建两个 A10-12Q 设备,在所有 V100 GPU 上创建两个 V100-8Q 设备,并在所有 T4 GPU 上创建两个 T4-8Q 设备。

如果需要自定义 vGPU 设备配置,超出默认 ConfigMap 提供的范围,您可以创建自己的 ConfigMap

$ kubectl create configmap custom-vgpu-config -n gpu-operator --from-file=config.yaml=/path/to/file

然后通过设置 vgpuDeviceManager.config.name=custom-vgpu-config 来配置 GPU Operator 以使用它。

应用新的 vGPU 设备配置#

我们可以通过设置 nvidia.com/vgpu.config 节点标签,在每个节点的基础上应用特定的 vGPU 设备配置。如果您不希望应用默认配置,建议在安装 GPU Operator 之前设置此节点标签。

在成功应用一个 vGPU 设备配置后切换 vGPU 设备配置,前提是节点上当前没有运行带有 vGPU 的虚拟机。任何现有的虚拟机都必须先关闭/迁移。

要在 GPU Operator 安装后应用新配置,只需更新 nvidia.com/vgpu.config 节点标签。让我们在一个带有两个 A10 GPU 的系统上运行一个示例。

$ nvidia-smi -L
GPU 0: NVIDIA A10 (UUID: GPU-ebd34bdf-1083-eaac-2aff-4b71a022f9bd)
GPU 1: NVIDIA A10 (UUID: GPU-1795e88b-3395-b27b-dad8-0488474eec0c)

按照前面章节中详细介绍的方式安装 GPU Operator,并且未用 nvidia.com/vgpu.config 标记节点后,将应用 default vGPU 配置 - 创建四个 A10-12Q 设备(每个 GPU 两个)

$ kubectl get node cnt-server-2 -o json | jq '.status.allocatable | with_entries(select(.key | startswith("nvidia.com/"))) | with_entries(select(.value != "0"))'
{
  "nvidia.com/NVIDIA_A10-12Q": "4"
}

如果相反,我们希望创建 A10-4Q 设备,我们可以像这样标记节点

$ kubectl label node <node-name> --overwrite nvidia.com/vgpu.config=A10-4Q

在 vGPU 设备管理器完成应用新配置后,所有 GPU Operator Pod 应该返回到 Running 状态。

$ kubectl get pods -n gpu-operator
NAME                                                          READY   STATUS    RESTARTS   AGE
...
nvidia-sandbox-device-plugin-daemonset-brtb6                  1/1     Running   0          10s
nvidia-sandbox-validator-ljnwg                                1/1     Running   0          10s
nvidia-vgpu-device-manager-8mgg8                              1/1     Running   0          30m
nvidia-vgpu-manager-daemonset-fpplc                           1/1     Running   0          31m

我们现在在节点上看到 12 个 A10-4Q 设备,因为每个 A10 GPU 可以创建 6 个 A10-4Q 设备。

$ kubectl get node cnt-server-2 -o json | jq '.status.allocatable | with_entries(select(.key | startswith("nvidia.com/"))) | with_entries(select(.value != "0"))'
{
  "nvidia.com/NVIDIA_A10-4Q": "12"
}

构建 NVIDIA vGPU Manager 镜像#

注意

仅当您计划使用 NVIDIA vGPU 时才需要构建 NVIDIA vGPU Manager 镜像。如果仅计划使用 PCI 直通,请跳过本节。

本节介绍如何构建 NVIDIA vGPU Manager 容器镜像并将其推送到私有注册表。

NVIDIA Licensing Portal下载 vGPU 软件。

  • 登录到 NVIDIA Licensing Portal 并导航到 Software Downloads 部分。

  • NVIDIA vGPU 软件位于 NVIDIA Licensing Portal 的 Software Downloads 部分。

  • vGPU 软件包以 zip 文件形式打包。下载并解压缩软件包以获取 NVIDIA vGPU Manager for Linux 文件,NVIDIA-Linux-x86_64-<version>-vgpu-kvm.run

    注意

    NVIDIA AI Enterprise 客户必须使用 aie .run 文件来构建 NVIDIA vGPU Manager 镜像。下载 NVIDIA-Linux-x86_64-<version>-vgpu-kvm-aie.run 文件,并将其重命名为 NVIDIA-Linux-x86_64-<version>-vgpu-kvm.run,然后再继续执行其余步骤。

接下来,克隆驱动程序容器存储库并使用以下步骤构建驱动程序镜像。

打开终端并克隆驱动程序容器镜像存储库。

$ git clone https://gitlab.com/nvidia/container-images/driver
$ cd driver

更改到您的操作系统的 vgpu-manager 目录。我们以 Ubuntu 20.04 为例。

$ cd vgpu-manager/ubuntu20.04

注意

对于 RedHat OpenShift,运行 cd vgpu-manager/rhel8 以使用 rhel8 文件夹。

从解压后的 zip 文件复制 NVIDIA vGPU Manager

$ cp <local-driver-download-directory>/*-vgpu-kvm.run ./
设置以下环境变量
PRIVATE_REGISTRY - 用于存储驱动程序镜像的私有注册表的名称
VERSION - 从 NVIDIA Software Portal 下载的 NVIDIA vGPU Manager 版本
OS_TAG - 这必须与 Guest OS 版本匹配。在下面的示例中,使用 ubuntu20.04。对于 RedHat OpenShift,这应该设置为 rhcos4.x,其中 x 是支持的次要 OCP 版本。
CUDA_VERSION - 用于构建驱动程序镜像的 CUDA 基础镜像版本。
$ export PRIVATE_REGISTRY=my/private/registry VERSION=510.73.06 OS_TAG=ubuntu20.04 CUDA_VERSION=11.7.1

构建 NVIDIA vGPU Manager 镜像。

$ docker build \
    --build-arg DRIVER_VERSION=${VERSION} \
    --build-arg CUDA_VERSION=${CUDA_VERSION} \
    -t ${PRIVATE_REGISTRY}/vgpu-manager:${VERSION}-${OS_TAG} .

将 NVIDIA vGPU Manager 镜像推送到您的私有注册表。

$ docker push ${PRIVATE_REGISTRY}/vgpu-manager:${VERSION}-${OS_TAG}