K8S GPU调度——手动管理GPU 节点

K8S调度GPU介绍

技术背景

在我们平时创建资源时,经常会这样配置:

yaml 复制代码
resources:  
  requests:    
    cpu: "1"    
    memory: "1Gi"  
  limits:    
    cpu: "2"    
    memory: "2Gi"

其中每个节点有多少 CPU、多少内存,kubelet 可以直接获取,scheduler 也能根据这些资源,把 Pod 调度到合适的节点。

但 GPU 不一样。GPU 属于不同厂商提供的专用硬件,比如 NVIDIA、AMD、Intel,每家的驱动、设备发现方式、容器使用方式都不一样。

如果 Kubernetes 自己把所有厂商的 GPU 管理逻辑都写进去,不仅代码会非常复杂,以后每增加一种硬件都要修改 Kubernetes 本身,维护成本也会很高。

因此,Kubernetes 采用了"插件化"的设计:

  • Kubernetes 只负责提供统一的 Device Plugin 接口。
  • 每个硬件厂商负责开发自己的 Device Plugin。
  • Device Plugin 运行在节点上,负责发现 GPU、管理 GPU,并把 GPU 信息注册给 kubelet。
  • kubelet 再把 GPU 作为一种扩展资源上报给 Kubernetes。
  • 最终,Pod 就可以像申请 CPU、内存一样,通过 resources.limits 申请 GPU。

简单来说:

CPU、内存:Kubernetes 自带支持。

GPU:Kubernetes 不直接管理,而是由厂商提供插件接入。

对于 NVIDIA GPU 来说,这个插件就是 NVIDIA Device Plugin。

安装并运行 NVIDIA Device Plugin 之后,节点上才会出现类似下面的资源:

plain 复制代码
nvidia.com/gpu

Pod 才能通过下面这种方式申请 GPU:

yaml 复制代码
resources:  
  limits:    
    nvidia.com/gpu: 1

也就是说,在 Kubernetes 里使用 NVIDIA GPU,不能只看宿主机上有没有驱动,还要看 Kubernetes 能不能识别和调度这个 GPU 资源。

整个调用过程可以分为如下三层:硬件系统层、容器层、Kubernetes 层

硬件系统层

在物理机上使用 NVIDIA GPU,最核心的组件是 NVIDIA Driver(驱动程序)。Driver 的作用是让操作系统能够识别、管理并驱动 NVIDIA GPU 硬件。

对于大多数 GPU,只要安装好 NVIDIA Driver,就可以正常使用 GPU。而对于 A100、H100、H800 等支持 NVSwitch 的服务器级 GPU,通常还需要安装并启动 NVIDIA Fabric Manager。

Fabric Manager 负责管理 NVSwitch 拓扑和 GPU 之间的高速互联,保证多 GPU 能够正常通信。如果 Fabric Manager 未正常运行,在这类服务器上可能会出现 GPU 无法正常初始化、GPU 间通信异常,甚至 nvidia-smi 无法正常显示 GPU 信息等问题。

驱动及相关组件安装完成后,通常可以通过 nvidia-smi 命令检查 GPU 是否工作正常。如果能够正常输出 GPU 型号、Driver Version、CUDA Version、显存使用情况等信息,说明宿主机已经成功识别 GPU,相关组件工作正常。

需要注意的是,nvidia-smi 输出中的 CUDA Version 表示当前 Driver 所支持的最高 CUDA Runtime 版本,并不代表宿主机已经安装了 CUDA Toolkit。

如果只是运行已经打包好的 CUDA 应用(例如 Docker 容器中的 AI 推理服务),通常只需要保证 Driver 版本满足容器内 CUDA Runtime 的兼容要求即可,无需在宿主机安装 CUDA Toolkit。

而如果需要在宿主机上开发或编译 CUDA 程序,例如使用 nvcc 编译 CUDA 代码,则还需要安装 CUDA Toolkit。CUDA Toolkit 包含 CUDA 编译器(nvcc)、头文件、运行时库、数学库以及调试、性能分析等开发工具,是 CUDA 应用开发所必需的组件。

因此,物理机环境可以简单理解为:

plain 复制代码
NVIDIA GPU 硬件
        ↓
NVIDIA Driver
        ↓
(部分服务器需要)NVIDIA Fabric Manager
        ↓
nvidia-smi / CUDA 应用

其中:

  • NVIDIA Driver:负责让操作系统识别并驱动 GPU。
  • NVIDIA Fabric Manager:仅部分支持 NVSwitch 的服务器需要,负责管理 GPU 之间的高速互联。
  • CUDA Toolkit:仅在开发或编译 CUDA 程序时需要,运行已编译好的 CUDA 应用通常无需安装。

这一层解决的核心问题是:操作系统能否正确识别和驱动 GPU,并为上层应用提供 GPU 计算能力。

容器层

进入 Docker 或 containerd 等容器环境后,仅安装 NVIDIA Driver 还不够。

这是因为容器默认是隔离运行的。即使宿主机已经能够正常识别 GPU,也不意味着容器能够直接访问 GPU。

要让容器使用 GPU,需要解决以下几个问题:

  • 如何将 GPU 设备文件(如 /dev/nvidia*)映射到容器中;
  • 如何将 NVIDIA Driver 相关的库文件挂载到容器中;
  • 容器运行时如何识别当前容器需要使用 GPU;
  • 容器启动时需要注入哪些 NVIDIA 相关环境变量和运行能力。

这些工作都由 NVIDIA Container Toolkit 完成。

可以将 NVIDIA Container Toolkit 理解为 连接 NVIDIA Driver 与容器运行时的桥梁。它为 Docker、containerd、CRI-O 等容器运行时提供 GPU 支持,使容器能够正常访问宿主机上的 GPU。

整个容器环境的关系可以简单理解为:

plain 复制代码
NVIDIA GPU 硬件
        ↓
NVIDIA Driver
        ↓
NVIDIA Container Toolkit
        ↓
Docker / containerd / CRI-O
        ↓
GPU 容器(可使用 GPU)

当容器启动时,NVIDIA Container Toolkit 会自动完成 GPU 设备、驱动库以及相关运行环境的注入,使容器能够像在宿主机上一样使用 GPU。

如果宿主机执行 nvidia-smi 正常,但容器内执行 nvidia-smi 失败,通常说明宿主机 Driver 没有问题,应重点检查 NVIDIA Container Toolkit 是否安装正确,以及 Docker 或 containerd 的运行时配置是否正确。

例如,在 Docker 环境下,可以使用下面的命令进行验证:

plain 复制代码
docker run --rm --gpus all \
  nvidia/cuda:12.4.1-base-ubuntu22.04 \
  nvidia-smi

如果能够正常输出 GPU 型号、Driver Version、CUDA Version 等信息,说明容器运行时已经能够正确访问宿主机 GPU,NVIDIA Container Toolkit 配置正常。

这一层解决的核心问题是:容器能否正常使用宿主机的 GPU。

Kubernetes 层

在 Docker 或 containerd 中,只需要解决容器如何使用 GPU 的问题;而在 Kubernetes 中,除了让容器能够使用 GPU,还需要解决GPU 如何被 Kubernetes 感知、管理和调度的问题。

Kubernetes 在调度 Pod 之前,需要知道:

  • 当前哪些节点安装了 GPU;
  • 每个节点有多少张 GPU;
  • 每张 GPU 是否已经被分配;
  • 当 Pod 申请 GPU 时,应该调度到哪个节点。

这些信息并不是 NVIDIA DriverNVIDIA Container Toolkit 能够提供的。

  • NVIDIA Driver 负责让宿主机识别和驱动 GPU;
  • NVIDIA Container Toolkit 负责让容器运行时能够使用 GPU;
  • NVIDIA Device Plugin 则负责将 GPU 注册给 Kubernetes,使 Kubernetes 能够识别、统计和调度 GPU 资源。

可以将 NVIDIA Device Plugin 理解为 连接 GPU 与 Kubernetes 的桥梁。它运行在每个 GPU 节点上,负责发现本机 GPU,并通过 Kubernetes 提供的 Device Plugin 接口,将 GPU 信息注册给 kubelet。

注册完成后,kubelet 会将 GPU 作为扩展资源(如 nvidia.com/gpu)上报给 Kubernetes,scheduler 便可以根据这些资源进行调度。当 Pod 声明需要 GPU 时,scheduler 会选择满足条件的节点,而容器启动时再由 NVIDIA Container Toolkit 将 GPU 设备注入到容器中。

整个 Kubernetes 环境的关系可以简单理解为:

plain 复制代码
NVIDIA GPU 硬件
        ↓
NVIDIA Driver
        ↓
NVIDIA Container Toolkit
        ↓
NVIDIA Device Plugin
        ↓
kubelet 注册 nvidia.com/gpu
        ↓
scheduler 根据 GPU 资源调度 Pod
        ↓
GPU Pod 启动并使用 GPU

例如,一个 Pod 可以通过下面的配置申请一张 GPU:

yaml 复制代码
resources:
  limits:
    nvidia.com/gpu: 1

当 Kubernetes 看到 nvidia.com/gpu: 1 时,会先由 scheduler 选择一台还有可用 GPU 的节点,再由 kubelet 和 NVIDIA Container Toolkit 完成 GPU 设备的挂载,最终使容器能够正常使用 GPU。

因此,这三类组件分别负责不同层面的工作:

  • 物理机层:NVIDIA Driver,让操作系统能够识别和驱动 GPU。
  • 容器层:NVIDIA Container Toolkit,让容器能够使用宿主机 GPU。
  • Kubernetes 层:NVIDIA Device Plugin,让 Kubernetes 能够发现、管理和调度 GPU。

这一层解决的核心问题是:Kubernetes 能否识别 GPU,并将 GPU 正确调度给需要它的 Pod。

组件依赖关系


|"① 强依赖
版本必须严格匹配驱动"| DRV

复制代码
TOOLKIT -->|"② 依赖<br/>注入宿主机驱动库到容器"| DRV
DOCKER -->|"③ 依赖<br/>通过 runtime hook 实现 GPU 透传"| TOOLKIT
DP -->|"④ 依赖 NVML<br/>探测 GPU 数量/状态"| DRV
DP -->|"⑤ 依赖<br/>容器运行 + GPU 注入"| DOCKER

classDef base fill:#742a2a,stroke:#e53e3e,color:#fff,stroke-width:2px
classDef mid fill:#744210,stroke:#dd6b20,color:#fff,stroke-width:2px
classDef cont fill:#1a365d,stroke:#3182ce,color:#fff,stroke-width:2px
classDef k8s fill:#22543d,stroke:#38a169,color:#fff,stroke-width:2px

class DRV base
class FM mid
class TOOLKIT,DOCKER cont
class DP k8s

-->

调用链

启动阶段(初始化):

  1. 宿主机加载 GPU 驱动内核模块。
  2. (NVSwitch 机型)nvidia-fabricmanager 守护进程启动,调用驱动完成 NVSwitch/NVLink 拓扑初始化。没有这一步,多卡之间无法通过 NVLink 通信,甚至 nvidia-smi 都会报错。
  3. k8s-device-plugin(通常以 DaemonSet 形式运行)启动,通过驱动的 NVML 库 (libnvidia-ml.so) 探测本机有几块 GPU,然后通过 gRPC 向 kubelet 注册资源 nvidia.com/gpu 并上报数量。

调度阶段(创建 Pod):

  1. 用户提交一个请求 nvidia.com/gpu: 1 的 Pod。
  2. K8s 调度器把 Pod 调度到有空闲 GPU 的节点。
  3. kubelet 通过 Device Plugin API 调用 k8s-device-plugin 的 Allocate 方法,device-plugin 返回应该挂载哪些 GPU 设备、设置哪些环境变量(如 NVIDIA_VISIBLE_DEVICES)。
  4. kubelet 把这些信息传给容器运行时(containerd/Docker)。
  5. 容器运行时在创建容器时触发 nvidia-container-toolkit 的 hook (nvidia-container-runtime-hook)。
  6. toolkit 根据 NVIDIA_VISIBLE_DEVICES,通过 libnvidia-container 把宿主机的 GPU 设备节点(/dev/nvidia*)和驱动用户态库注入到容器的 namespace 中。
  7. 容器内的 CUDA 应用此时就能通过注入进来的 libcuda.so 访问 GPU 了。

了解了各个组件的作用及它们之间的调用关系后,接下来通过实际操作,逐步完成 GPU 环境的搭建,并验证各个组件的工作流程,加深对整个 GPU 使用链路的理解。

GPU 节点配置

安装GPU驱动

开始之前,请确保您没有任何现有的 NVIDIA 驱动程序,可以使用以下命令卸载它们,并重新启动服务器。

bash 复制代码
# 删除所有nvidia包
sudo apt purge '^nvidia-.*'
sudo apt purge '^libnvidia-.*'
sudo apt purge '^cuda-.*'
sudo apt purge '^libcuda.*'
sudo apt autoremove --purge
# 删除残留配置
sudo rm -rf /etc/modprobe.d/nvidia*
sudo rm -rf /etc/modules-load.d/nvidia*
sudo rm -rf /usr/local/cuda*
sudo rm -rf /var/lib/dkms/nvidia*
# 更新 initramfs
sudo update-initramfs -u

也可以使用官方提供的卸载脚本卸载(仅限之前通过官方.run 文件安装的驱动)

bash 复制代码
/usr/bin/nvidia-uninstall
dpkg -r nvidia-fabricmanager-550
dpkg --purge nvidia-fabricmanager-550
dpkg -l | grep nvidia-fabricmanager

浏览官网https://www.nvidia.com/en-us/drivers/unix/,下载合适版本的驱动。

bash 复制代码
wget https://download.nvidia.com/XFree86/Linux-x86_64/550.163.01/NVIDIA-Linux-x86_64-550.163.01.run
chmod +x NVIDIA-Linux-x86_64-550.163.01.run
./NVIDIA-Linux-x86_64-550.163.01.run -no-x-check -no-nouveau-check -no-opengl-files

重启并通过运行以下命令确保驱动程序正常工作:

bash 复制代码
# nvidia-smi 
Wed Oct 16 14:56:01 2024       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| 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 A100-SXM4-80GB          On  |   00000000:3F:00.0 Off |                    0 |
| N/A   32C    P0             54W /  400W |       0MiB /  81920MiB |      0%      Default |
|                                         |                        |             Disabled

安装nvidia-fabricmanager

专门用于管理多 GPU 系统中的 GPU 间通信优化,对于 A100、H100、H800 等支持 NVSwitch 的服务器级 GPU,确保GPU间的连接和通信的稳定性。

浏览官网:https://developer.download.nvidia.cn/compute/cuda/repos/,下载合适版本的软件包,以 ubuntu22.04 为例:

bash 复制代码
wget https://developer.download.nvidia.cn/compute/cuda/repos/ubuntu2204/x86_64/nvidia-fabricmanager-550_550.163.01-1_amd64.deb
apt --fix-broken install -y
apt-get install ./nvidia-fabricmanager-550_550.163.01-1_amd64.deb
systemctl enable nvidia-fabricmanager
systemctl restart nvidia-fabricmanager
systemctl status nvidia-fabricmanager

安装容器工具包

容器环境使用 GPU 需要配置 NVIDIA runtime,具体可参考文档:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html

我们首先配置容器工具包软件仓库

bash 复制代码
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

bash 复制代码
apt-get update
apt-get install nvidia-container-toolkit -y

配置 containerd 运行时环境

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

bash 复制代码
sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.bak

然后,我们可以根据k8s-device-plugin 自述文件手动配置containerd,或者运行

bash 复制代码
sudo nvidia-ctk runtime configure --runtime=containerd

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

配置 docker 运行时环境

bash 复制代码
# 配置 nvidia runtime
nvidia-ctk runtime configure --runtime=docker

# 重启 Docker
systemctl restart docker

验证是否成功

bash 复制代码
docker run --rm --gpus all docker.io/nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

k8s配置

要想让 kubelet 能够感知到节点上的 GPU 设备,以便进行 GPU 资源调度和管理,需要安装 k8s-device-plugin,具体可参考文档:https://github.com/NVIDIA/k8s-device-plugin

安装NVIDIA 设备插件

GPU 节点添加标签

bash 复制代码
kubectl label nodes ${node} nvidia.com/gpu.present=true

安装工作的 CUDA 驱动程序、设置 NVIDIA 容器工具包和将 containerd 配置为使用 NVIDIA 运行时环境,我们现在可以使用其 Helm chart 来应用 NVIDIA 设备插件。

bash 复制代码
helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
helm repo update
helm search repo nvdp --devel
helm upgrade -i nvdp nvdp/nvidia-device-plugin \
  --namespace nvidia-device-plugin \
  --create-namespace \
  --version 0.19.3

查看是否已经识别 gpu 资源

bash 复制代码
# kubectl describe node | grep nvidia.com/gpu                   
                    nvidia.com/gpu.present=true
                    management.cattle.io/pod-limits: {"cpu":"22890m","ephemeral-storage":"8Gi","memory":"75780Mi","nvidia.com/gpu":"1"}
                    management.cattle.io/pod-requests: {"cpu":"11842m","ephemeral-storage":"200Mi","memory":"39990Mi","nvidia.com/gpu":"1","pods":"24"}
  nvidia.com/gpu:     8
  nvidia.com/gpu:     8
  nvidia.com/gpu     0             0

测试验证

通过启动一个请求 GPU 资源的 pod 来使用 GPU。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  restartPolicy: Never
  containers:
    - name: cuda-container
      image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda12.5.0
      resources:
        limits:
          nvidia.com/gpu: "1" # 需要一张1GPU显卡资源
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule
kubectl apply -f cuda-vectoradd.yaml

如果一切顺利,工作负载的日志应该显示:

plain 复制代码
kubectl logs -n cuda-test cuda-vectoradd
[Vector addition of 50000 elements]
... Test PASSED

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

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

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

GPU 共享访问

NVIDIA 设备插件的默认行为是将整个 GPU 分配给单个 pod,这意味着如果有多个 pod 请求 GPU 时间,每次只会调度一个 pod。NVIDIA 设备插件通过其配置文件中一组扩展选项允许 GPU 的超额分配。有两种可用的共享方式:时间切片和 MPS,具体可参考文档:https://github.com/NVIDIA/k8s-device-plugin?tab=readme-ov-file#shared-access-to-gpus

CUDA 时间切片和 MPS 的使用是互斥的。

  • 在时间切片的情况下,CUDA 时间切片用于允许共享 GPU 的工作负载相互交错。然而,并未采取特殊措施来隔离从同一底层 GPU 获得副本的工作负载,每个工作负载都可以访问 GPU 内存,并在与其他所有工作负载相同的故障域中运行(这意味着如果一个工作负载崩溃,它们全部都会崩溃)。
  • 在 MPS 的情况下,使用控制守护程序来管理对共享 GPU 的访问。与时间切片相反,MPS 进行空间分区,并允许内存和计算资源被显式地分区,并对每个工作负载强制执行这些限制。

两者对比如下:

维度 CUDA timeslice MPS(Multi-Process Service)
原理 GPU 在多个进程间按时间片轮转,同一时刻只有一个进程在跑 多个进程的 kernel 同时并发在 GPU 上跑(空间共享)
上下文切换 有,频繁切换有开销 无,共享一个 CUDA context
算力分配 轮流,无配额 可设每个客户端算力百分比上限(active thread percentage)
显存限制 可限制每个客户端显存上限 (CUDA_MPS_PINNED_DEVICE_MEM_LIMIT)
故障隔离 无(一个进程崩可能拖垮 MPS daemon 影响全部)
延迟 时间片切换有抖动 并发执行,延迟更稳定、吞吐更高

使用 CUDA timeslice

首先创建一个ConfigMap,配置最大 10 个副本来配置时间切片。也就是说将一个 GPU 显卡拆分成了 10 个资源,假设一台服务器原本是 8 张 GPU 显卡,则现在可以使用的资源为 80 个。

yaml 复制代码
# 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,并更新nvidia-device-plugin使用它。

bash 复制代码
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的容量:

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

请注意,工作负载从同一 GPU 获取副本,每个工作负载都可以访问相同的 GPU 内存,并在同一故障域中运行,这意味着如果一个工作负载崩溃,它们都会崩溃。

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

使用 CUDA MPS

目前在启用了 MIG 的设备上不支持使用 MPS 进行共享

  1. 创建配置文件
yaml 复制代码
cat << EOF > mps-config.yaml
version: v1
sharing:
  mps:
    resources:
    - name: nvidia.com/gpu
      replicas: 10
EOF

如果将此配置应用于具有 8 个 GPU 的节点,则该插件现在将向nvidia.com/gpuKubernetes 通告 80 个资源,而不是 8 个。每块卡会按照 10 分之一的资源来作为 nvidia.com/gpu: 1 受用。

  1. 添加节点标签
yaml 复制代码
kubectl label nodes ${node} nvidia.com/mps.capable=true
  1. 更新 NVIDIA-K8s-Device-Plugin插件
bash 复制代码
helm upgrade nvidia-device-plugin nvdp/nvidia-device-plugin \
  --namespace nvidia-device-plugin \
  --create-namespace \
  --set-file config.map.config=mps-config.yaml
  1. 验证
bash 复制代码
kubectl describe node miaohua-a-w-69 | grep nvidia.com/gpu
                    nvidia.com/gpu.present=true
                    management.cattle.io/pod-limits: {"cpu":"35270m","memory":"341212Mi","nvidia.com/gpu":"0"}
                    management.cattle.io/pod-requests: {"cpu":"35212m","memory":"341242Mi","nvidia.com/gpu":"0","pods":"15"}
  nvidia.com/gpu:     80
  nvidia.com/gpu:     80
  nvidia.com/gpu     0               0

CUDA MIG

功能介绍

多实例GPU(MIG)功能允许将NVIDIA A卡或者H卡安全地划分为多个独立的GPU实例,供CUDA应用程序使用。例如,NVIDIA A100支持最多七个独立的GPU实例。

MIG为多个用户提供了独立的GPU资源,以实现最佳的GPU利用率。该功能对于未能充分利用GPU计算能力的工作负载特别有益,因此用户可能希望并行运行不同的工作负载,以最大化资源的利用。

技术对比

特性 Time-Slicing MPS MIG
资源分配方式 按时间动态分配 动态共享 静态划分
资源隔离 无资源隔离 无资源隔离 硬件级资源隔离
性能开销 上下文切换开销 较小(减少上下文切换) 无上下文切换开销
硬件支持 所有 CUDA GPU 所有 CUDA GPU 仅支持 MIG 的 GPU
适用任务 非关键任务 高性能计算、多进程任务 多租户环境、实时推理任务
配置复杂度 无需配置,默认启用 需要配置 MPS 服务 需要显式创建 MIG 实例

部署MIG

启用 MIG 模式

在宿主机上运行以下命令来启用 MIG 模式,如果启用失败多半是因为资源占用,可以停止相关 gpu 资源然后执行 nvidia-smi -r或者重启服务器。

bash 复制代码
# nvidia-smi -mig 1
Enabled MIG Mode for GPU 00000000:3F:00.0
Enabled MIG Mode for GPU 00000000:44:00.0
Enabled MIG Mode for GPU 00000000:62:00.0
Enabled MIG Mode for GPU 00000000:68:00.0
Enabled MIG Mode for GPU 00000000:A9:00.0
Enabled MIG Mode for GPU 00000000:AD:00.0
Enabled MIG Mode for GPU 00000000:D2:00.0
Enabled MIG Mode for GPU 00000000:D5:00.0
All done.

除了将所有显卡都启用 mig 模式外,也可以指定显卡启用 mig,例如只开启 GPU 0 的 mig。

bash 复制代码
# nvidia-smi -i 0 -mig 1

创建GI实例

启用 MIG 模式后,你需要为每个 GPU 创建 MIG 实例,创建GI实例,即我们所需要的子GPU,首先我们产看一下可以创建些什么样的子GPU

查看 0 号 GPU 显卡支持的 MIG 规格

bash 复制代码
# nvidia-smi mig -i 0 -lgip
+-----------------------------------------------------------------------------+
| GPU instance profiles:                                                      |
| GPU   Name             ID    Instances   Memory     P2P    SM    DEC   ENC  |
|                              Free/Total   GiB              CE    JPEG  OFA  |
|=============================================================================|
|   0  MIG 1g.10gb       19     0/7        9.50       No     14     0     0   |
|                                                             1     0     0   |
+-----------------------------------------------------------------------------+
|   0  MIG 1g.10gb+me    20     0/1        9.50       No     14     1     0   |
|                                                             1     1     1   |
+-----------------------------------------------------------------------------+
|   0  MIG 1g.20gb       15     0/4        19.50      No     14     1     0   |
|                                                             1     0     0   |
+-----------------------------------------------------------------------------+
|   0  MIG 2g.20gb       14     0/3        19.50      No     28     1     0   |
|                                                             2     0     0   |
+-----------------------------------------------------------------------------+
|   0  MIG 3g.40gb        9     0/2        39.25      No     42     2     0   |
|                                                             3     0     0   |
+-----------------------------------------------------------------------------+
|   0  MIG 4g.40gb        5     0/1        39.25      No     56     2     0   |
|                                                             4     0     0   |
+-----------------------------------------------------------------------------+
|   0  MIG 7g.80gb        0     0/1        78.75      No     98     5     0   |
|                                                             7     1     1   |
+-----------------------------------------------------------------------------+

创建 GI 实例

可以看到 0 号显卡最多支持 7g.80gb 资源,我们可以对资源进行拆分,只要总和不超过这个最大值既可。

例如在第 0 块显卡创建 GPU 的计算核心数量为 3g,显存 40gb 的资源和一个计算核心 4g,显存为 40gb 的资源。

bash 复制代码
# nvidia-smi mig -cgi 3g.40gb,4g.40gb -i 0
Successfully created GPU instance ID  1 on GPU  0 using profile MIG 3g.40gb (ID  9)
Successfully created GPU instance ID  2 on GPU  0 using profile MIG 4g.40gb (ID  5)

验证实例创建情况:

bash 复制代码
# nvidia-smi mig -lgi
+-------------------------------------------------------+
| GPU instances:                                        |
| GPU   Name             Profile  Instance   Placement  |
|                          ID       ID       Start:Size |
|=======================================================|
|   0  MIG 3g.40gb          9        1          4:4     |
+-------------------------------------------------------+
|   0  MIG 4g.40gb          5        2          0:4     |
+-------------------------------------------------------+

如需清除创建的 mig 实例

bash 复制代码
# nvidia-smi mig -dgi -i 0  # 删除GPU0上的所有 GPU 实例

创建CI实例

上述操作只创建了GI,这些GI并不能直接使用,

查看物理卡的状态

bash 复制代码
# nvidia-smi -i 0
Tue Jan  7 13:31:24 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| 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 A100-SXM4-80GB          On  |   00000000:3F:00.0 Off |                   On |
| N/A   28C    P0             53W /  400W |      87MiB /  81920MiB |     N/A      Default |
|                                         |                        |              Enabled |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| MIG devices:                                                                            |
+------------------+----------------------------------+-----------+-----------------------+
| GPU  GI  CI  MIG |                     Memory-Usage |        Vol|      Shared           |
|      ID  ID  Dev |                       BAR1-Usage | SM     Unc| CE ENC DEC OFA JPG    |
|                  |                                  |        ECC|                       |
|==================+==================================+===========+=======================|
|  No MIG devices found                                                                   |
+-----------------------------------------------------------------------------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

显示No MIG devices found,因为它没有创建对应的计算实例CI,因此还需要创建 CI。

查看CI的profile 信息

bash 复制代码
# nvidia-smi mig -i 0 -lcip
+--------------------------------------------------------------------------------------+
| Compute instance profiles:                                                           |
| GPU     GPU       Name             Profile  Instances   Exclusive       Shared       |
|       Instance                       ID     Free/Total     SM       DEC   ENC   OFA  |
|         ID                                                          CE    JPEG       |
|======================================================================================|
|   0      1       MIG 1c.3g.40gb       0      3/3           14        2     0     0   |
|                                                                      3     0         |
+--------------------------------------------------------------------------------------+
|   0      1       MIG 2c.3g.40gb       1      1/1           28        2     0     0   |
|                                                                      3     0         |
+--------------------------------------------------------------------------------------+
|   0      1       MIG 3g.40gb          2*     1/1           42        2     0     0   |
|                                                                      3     0         |
+--------------------------------------------------------------------------------------+
|   0      2       MIG 1c.4g.40gb       0      4/4           14        2     0     0   |
|                                                                      4     0         |
+--------------------------------------------------------------------------------------+
|   0      2       MIG 2c.4g.40gb       1      2/2           28        2     0     0   |
|                                                                      4     0         |
+--------------------------------------------------------------------------------------+
|   0      2       MIG 4g.40gb          3*     1/1           56        2     0     0   |
|                                                                      4     0         |
+--------------------------------------------------------------------------------------+

在 GPU 0 的 GPU Instance 1 和 2 上分别创建 Compute Instances(CI),指定了两个 CI Profiles:01。 也就是1c.3g.40gb 和2c.3g.40gb

bash 复制代码
# nvidia-smi mig -i 0 -cci 0,1 -gi 1
Successfully created compute instance ID  0 on GPU  0 GPU instance ID  1 using profile MIG 1c.3g.40gb (ID  0)
Successfully created compute instance ID  1 on GPU  0 GPU instance ID  1 using profile MIG 2c.3g.40gb (ID  1)
# nvidia-smi mig -i 0 -cci 0,1 -gi 2
Successfully created compute instance ID  0 on GPU  0 GPU instance ID  2 using profile MIG 1c.4g.40gb (ID  0)
Successfully created compute instance ID  1 on GPU  0 GPU instance ID  2 using profile MIG 2c.4g.40gb (ID  1)

查看创建的设备信息

bash 复制代码
# nvidia-smi -i 0
Tue Jan  7 13:45:24 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| 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 A100-SXM4-80GB          On  |   00000000:3F:00.0 Off |                   On |
| N/A   28C    P0             53W /  400W |      87MiB /  81920MiB |     N/A      Default |
|                                         |                        |              Enabled |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| MIG devices:                                                                            |
+------------------+----------------------------------+-----------+-----------------------+
| GPU  GI  CI  MIG |                     Memory-Usage |        Vol|      Shared           |
|      ID  ID  Dev |                       BAR1-Usage | SM     Unc| CE ENC DEC OFA JPG    |
|                  |                                  |        ECC|                       |
|==================+==================================+===========+=======================|
|  0    1   0   0  |              37MiB / 40192MiB    | 14      0 |  3   0    2    0    0 |
|                  |                 0MiB / 65535MiB  |           |                       |
+------------------+                                  +-----------+-----------------------+
|  0    1   1   1  |                                  | 28      0 |  3   0    2    0    0 |
|                  |                                  |           |                       |
+------------------+----------------------------------+-----------+-----------------------+
|  0    2   0   2  |              49MiB / 40192MiB    | 14      0 |  4   0    2    0    0 |
|                  |                 0MiB / 65535MiB  |           |                       |
+------------------+                                  +-----------+-----------------------+
|  0    2   1   3  |                                  | 28      0 |  4   0    2    0    0 |
|                  |                                  |           |                       |
+------------------+----------------------------------+-----------+-----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

查看资源规格列表

bash 复制代码
# nvidia-smi -L
GPU 0: NVIDIA A100-SXM4-80GB (UUID: GPU-59227d3b-92c1-9394-2694-e5d2d09d7ca6)
  MIG 1c.3g.40gb  Device  0: (UUID: MIG-ea8d189c-bcdb-519a-a97e-8a2e7848bc1c)
  MIG 2c.3g.40gb  Device  1: (UUID: MIG-28722365-8992-5bc1-bc81-c8bd010e507c)
  MIG 1c.4g.40gb  Device  2: (UUID: MIG-54f0f6f3-0c7c-5087-b810-9770732e8591)
  MIG 2c.4g.40gb  Device  3: (UUID: MIG-b8753643-33b5-589f-bc5f-da5995b5674c)

如需清除创建的 ci 实例

bash 复制代码
# nvidia-smi mig -dci -i 0  # 删除GPU0上的所有CI实例

同时创建 CI+GI 实例

如果要直接创建一个子GPU 并且将其可用的运算单元都挂载上,在创建GI的时候添加-C参数即可完成。

bash 复制代码
# nvidia-smi mig -i 1 -cgi 2g.20gb,2g.20gb,1g.10gb -C
Successfully created GPU instance ID  3 on GPU  1 using profile MIG 2g.20gb (ID 14)
Successfully created compute instance ID  0 on GPU  1 GPU instance ID  3 using profile MIG 2g.20gb (ID  1)
Successfully created GPU instance ID  5 on GPU  1 using profile MIG 2g.20gb (ID 14)
Successfully created compute instance ID  0 on GPU  1 GPU instance ID  5 using profile MIG 2g.20gb (ID  1)
Successfully created GPU instance ID  9 on GPU  1 using profile MIG 1g.10gb (ID 19)
Successfully created compute instance ID  0 on GPU  1 GPU instance ID  9 using profile MIG 1g.10gb (ID  0)

查看资源规格列表

bash 复制代码
# nvidia-smi -L                                                                                                                                                                            
GPU 0: NVIDIA A100-SXM4-80GB (UUID: GPU-59227d3b-92c1-9394-2694-e5d2d09d7ca6)                                                                                                                                   
  MIG 1c.3g.40gb  Device  0: (UUID: MIG-ea8d189c-bcdb-519a-a97e-8a2e7848bc1c)                                                                                                                                   
  MIG 2c.3g.40gb  Device  1: (UUID: MIG-28722365-8992-5bc1-bc81-c8bd010e507c)                                                                                                                                   
  MIG 1c.4g.40gb  Device  2: (UUID: MIG-54f0f6f3-0c7c-5087-b810-9770732e8591)                                                                                                                                   
  MIG 2c.4g.40gb  Device  3: (UUID: MIG-b8753643-33b5-589f-bc5f-da5995b5674c)                                                                                                                                   
GPU 1: NVIDIA A100-SXM4-80GB (UUID: GPU-13d510f6-4f49-f720-697a-91908f12e2d6)                                                                                                                                   
  MIG 2g.20gb     Device  0: (UUID: MIG-593bb78a-c62d-51f5-970b-5056771ec6fb)                                                                                                                                   
  MIG 2g.20gb     Device  1: (UUID: MIG-658e3359-4288-54e8-bb01-98ae7c9448ea)                                                                                                                                   
  MIG 1g.10gb     Device  2: (UUID: MIG-6b765df0-e1a2-54ab-9525-224e8d14ce0a)

更新 nvidia-device-plugin 配置

migStrategy策略分为以下几种:

none策略禁用 MIG

single策略 每个 GPU 只能创建一个 MIG 实例。

mixed策略 允许创建多个不同的 MIG 实例。

bash 复制代码
# helm upgrade nvdp -n nvidia-device-plugin . -f values.yaml --set migStrategy=mixed

验证 Kubernetes识别 MIG 实例

执行以下命令,确认 Kubernetes 节点上已经注册了 GPU 资源,如果数据未更新,可重启nvidia-device-plugin 后再次查看。

bash 复制代码
# kubectl describe node gpu-1 | grep mig
  nvidia.com/mig-1g.10gb:  1
  nvidia.com/mig-2g.20gb:  2
  nvidia.com/mig-3g.40gb:  2
  nvidia.com/mig-4g.40gb:  2
  nvidia.com/mig-1g.10gb:  1
  nvidia.com/mig-2g.20gb:  2
  nvidia.com/mig-3g.40gb:  2
  nvidia.com/mig-4g.40gb:  2

配置 Pod 使用 MIG 实例

编写一个 Pod 配置文件来使用 MIG 资源:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: mig-demo-pod
spec:
  containers:
  - name: cuda-container
    image: nvidia/cuda:11.8.0-base
    resources:
      limits:
        nvidia.com/mig-3g.40gb: 1
    command: ["nvidia-smi"]

DCGM exporter 部署

参考文档:https://github.com/NVIDIA/dcgm-exporter

安装 exporter

helm 部署 exporter

bash 复制代码
# helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts
# helm pull gpu-helm-charts/dcgm-exporter --untar
# cd dcgm-exporter/
# vim values.yaml
nodeSelector:  # 添加nvidia节点标签调度
  nvidia.com/gpu.present: "true"
kubernetes: # 包含pod标签和uuid
  enablePodLabels: true
  enablePodUID: true
# helm install dcgm-exporter -n nvidia-device-plugin . -f values.yaml 
NAME: dcgm-exporter
LAST DEPLOYED: Wed Aug 27 15:33:29 2025
NAMESPACE: nvidia-device-plugin
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods -n nvidia-device-plugin -l "app.kubernetes.io/name=dcgm-exporter,app.kubernetes.io/instance=dcgm-exporter" -o jsonpath="{.items[0].metadata.name}")
  kubectl -n nvidia-device-plugin port-forward $POD_NAME 8080:9400 &
  echo "Visit http://127.0.0.1:8080/metrics to use your application"

查看验证

bash 复制代码
# kubectl get pod -n nvidia-device-plugin
NAME                              READY   STATUS    RESTARTS   AGE
nvdp-nvidia-device-plugin-6mnv4   1/1     Running   0          20m
dcgm-exporter-8d2db               1/1     Running   0          2m53s
# kubectl get svc -n nvidia-device-plugin 
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
dcgm-exporter   ClusterIP   10.107.116.236   <none>        9400/TCP   36s

创建ServiceMonitor

yaml 复制代码
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: dcgm-exporter
  namespace: monitoring
spec:
  jobLabel: dcgm-exporter
  endpoints:
  - interval: 30s
    port: metrics
    path: /metrics
    scheme: http
    relabelings: # 新增ip标签方便关联查询
    - action: replace
      regex: (.*)
      replacement: $1
      sourceLabels:
      - __meta_kubernetes_pod_host_ip
      targetLabel: ip
  selector: # svc标签选择器,匹配service的labels
    matchLabels:
      app.kubernetes.io/component: dcgm-exporter
      app.kubernetes.io/instance: dcgm-exporter
      app.kubernetes.io/name: dcgm-exporter
  namespaceSelector: # namespace选择
    matchNames:
    - nvidia-device-plugin

查看 prometheus targets

导入 dashboard

参考 dashboard:https://grafana.com/grafana/dashboards/12239-nvidia-dcgm-exporter-dashboard/

常用 promql

GPU 常用指标可参考文档:https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/introduction-to-metrics#section-ts3-5l7-lwm

  • 总 GPU 数
bash 复制代码
count(DCGM_FI_DEV_DEC_UTIL)
  • 空闲 GPU 数
bash 复制代码
DCGM_FI_DEV_DEC_UTIL{exported_namespace=""}
  • 各 deployment 资源使用量
bash 复制代码
count(DCGM_FI_DEV_DEC_UTIL{})by(exported_namespace)
  • 各 deployment 资源使用率
bash 复制代码
avg by (exported_namespace, deployment) (
  label_replace(
    DCGM_FI_DEV_GPU_UTIL{},
    "deployment",
    "$1",
    "exported_pod",
    "^(.*)-[^-]+-[^-]+$"
  )
)

禁用GPU

如果单个 GPU 硬件出现故障,为了避免将资源调度到异常 GPU 运行,可以将故障 GPU 禁用。

bash 复制代码
1. 查询 GPU 的编号
# nvidia-smi
2. 将指定的 GPU(0000:D0:00.0)设置为排空状态,并在 1 秒内完成该操作。
# nvidia-smi drain -p 0000:D0:00.0 -m 1
3. 此命令将指定的 PCI 设备(0000:d0:00.0)从系统中移除。
echo "1" | sudo tee /sys/bus/pci/devices/0000:d0:00.0/remove
3. 启用指定的 PCI 设备(0000:d0:00.0)。重新启用设备并恢复其工作状态。
echo "0" | sudo tee /sys/bus/pci/devices/0000:d0:00.0/enable
4. 将指定的 PCI 设备(0000:d0:00.0)从 NVIDIA 驱动程序中解绑。
echo -n "0000:d0:00.0" | sudo tee /sys/bus/pci/drivers/nvidia/unbind
相关推荐
sunburn-4 小时前
Java 队列全面详解:从入门到面试实战
java·开发语言·汇编·ide·idea
vx-程序开发4 小时前
springboot农产品运输服务平台---附源码75498
java·javascript·spring boot·python·eclipse·django·php
zzh___zzh5 小时前
SQL 窗口函数 ROW_NUMBER、RANK、DENSE_RANK 常用方法笔记
java
计科土狗6 小时前
GESP六级专题之类与对象
java·前端·数据库
Slow菜鸟6 小时前
第3篇:实操落地篇 · 3套企业标准工作流(IDEA可视化版)
java·ide·intellij-idea
weixin_440784116 小时前
Java基础常见题
java·开发语言·python·java基础
掉鱼的猫6 小时前
Hot-Plug 实战:运行期管理 Solon 插件
java·hotfix
markinmarkin7 小时前
Spring 中有哪些方式可以把Bean 注入到IOC 容器?
java·spring
风曦Kisaki7 小时前
# Kubernetes(K8s)笔记Day12 :K8s 七层代理(Ingress 和 Ingress Controller)
linux·笔记·云原生·容器·kubernetes
带刺的坐椅7 小时前
Hot-Plug 实战:运行期管理 Solon 插件
java·solon·插件·热插拔