GPU虚拟化

引言

现有如下环境(注意相关配置:只有一个k8s节点,且该节点上只有一张GPU卡):

text 复制代码
// k8s版本
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.7", GitCommit:"b56e432f2191419647a6a13b9f5867801850f969", GitTreeState:"clean", BuildDate:"2022-02-16T11:50:27Z", GoVersion:"go1.16.14", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.7", GitCommit:"b56e432f2191419647a6a13b9f5867801850f969", GitTreeState:"clean", BuildDate:"2022-02-16T11:43:55Z", GoVersion:"go1.16.14", Compiler:"gc", Platform:"linux/amd64"}

// k8s节点信息
$ kubectl get node
NAME              STATUS   ROLES                  AGE    VERSION
desktop-72rd6ov   Ready    control-plane,master   419d   v1.22.7
$ kubectl get node desktop-72rd6ov -oyaml | grep nvidia.com -A 1 -B 6
  allocatable:
    cpu: "16"
    ephemeral-storage: "972991057538"
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 16142536Ki
    nvidia.com/gpu: "1"
    pods: "110"
  capacity:
    cpu: "16"
    ephemeral-storage: 1055762868Ki
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 16244936Ki
    nvidia.com/gpu: "1"
    pods: "110"

// nvidia k8s-device-plugin版本
// nvidia k8s-device-plugin使用默认配置运行
root@nvidia-device-plugin-daemonset-wtqrg:/# nvidia-device-plugin --version
NVIDIA Device Plugin version 42a0fa92
commit: 42a0fa92ce166592ab5702a1143ddecd891c8e5e

// nvidia-container-toolkit版本
$ nvidia-container-toolkit --version
NVIDIA Container Runtime Hook version 1.17.4
commit: 9b69590c7428470a72f2ae05f826412976af1395

// nvidia GPU及driver信息
$ nvidia-smi
Mon Jun  2 10:51:49 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 560.35.02              Driver Version: 560.94         CUDA Version: 12.6     |
|-----------------------------------------+------------------------+----------------------+
| 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 RTX 4060 Ti     On  |   00000000:01:00.0  On |                  N/A |
|  0%   42C    P8              8W /  165W |     690MiB /  16380MiB |      3%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A        25      G   /Xwayland                                   N/A      |
+-----------------------------------------------------------------------------------------+

// cuda版本
$ nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2024 NVIDIA Corporation
Built on Tue_Oct_29_23:50:19_PDT_2024
Cuda compilation tools, release 12.6, V12.6.85
Build cuda_12.6.r12.6/compiler.35059454_0

在上述单节点的k8s环境中,我先用如下配置了使用GPU的yaml起一个pod:

yaml 复制代码
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod-1
spec:
  restartPolicy: Never
  containers:
    - name: nginx
      image: nginx:latest
      imagePullPolicy: IfNotPresent
      resources:
        limits:
          nvidia.com/gpu: 1
      securityContext:
        capabilities:
          add: ["SYS_ADMIN"]
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
    operator: Exists

可以看到pod正常运行:

text 复制代码
$ kubectl apply -f nginx-pod.yaml
pod/gpu-pod-1 created
$ kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP             NODE              NOMINATED NODE   READINESS GATES
gpu-pod-1   1/1     Running   0          64s   10.244.0.221   desktop-72rd6ov   <none>           <none>
$ kubectl exec -ti gpu-pod-1 -- bash
root@gpu-pod-1:/# nvidia-smi --version
NVIDIA-SMI version  : 560.35.02
NVML version        : 560.35
DRIVER version      : 560.94
CUDA Version        : 12.6

如果再用上述yaml起一个gpu-pod-2,会发现pod一直Pending,因为节点上已经没有剩余可用的GPU资源可用:

text 复制代码
$ kubectl apply -f nginx-pod.yaml
pod/gpu-pod-2 created
$ kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE     IP             NODE              NOMINATED NODE   READINESS GATES
gpu-pod-1   1/1     Running   0          13m     10.244.0.221   desktop-72rd6ov   <none>           <none>
gpu-pod-2   0/1     Pending   0          4m20s   <none>         <none>            <none>           <none>

但是试想一种场景:我有多个任务需要使用GPU资源计算,但是GPU资源数量明显少于任务数量,而且某些任务运行过程中也并不是一直会使用GPU资源。为了充分利用GPU资源,我们希望任务能公用这些GPU资源,这也就引出了本文的主题:GPU虚拟化。

一、GPU虚拟化技术概览

1.1 为什么需要GPU虚拟化?

1.1.1 物理GPU的固有特点

  • 资源利用率低下:传统GPU独占模式下,AI推理/轻量计算任务仅占用10%-30%算力,大量资源闲置
  • 成本与扩展性瓶颈:企业需为每个用户/应用单独采购GPU硬件(如设计师工作站);云服务商无法通过共享降低租户算力成本
  • 多租户隔离缺失:多个任务竞争同一GPU导致性能抖动(如显存溢出影响邻位应用)

1.2 两大核心模式对比

1.2.1 一虚多(1 GPU → N个实例)

本质:单物理GPU分割为多个逻辑虚拟GPU

实现原理:

  • 时分复用(Time-Slicing):GPU时间片轮转调度(如每10ms切换任务),代表方案:NVIDIA vGPU、开源GPU-PV,适用场景:图形渲染、轻量计算
  • 空间分区(Spatial Partitioning):物理切割GPU计算单元/显存(如NVIDIA MIG),适用场景:AI推理、高隔离性任务

架构示意图:

text 复制代码
Physical GPU → Hypervisor层(vGPU Manager)→ 虚拟GPU实例(vGPU1/vGPU2/...) → VM/Container

1.2.2 多虚一(N GPU → 1实例)

本质:聚合多个GPU资源服务单个计算密集型应用

实现原理:

  • 设备级聚合:通过NVLink互联多卡(如DGX服务器的8-GPU Cube Mesh)
  • 节点级聚合:GPUDirect RDMA跨节点通信(InfiniBand网络)

典型架构:

复制代码
App (e.g. LLM训练) → 聚合框架 (NCCL/DDP) → GPU Pool (本地多卡/跨节点集群)

1.2.3 关键特性对比表

维度 一虚多 (1→N) 多虚一 (N→1)
核心目标 资源分片共享 算力聚合加速
隔离性 中-高(MIG为物理隔离) 无(所有资源协同工作)
延迟敏感性 低(毫秒级调度) 高(微秒级通信延迟影响显著)
典型硬件 Tesla T4/vWS, A100 (MIG) A100/H100 + NVLink Switch
适用场景 云游戏、VDI、AI推理 大模型训练、科学计算
代表技术 NVIDIA vGPU, MIG, GPU-PV NVLink, NCCL, GPUDirect RDMA

1.2.4 技术演进里程碑

  • 2013:NVIDIA GRID K1首发vGPU技术(针对虚拟桌面)
  • 2020:安培架构推出MIG(首个硬件级多实例GPU)
  • 2022:Hopper架构支持机密计算vGPU(加密显存保护数据)
  • 2023:vGPU 2.0支持动态资源分配(运行时调整vGPU显存/算力)

二、一虚多(1 GPU → N个实例)技术解析

2.1 实现原理

2.1.1 硬件辅助虚拟化(NVIDIA vGPU 技术栈):

核心组件:

  • Hypervisor驱动与调度器:GPU内部的任务队列管理单元(如Ampere架构的GSP调度引擎)
  • vGPU Manager:驻留在Hypervisor的驱动层,负责GPU资源切分与调度
  • Guest驱动:虚拟机/容器内识别虚拟GPU的标准驱动(与物理驱动兼容)

2.1.2 资源分割的两大范式

类型 时分复用(Time-Slicing) 空间分区(MIG)
原理 GPU时间片轮转服务多个任务 物理切割GPU为独立计算单元
隔离级别 软件级(易受干扰) 硬件级(显存/缓存/计算单元隔离)
调度粒度 毫秒级(通常10-50ms) 永久性分区(需重启生效)
代表技术 NVIDIA vGPU, GPU-PV NVIDIA MIG(仅安培/霍珀架构)
适用场景 图形渲染、轻量级AI推理 高SLA要求的AI推理/科学计算

2.2 主流方案简介

2.2.1 NVIDIA vGPU/vWS(企业级方案)

架构流程:

text 复制代码
GPU硬件分片 → 虚拟GPU(vGPU) → 虚拟机

关键特性:

  • Profile配置表(以NVIDIA A100 40GB为例)
Profile名称 显存 CUDA核心 最大实例数
A100-1B 1GB 1/7 7
A100-2B 2GB 1/7 7
A100-3B 3GB 1/7 4
A100-4B 4GB 1/7 3
A100-7B 7GB 1/7 1
  • 许可证机制:

需连接nvidia-licence-server(默认端口7070)

许可证类型:vWS(图形工作站)、vCS(计算加速)、vPC(基础办公)

2.2.2 NVIDIA MIG(Multi-Instance GPU)(硬件级隔离方案):

物理切割原理:

  • 安培架构GPU(如A100)含7个GPC(图形处理集群)
  • 每个MIG实例独占:
    • 独立计算单元(SMs子集)
    • 专用L2缓存切片
    • 隔离的显存通道

7种实例规格(A100 40GB):

限制:总实例数≤7,显存总和≤物理显存

实例类型 算力占比 显存 适用场景
1g.5gb 1/7 5GB 轻量推理
1g.10gb 1/7 10GB 中等模型推理
2g.20gb 2/7 20GB 大模型推理
3g.40gb 3/7 40GB 训练/高负载推理

2.2.3 开源方案:GPU-PV(容器/虚拟机场景轻量虚拟化)(如Kubevirt + vGPU)

实现原理:

  • 通过Kubernetes Device Plugin暴露分时GPU
  • 无硬件虚拟化支持,依赖CUDA MPS(多进程服务)

2.3 性能隔离与QoS保障

资源类型 隔离方案 技术实现
计算单元 vGPU:时间片轮转 MIG:物理隔离 GPU调度器(GSP) 硬件分区
显存 静态分区 每个vGPU/MIG实例固定显存配额
显存带宽 令牌桶算法限流 NVIDIA Frame Rate Limiter (FRL)
PCIe带宽 权重分配 SR-IOV VF流量控制

三、多虚一(N GPU → 1实例)技术概览

3.1 核心应用场景

场景 需求特征 典型案例
大语言模型训练 千亿参数加载/显存需求>80GB GPT-4训练需128张H100 GPU集群
科学计算仿真 双精度浮点性能>100 TFLOPS CFD流体模拟(10亿网格粒子)
实时渲染农场 4K@120fps实时光线追踪 电影级场景渲染(《阿凡达2》)
基因组学分析 TB级数据并行处理 癌症基因组序列比对

3.2 核心实现方案

3.2.1 硬件互联层:打破通信瓶颈

NVLink拓扑架构(以DGX H100为例):

  • 全连接带宽:900 GB/s(NVLink 4.0)
  • 8卡互连延迟:<500 ns
  • 对比PCIe 5.0:仅128 GB/s,延迟>2 μs

GPUDirect RDMA关键技术:

  • 绕过CPU直接访问远端GPU显存
  • 支持InfiniBand/ROCEv2网络(要求≥100 Gbps)
  • 带宽利用率达95%(传统TCP/IP仅40%)

3.2.2 软件聚合层:并行计算框架

框架 通信机制 适用场景
PyTorch DDP 环状AllReduce 动态图模型训练
TensorFlow MirroredStrategy Hierarchical Copy 静态图分布式训练
DeepSpeed ZeRO 显存分片+梯度聚合 千亿参数模型训练
NVIDIA NCCL GPU-GPU直接通信 底层集合通信库

3.3 性能优化关键

3.3.1 通信-计算重叠

梯度压缩:

  • FP16混合精度(节省50%通信量)
  • 1-bit Adam(通信量降至1/32)

流水线并行(Pipeline Parallelism):

  • 微批次(Micro-batching)隐藏通信延迟
  • Megatron-LM实现千卡训练效率>52%

3.3.2 拓扑感知调度

NVIDIA DGX SuperPOD架构:

  • 32节点(256 GPU)通过InfiniBand分层交换
  • 通信热点区域带宽保障

Kubernetes调度策略:

yaml 复制代码
# 要求GPU同节点拓扑
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.kubernetes.io/zone
          operator: In
          values: [ "nvlink-group-1" ]

3.4 与一虚多技术的协同模式

混合架构案例:AI云服务平台:

优势:

  • 推理任务:MIG隔离保障SLA
  • 训练任务:多虚一突破单卡限制

资源调度:Kubernetes实现自动扩缩容

四、GPU虚拟化示例

基于文章开头单节点单GPU无法部署多个使用GPU的pod场景,来看看可以怎么操作实现多pod使用一块GPU。这里我们使用Time-Slicing方案,官方参考文档:

About Configuring GPU Time-Slicing

Shared Access to GPUs

4.1 准备configMap

准备如下configMap用于Time-Slicing配置:

yaml 复制代码
# cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nvidia-device-plugin-config
  namespace: kube-system
data:
  config.yaml: |
    version: v1
    flags:
      migStrategy: none
      failOnInitError: true
      nvidiaDriverRoot: /
    sharing:
      timeSlicing:
        resources:
        - name: nvidia.com/gpu
          replicas: 5  # 将1个GPU拆分成5个虚拟实例,如果有n个GPU,则会拆分成5*n个实例

创建该configMap:

text 复制代码
$ kubectl apply -f cm.yaml
configmap/nvidia-device-plugin-config created

4.2 修改nvidia k8s-device-plugin(daemonSet)

使用kubectl -n kube-system edit ds nvidia-device-plugin-daemonset命令修改nvidia k8s-device-plugin配置:

yaml 复制代码
...
spec:
  template:
    spec:
      - containers:
        name: nvidia-device-plugin-ctr
        args: # 新增args配置
        - --config-file=/etc/nvidia-device-plugin/config.yaml
        ...
        volumeMounts:
        - mountPath: /etc/nvidia-device-plugin # 新增configMap的mountPath
          name: device-plugin-config
      ...
      volumes:
      - name: device-plugin-config # 新增configMap volume
        configMap:
          name: nvidia-device-plugin-config
...

修改完daemonSet pod会自动重建:

text 复制代码
$ kubectl -n kube-system get pod | grep nvidia
nvidia-device-plugin-daemonset-nn9dc      1/1     Running   0              47s

4.3 查看node上的GPU资源

可以看到nvidia k8s-device-plugin完成了GPU资源的上报,并把GPU资源数量修改为了5个(物理层面只有一个)。

text 复制代码
$ kubectl get node
NAME              STATUS   ROLES                  AGE    VERSION
desktop-72rd6ov   Ready    control-plane,master   425d   v1.22.7

$ kubectl get node desktop-72rd6ov -oyaml| grep nvidia.com -A 1 -B 6
  allocatable:
    cpu: "16"
    ephemeral-storage: "972991057538"
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 16142544Ki
    nvidia.com/gpu: "5"
    pods: "110"
  capacity:
    cpu: "16"
    ephemeral-storage: 1055762868Ki
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 16244944Ki
    nvidia.com/gpu: "5"
    pods: "110"

4.4 创建pod验证

还是使用文章开头的nginx pod yaml创建多个pod:

text 复制代码
// 先删除旧pod
$ kubectl delete pod gpu-pod-1
pod "gpu-pod-1" deleted
$ kubectl delete pod gpu-pod-2
pod "gpu-pod-2" deleted

// 修改yaml中pod名称重新创建6个
$ kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP             NODE              NOMINATED NODE   READINESS GATES
gpu-pod-1   1/1     Running   0          41s   10.244.0.236   desktop-72rd6ov   <none>           <none>
gpu-pod-2   1/1     Running   0          31s   10.244.0.237   desktop-72rd6ov   <none>           <none>
gpu-pod-3   1/1     Running   0          24s   10.244.0.238   desktop-72rd6ov   <none>           <none>
gpu-pod-4   1/1     Running   0          19s   10.244.0.239   desktop-72rd6ov   <none>           <none>
gpu-pod-5   1/1     Running   0          12s   10.244.0.240   desktop-72rd6ov   <none>           <none>
gpu-pod-6   0/1     Pending   0          6s    <none>         <none>            <none>           <none>

// 进入2个pod验证
$ kubectl exec -ti gpu-pod-1 -- bash
root@gpu-pod-1:/# nvidia-smi --version
NVIDIA-SMI version  : 560.35.02
NVML version        : 560.35
DRIVER version      : 560.94
CUDA Version        : 12.6

$ kubectl exec -ti gpu-pod-4 -- bash
root@gpu-pod-4:/# nvidia-smi --version
NVIDIA-SMI version  : 560.35.02
NVML version        : 560.35
DRIVER version      : 560.94
CUDA Version        : 12.6
root@gpu-pod-4:/#

总结

本文简单介绍了nvidia GPU虚拟化的常见方法,包含"一虚多"和"多虚一"场景。以"一虚多"为例,本文以nvidia k8s-device-plugin支持的Time-Slicing方案演示了多个pod使用一张GPU的应用,但实际使用过程中,Time-Slicing存在多应用互相影响的风险,在某些业务场景是无法接受的,生产中一般会按cuda核心和显存大小切割成不同的实例避免应用相互影响,但这又可能降低GPU资源的利用率,总之针对不同场景、不同应用可能需要选择不同的GPU虚拟化方案,GPU虚拟化也有待更深入地探索。

相关推荐
探索云原生2 小时前
Kubernetes 官方出品:一个 Controller 搞定 Job 排队和资源配额
ai·云原生·kubernetes·scheduler
BullSmall4 小时前
Sonarqube9.9 docker 镜像启动后 conf 下的配置文件没有了
docker·容器·eureka
ruofu334 小时前
wsl端是py312,但是项目是py311, 如何下载py311的依赖包(wheels)
python·docker
spider_xcxc5 小时前
Docker 实战:容器化多服务应用,Dockerfile 与 Compose 配置全解析
docker·云原生·eureka·虚拟化·容器化
hay_lee6 小时前
Kubernetes StatefulSet:OrderedReady 极简指南
云原生·容器·kubernetes
晚风吹长发6 小时前
Docker使用——Docker容器及相关命令
linux·运维·服务器·docker·容器·架构
ShiXZ2136 小时前
Docker Compose 安装与配置指南
运维·docker·容器
BullSmall7 小时前
Anolis OS 8.10 Docker 部署 SonarQube 9.9 完整教程
运维·docker·容器
成为你的宁宁8 小时前
【K8s HPA 水平扩缩容】
kubernetes·hpa
开发者联盟league8 小时前
vscode 进入docker容器
vscode·docker