使用 NVIDIA MPS 在多个工作负载间共享 GPU


本页面介绍如何使用 CUDA 多进程服务 (MPS) 允许多个工作负载在 Google Kubernetes Engine (GKE) 节点中共享一个 NVIDIA GPU 硬件加速器。

概览

NVIDIA MPS 是一种 GPU 共享解决方案,可让多个容器共享一个挂接到节点的物理 NVIDIA GPU 硬件。

NVIDIA MPS 依赖于 NVIDIA 的 CUDA 多进程服务。NVIDIA MPS 是 CUDA API 的二进制兼容替代实现,旨在以透明方式使协作多进程 CUDA 应用能够在单个 GPU 设备上并发运行。

借助 NVIDIA MPS,您可以指定物理 GPU 的最大共享容器数量。此值根据以下特征确定每个容器获得的物理 GPU 性能:

如需详细了解如何使用 NVIDIA MPS 调度 GPU 以及应何时使用 CUDA MPS,请参阅 GKE 中的 GPU 共享解决方案简介

本指南的适用对象

如果您属于以下对象,则本节中的说明适用于您:

  • 平台管理员:创建和管理 GKE 集群、规划基础架构和资源要求,并监控集群的性能。
  • 应用开发者:在 GKE 集群上设计和部署工作负载。如需查看有关请求用于 GPU 的 NVIDIA MPS 的说明,请参阅部署使用用于 GPU 的 NVIDIA MPS 的工作负载

要求

  • GKE 版本:您可以在运行 GKE 1.27.7-gke.1088000 及更高版本的 GKE Standard 集群上启用通过 NVIDIA MPS 共享 GPU。
  • GPU 类型:您可以为所有 NVIDIA Tesla® GPU 类型启用 NVIDIA MPS。

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行 gcloud components update 以获取最新版本。

在 GKE 集群上启用用于 GPU 的 NVIDIA MPS

作为平台管理员,您必须在 GKE Standard 集群上启用用于 GPU 的 NVIDIA MPS。然后,应用开发者可以部署工作负载,以使用用于 GPU 的 NVIDIA MPS。如需在 GKE 上启用用于 GPU 的 NVIDIA MPS,请执行以下操作:

  1. 在新的 GKE 集群上启用用于 GPU 的 NVIDIA MPS
  2. 安装 NVIDIA GPU 设备驱动程序(如果需要)
  3. 验证节点上可用的 GPU 资源

在 GKE 集群上启用用于 GPU 的 NVIDIA MPS

您可以在创建 GKE Standard 集群时启用用于 GPU 的 NVIDIA MPS。集群中的默认节点池已启用该功能。在该集群中手动创建新的节点池时,您仍然需要启用用于 GPU 的 NVIDIA MPS。

使用 Google Cloud CLI 创建启用了 NVIDIA MPS 的集群:

gcloud container clusters create CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --cluster-version=CLUSTER_VERSION \
    --machine-type=MACHINE_TYPE \
    --accelerator=type=GPU_TYPE,count=GPU_QUANTITY,gpu-sharing-strategy=mps,max-shared-clients-per-gpu=CLIENTS_PER_GPU,gpu-driver-version=DRIVER_VERSION

替换以下内容:

  • CLUSTER_NAME:新集群的名称。
  • COMPUTE_REGION:新集群的 Compute Engine 区域。对于可用区级集群,请指定 --zone=COMPUTE_ZONE。您使用的 GPU 类型必须在所选可用区中可用
  • CLUSTER_VERSION:集群控制层面和节点的 GKE 版本。使用 GKE 1.27.7-gke.1088000 或更高版本。或者,您可以使用 --release-channel=RELEASE_CHANNEL 标志指定具有该 GKE 版本的发布渠道
  • MACHINE_TYPE:节点的 Compute Engine 机器类型。
  • GPU_TYPE:GPU 类型,必须是 NVIDIA Tesla GPU 平台,例如 nvidia-tesla-v100
  • GPU_QUANTITY:要挂接到默认节点池中每个节点的物理 GPU 数量。
  • CLIENTS_PER_GPU:可以共享每个物理 GPU 的容器数量上限。
  • DRIVER_VERSION:要安装的 NVIDIA 驱动程序版本。可以是以下各项之一:
    • default:为您的 GKE 版本安装默认驱动程序版本。
    • latest:为您的 GKE 版本安装最新可用的驱动程序版本。仅适用于使用 Container-Optimized OS 的节点。
    • disabled:跳过自动驱动程序安装。创建节点池后,您必须手动安装驱动程序。 如果您省略 gpu-driver-version,则这是默认选项。

在新节点池上启用用于 GPU 的 NVIDIA MPS

您可以在 GKE 集群中手动创建新节点池时,启用用于 GPU 的 NVIDIA MPS。使用 Google Cloud CLI 创建启用了 NVIDIA MPS 的节点池:

gcloud container node-pools create NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --region=COMPUTE_REGION \
    --accelerator=type=GPU_TYPE,count=GPU_QUANTITY,gpu-sharing-strategy=mps,max-shared-clients-per-gpu=CONTAINER_PER_GPU,gpu-driver-version=DRIVER_VERSION

替换以下内容:

  • NODEPOOL_NAME:新节点池的名称。
  • CLUSTER_NAME:您的集群的名称,该集群必须运行 1.27.7-gke.1088000 或更高版本。
  • COMPUTE_REGION:集群的 Compute Engine 区域。对于可用区级集群,请指定 --zone=COMPUTE_ZONE
  • MACHINE_TYPE:节点的 Compute Engine 机器类型。对于 A100 GPU,请使用 A2 机器类型。对于所有其他 GPU,请使用 N1 机器类型
  • GPU_TYPE:GPU 类型,必须是 NVIDIA Tesla GPU 平台,例如 nvidia-tesla-v100
  • GPU_QUANTITY:要挂接到节点池中每个节点的物理 GPU 数量。
  • CONTAINER_PER_GPU:可以共享每个物理 GPU 的容器数量上限。
  • DRIVER_VERSION:要安装的 NVIDIA 驱动程序版本。可以是下列选项之一:

    • default:为您的 GKE 版本安装默认驱动程序版本。
    • latest:为您的 GKE 版本安装最新可用的驱动程序版本。仅适用于使用 Container-Optimized OS 的节点。
    • disabled:跳过自动驱动程序安装。创建节点池后,您必须手动安装驱动程序。 如果您省略 gpu-driver-version,则这是默认选项。

安装 NVIDIA GPU 设备驱动程序

如果您在创建集群时选择了停用自动驱动程序安装,或者如果您使用低于 1.27.2-gke.1200 的 GKE 版本,则必须手动安装兼容的 NVIDIA 驱动程序来管理 NVIDIA MPS 对物理 GPU 的划分。如需安装驱动程序,请部署用于设置驱动程序的 GKE 安装 DaemonSet。

如需了解相关说明,请参阅安装 NVIDIA GPU 设备驱动程序

验证可用的 GPU 资源

您可以验证节点中的 GPU 数量是否与启用 NVIDIA MPS 时指定的数量一致。您还可以验证 NVIDIA MPS 控制守护程序是否正在运行。

验证节点上可用的 GPU 资源

如需验证节点上可用的 GPU 资源,请运行以下命令:

kubectl describe nodes NODE_NAME

NODE_NAME 替换为您的其中一个节点的名称。

输出类似于以下内容:

...
Capacity:
  ...
  nvidia.com/gpu:             3
Allocatable:
  ...
  nvidia.com/gpu:             3

在此输出中,由于以下值,节点上 GPU 资源的数量为 3

  • max-shared-clients-per-gpu 中的值为 3
  • 要挂接到节点的物理 GPU 的 count1。如果物理 GPU 的 count2,则输出将显示 6 个可分配的 GPU 资源,每个物理 GPU 上有三个。

验证 MPS 控制守护程序是否正在运行

GPU 设备插件对 MPS 控制守护程序执行健康检查。当 MPS 控制守护程序运行状况良好时,您可以部署容器。

要验证 MPS 是否处于该状态,请运行以下命令:

kubectl logs -l k8s-app=nvidia-gpu-device-plugin -n kube-system --tail=100 | grep MPS

输出类似于以下内容:

I1118 08:08:41.732875       1 nvidia_gpu.go:75] device-plugin started
...
I1110 18:57:54.224832       1 manager.go:285] MPS is healthy, active thread percentage = 100.0
...

在输出中,您可能会看到发生了以下事件:

  • failed to start GPU device manager 错误在 MPS is healthy 错误之前。此错误是暂时性的。如果您看到 MPS is healthy 消息,则表示控制守护程序正在运行。
  • active thread percentage = 100.0 消息表示整个物理 GPU 资源都有一个完全活跃的线程。

部署使用 MPS 的工作负载

作为部署 GPU 工作负载的应用操作员,您可以指示 GKE 在同一物理 GPU 中共享 MPS 共享单元。在以下清单中,您需要请求一个物理 GPU 并设置 max-shared-clients-per-gpu=3。物理 GPU 获得三个 MPS 共享单元,并启动一个并行运行三个 Pod(容器)的 nvidia/samples:nbody 作业。

  1. 将清单保存为 gpu-mps.yaml

      apiVersion: batch/v1
      kind: Job
      metadata:
        name: nbody-sample
      spec:
        completions: 3
        parallelism: 3
        template:
          spec:
            hostIPC: true
            nodeSelector:
              cloud.google.com/gke-gpu-sharing-strategy: mps
            containers:
              - name: nbody-sample
                image: nvidia/samples:nbody
                command: ["/tmp/nbody"]
                args: ["-benchmark", "-i=5000"]
                resources:
                  limits:
                    nvidia.com/gpu: 1
            restartPolicy: "Never"
        backoffLimit: 1
    

    在此清单中:

    • hostIPC: true 使 Pod 能够与 MPS 控制守护程序通信。因此必须提供。但是,请注意 hostIPC: true 配置允许容器访问主机资源,这会带来安全风险。
    • 在基准模式下运行 5,000 次迭代。
  2. 应用清单:

    kubectl apply -f gpu-mps.yaml
    
  3. 验证所有 pod 是否正在运行:

    kubectl get pods
    

    输出类似于以下内容:

    NAME                           READY   STATUS    RESTARTS   AGE
    nbody-sample-6948ff4484-54p6q   1/1     Running   0          2m6s
    nbody-sample-6948ff4484-5qs6n   1/1     Running   0          2m6s
    nbody-sample-6948ff4484-5zpdc   1/1     Running   0          2m5s
    
  4. 检查来自 Pod 的日志以验证作业是否已完成:

    kubectl logs -l job-name=nbody-sample -f
    

    输出类似于以下内容:

    ...
    > Compute 8.9 CUDA device: [NVIDIA L4]
    18432 bodies, total time for 5000 iterations: 9907.976 ms
    = 171.447 billion interactions per second
    = 3428.941 single-precision GFLOP/s at 20 flops per interaction
    ...
    

    由于 GKE 运行 50,000 次迭代,因此日志可能需要几分钟时间。

清理

通过运行以下命令删除作业及其所有 Pod:

kubectl delete job --all

使用 NVIDIA MPS 限制固定的设备内存和活跃线程

默认情况下,在 GKE 上使用 NVIDIA MPS 调度 GPU 时,系统会将以下 CUDA 环境变量注入 GPU 工作负载:

  • CUDA_MPS_ACTIVE_THREAD_PERCENTAGE:此变量表示每个 MPS 共享单元可以使用的可用线程百分比。默认情况下,GPU 的每个 MPS 共享单元都设置为 100 / MaxSharedClientsPerGPU,从而根据流多处理器获得相等的 GPU 计算切片。
  • CUDA_MPS_PINNED_DEVICE_MEM_LIMIT:此变量用于限制 GPU 的 MPS 共享单元可以分配的 GPU 内存量。默认情况下,GPU 的每个 MPS 共享单元会设置为 total mem / MaxSharedClientsPerGPU,以获取相等的 GPU 内存切片。

如需为 GPU 工作负载设置资源限制,请配置以下 NVIDIA MPS 环境变量:

  1. 查看并构建 GitHub 中的 cuda-mps 示例的映像。

  2. 将以下清单保存为 cuda-mem-and-sm-count.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: cuda-mem-and-sm-count
    spec:
      hostIPC: true
      nodeSelector:
        cloud.google.com/gke-gpu-sharing-strategy: mps
      containers:
        - name: CUDA_MPS_IMAGE
          image: gcr.io/gracegao-gke-dev/cuda-mem-and-sm-count:latest
          securityContext:
            privileged: true
          resources:
            limits:
              nvidia.com/gpu: 1
    

    CUDA_MPS_IMAGE 替换为您为 cuda-mps 示例构建的映像的名称。

    NVIDIA MPS 要求您在 Pod 上设置 hostIPC:truehostIPC:true 配置允许容器访问主机资源,这会带来安全风险。

  3. 应用清单:

    kubectl apply -f cuda-mem-and-sm-count.yaml
    
  4. 检查此 Pod 的日志:

    kubectl logs cuda-mem-and-sm-count
    

    在将 NVIDIA Tesla® L4gpu-sharing-strategy=mpsmax-shared-clients-per-gpu=3 搭配使用的示例中,输出类似于以下内容:

    For device 0:  Free memory: 7607 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 18
    

    在此示例中,NVIDIA Tesla® L4 GPU 具有 60 个 SM 和 24 GB 内存。每个 MPS 共享单元大约可获得 33% 的活跃线程和 8 GB 内存。

  5. 将清单更新为请求 2 个 nvidia.com/gpu

      resources:
            limits:
              nvidia.com/gpu: 2
    

    输出类似于以下内容:

    For device 0:  Free memory: 15230 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 38
    
  6. 更新清单以替换 CUDA_MPS_ACTIVE_THREAD_PERCENTAGECUDA_MPS_PINNED_DEVICE_MEM_LIMIT 变量:

      env:
        - name: CUDA_MPS_ACTIVE_THREAD_PERCENTAGE
          value: "20"
        - name: CUDA_MPS_PINNED_DEVICE_MEM_LIMIT
          value: "0=8000M"
    

    输出类似于以下内容:

    For device 0:  Free memory: 7952 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 10
    

限制

  • Volta 中及之后的 GPU 类型相比,Volta 之前的 GPU(k80 和 P100)上的 MPS 功能有限
  • 借助 NVIDIA MPS,GKE 可确保每个容器都获得有限的固定设备内存和活跃线程。但是,并未捕获其他资源(例如内存带宽、编码器或解码器)以纳入这些资源限制。因此,如果容器都请求相同的无限制资源,则它们可能会对性能产生负面影响。
  • NVIDIA MPS 具有内存保护和错误控制限制。我们建议您评估此限制,以确保与您的工作负载兼容。
  • NVIDIA MPS 要求您在 Pod 上设置 hostIPC:truehostIPC:true 配置允许容器访问主机资源,这会带来安全风险。
  • 使用 NVIDIA MPS 时,GKE 可能会拒绝某些 GPU 请求,以防止在容量分配期间出现意外行为。如需了解详情,请参阅 GPU 共享解决方案的请求限制
  • 可以与 NVIDIA MPS 共享单个物理 GPU 的容器数量上限为 48 个(Volta 之前的 GPU 仅支持 16 个)。在规划您的 NVIDIA MPS 配置时,请考虑工作负载的资源需求以及底层物理 GPU 的容量,从而优化性能和响应速度。
  • 仅支持使用 Google Cloud CLI 或 Google Cloud 控制台配置 NVIDIA MPS API。

后续步骤