引言
現有如下環境(注意相關配置:只有一個k8s節點,且該節點上只有一張GPU卡):
// 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 6allocatable:cpu: "16"ephemeral-storage: "972991057538"hugepages-1Gi: "0"hugepages-2Mi: "0"memory: 16142536Kinvidia.com/gpu: "1"pods: "110"capacity:cpu: "16"ephemeral-storage: 1055762868Kihugepages-1Gi: "0"hugepages-2Mi: "0"memory: 16244936Kinvidia.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:
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:name: gpu-pod-1
spec:restartPolicy: Nevercontainers:- name: nginximage: nginx:latestimagePullPolicy: IfNotPresentresources:limits:nvidia.com/gpu: 1securityContext:capabilities:add: ["SYS_ADMIN"]tolerations:- key: nvidia.com/gpuoperator: Existseffect: NoSchedule- effect: NoSchedulekey: node-role.kubernetes.io/masteroperator: Exists
可以看到pod正常運行:
$ 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資源可用:
$ 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推理、高隔離性任務
架構示意圖:
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(企業級方案)
架構流程:
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調度策略:
# 要求GPU同節點拓撲
affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: topology.kubernetes.io/zoneoperator: Invalues: [ "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配置:
# cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: nvidia-device-plugin-confignamespace: kube-system
data:config.yaml: |version: v1flags:migStrategy: nonefailOnInitError: truenvidiaDriverRoot: /sharing:timeSlicing:resources:- name: nvidia.com/gpureplicas: 5 # 將1個GPU拆分成5個虛擬實例,如果有n個GPU,則會拆分成5*n個實例
創建該configMap:
$ 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配置:
...
spec:template:spec:- containers:name: nvidia-device-plugin-ctrargs: # 新增args配置- --config-file=/etc/nvidia-device-plugin/config.yaml...volumeMounts:- mountPath: /etc/nvidia-device-plugin # 新增configMap的mountPathname: device-plugin-config...volumes:- name: device-plugin-config # 新增configMap volumeconfigMap:name: nvidia-device-plugin-config
...
修改完daemonSet pod會自動重建:
$ 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個(物理層面只有一個)。
$ 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 6allocatable:cpu: "16"ephemeral-storage: "972991057538"hugepages-1Gi: "0"hugepages-2Mi: "0"memory: 16142544Kinvidia.com/gpu: "5"pods: "110"capacity:cpu: "16"ephemeral-storage: 1055762868Kihugepages-1Gi: "0"hugepages-2Mi: "0"memory: 16244944Kinvidia.com/gpu: "5"pods: "110"
4.4 創建pod驗證
還是使用文章開頭的nginx pod yaml創建多個pod:
// 先刪除舊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虛擬化也有待更深入地探索。