Skip to content
Published on

GPU Operator × KubeVirt 完全整理 — 组件、配置、版本,从部分 MIG 到手动 MIG

分享
Authors

引言 — 容器的 GPU,虚拟机的 GPU

Kubernetes 上的 GPU 基础设施分成两个世界。一个是把 GPU 插进容器的世界(GPU Operator),另一个是在 Kubernetes 上运行虚拟机、把 GPU 整块交出去的世界(KubeVirt)。有意思的是,这两者最终会在同一个技术栈里相遇——GPU Operator 也负责为 KubeVirt 虚拟机做 GPU 供给。本文把两大支柱的组件、配置、版本做一次完整整理,并用实战命令处理两个常见问题——只对部分 GPU 应用 MIG,以及手动操作 MIG。先读一遍GPU Operator 安装与 MIG 基础篇,本文会更容易理解一倍。

第一部分 — GPU Operator:组件、配置、版本

组件(Operand)

GPU Operator 是一个把"GPU 节点所需的一切"以 DaemonSet operand 形式部署、并用一个 ClusterPolicy 统一调度的 operator。

Operand                     作用
─────────────────────────  ─────────────────────────────────────
driver                     以容器方式加载 NVIDIA 驱动
container-toolkit          配置运行时,使容器能访问 GPU
device-plugin              向调度器广播 nvidia.com/gpu 资源
gpu-feature-discovery      把 GPU 型号、显存、MIG 状态写成节点标签
dcgm-exporter              把利用率、温度、功耗导出为 Prometheus 指标
mig-manager                基于节点标签的 MIG 分区
node-feature-discovery     硬件检测(依赖组件)
validator                  各阶段的校验 Job
(sandbox 系列)             vfio-manager、vgpu-device-manager、
                           kubevirt-gpu-device-plugin —— 见第五部分

配置 — ClusterPolicy 是唯一真相来源

所有 operand 的配置都汇聚到一个 CR 上:clusterpolicies.nvidia.com/cluster-policy。Helm 的 --set 归根到底就是在填这个 CR 的字段。实务中最常动的字段:

# ClusterPolicy 主要字段(节选)
spec:
  driver:
    enabled: true          # 若节点已有驱动则设为 false
    version: "580.65.06"   # 固定驱动版本
  toolkit:
    enabled: true
  mig:
    strategy: mixed        # single | mixed
  migManager:
    enabled: true
    config:
      name: custom-mig-config   # 自定义 MIG 配置文件的 ConfigMap
    env:
      - name: WITH_REBOOT       # 类似 CSP 那种需要重启的环境
        value: "true"
  devicePlugin:
    config:
      name: time-slicing-config # 时间片配置也放在 ConfigMap 里
  sandboxWorkloads:
    enabled: false         # 对接 KubeVirt 时设为 true(第五部分)

修改方式是通过 Helm 升级,或者 kubectl patch clusterpolicies...,operator 会检测到变化并只重新配置受影响的那个 operand。

版本体系

GPU Operator 采用基于年份的版本号(vYY.N.patch)——截至本文写作时最新系列是 v26.x(例如 v26.3.3),在此之前还有 v25.x、v24.9、v23.9 系列。每个版本都在文档中明确写出所支持的驱动版本组合与 Kubernetes 版本范围,因此升级前务必核对兼容性矩阵。要记住的原则是:operator 版本管理着驱动版本,所以如果只想单独升级驱动,正统做法是使用 ClusterPolicy 里的 driver.version

第二部分 — 只对部分 GPU 应用 MIG

在一个 8 卡节点上,"只把 0 号 GPU 切细用于推理,剩下 7 张整卡用于训练"是非常常见的需求。答案是mixed 策略 + 自定义 MIG 配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-mig-config
  namespace: gpu-operator
data:
  config.yaml: |
    version: v1
    mig-configs:
      # 只对 0 号 GPU 做 MIG,1~7 号保持原样
      partial-mig:
        - devices: [0]
          mig-enabled: true
          mig-devices:
            "1g.10gb": 4
            "3g.40gb": 1
        - devices: [1, 2, 3, 4, 5, 6, 7]
          mig-enabled: false
# ① 让 ClusterPolicy 指向这个 ConfigMap(如已配置可跳过)
kubectl patch clusterpolicies.nvidia.com/cluster-policy --type='json' \
  -p='[{"op":"replace","path":"/spec/migManager/config/name","value":"custom-mig-config"}]'

# ② 确认策略是 mixed —— 配置文件在不同 GPU 间不一样,single 无法表达
kubectl patch clusterpolicies.nvidia.com/cluster-policy --type='json' \
  -p='[{"op":"replace","path":"/spec/mig/strategy","value":"mixed"}]'

# ③ 把配置应用到节点
kubectl label nodes <节点> nvidia.com/mig.config=partial-mig --overwrite

# ④ 结果:资源会这样被广播
#   nvidia.com/gpu: 7                ← 未做 MIG 的 7 张卡
#   nvidia.com/mig-1g.10gb: 4        ← 从 0 号卡切出的碎片
#   nvidia.com/mig-3g.40gb: 1

Pod 只需根据用途请求 nvidia.com/gpu(整卡)或 nvidia.com/mig-1g.10gb(碎片)即可。两条核心规则——只要配置文件在 GPU 间不同,策略就必须是 mixed;若要明确说明 devices 列表里未列出的 GPU 该如何处理,一定要一起写上 mig-enabled: false 这一项(不要把它交给隐式状态)。

第三部分 — 手动应用 MIG:用 nvidia-smi 直接操作

在 Kubernetes 之外的裸机服务器上,或者不使用 mig-manager 的环境里,可以直接用 nvidia-smi mig 做分区。这正是 GPU Operator 在内部做的事情,所以亲手操作一遍,也能同时锻炼 operator 的调试能力。

# ① 开启 MIG 模式(仅 0 号 GPU)—— 需先清空目标 GPU 上的进程
sudo nvidia-smi -i 0 -mig 1
# 若显示 "Pending",需要重置 GPU 或重启:
sudo nvidia-smi --gpu-reset -i 0

# ② 确认可创建的 GPU 实例(GI)配置文件
nvidia-smi mig -lgip
#  例如:H100 80GB → 1g.10gb(ID 19)、2g.20gb(14)、3g.40gb(9)、7g.80gb(0) ...

# ③ 创建 GI —— 3g.40gb 一个 + 1g.10gb 四个,-C 会自动一并创建计算实例(CI)
sudo nvidia-smi mig -i 0 -cgi 9,19,19,19,19 -C

# ④ 确认
nvidia-smi mig -lgi        # 已创建的 GI 列表
nvidia-smi -L              # MIG 设备的 UUID 列表(MIG-xxxx 格式)

# ⑤ 在特定实例上运行 —— 用 UUID 指定
CUDA_VISIBLE_DEVICES=MIG-<uuid> python infer.py

# ⑥ 释放 —— 按 CI → GI 的顺序删除,再关闭 MIG 模式
sudo nvidia-smi mig -i 0 -dci && sudo nvidia-smi mig -i 0 -dgi
sudo nvidia-smi -i 0 -mig 0

需要掌握的概念:MIG 是一种两层结构——GI(GPU Instance,内存与 SM 的硬件级分区)内部包含着CI(Compute Instance,SM 的再分区)。大多数情况下每个 GI 配一个 CI(-C 选项)就足够,再把 CI 进一步细分,是那种想在共享同一内存分区的同时只分割计算能力的特殊场景。另外,②中的配置文件 ID 因 GPU 型号而异,请务必先用 -lgip 确认。

第四部分 — KubeVirt:组件、配置、版本

为什么要在 Kubernetes 上跑虚拟机

无法迁移到容器的工作负载,比想象中要多——需要内核模块的遗留系统、Windows 客户机、需要强隔离的多租户 GPU 环境。KubeVirt 把虚拟机变成 Kubernetes 的一等公民(CRD),让 Pod 和虚拟机能用同一个集群、同一个网络、同一套 kubectl来管理。

四大组件

组件             形态          作用
──────────────  ───────────  ─────────────────────────────────────
virt-operator    Deployment   管理 KubeVirt 自身的安装与升级
virt-controller  Deployment   集群级虚拟机生命周期管理(创建 VMI 等)
virt-handler     DaemonSet    节点级守护进程 —— 同步 VMI 规格与 libvirt 域
virt-launcher    Pod(每虚拟机一个)  包裹虚拟机的 Pod —— 内部的 libvirtd 驱动 QEMU/KVM

流程是这样的:用户创建一个 VirtualMachine CR → virt-controller 创建 VirtualMachineInstance(VMI)和 virt-launcher Pod → 对应节点上的 virt-handler 把 VMI 规格翻译成 libvirt 域 → virt-launcher 内部的 libvirtd 启动 QEMU/KVM 进程。虚拟机本质上就是 Pod 里的一个进程,因此 Kubernetes 的网络、调度机制原样适用。磁盘镜像的导入由另一个项目 CDI(Containerized Data Importer)通过 DataVolume CRD 负责。

配置 — KubeVirt CR 与 featureGates

KubeVirt 的全局配置也汇聚到一个 CR 上(kubevirt.io/v1 里的 KubeVirt)。GPU 直通所需的核心配置:

apiVersion: kubevirt.io/v1
kind: KubeVirt
metadata:
  name: kubevirt
  namespace: kubevirt
spec:
  configuration:
    developerConfiguration:
      featureGates:
        - GPU                 # 允许给虚拟机分配 GPU
        - HostDevices
    permittedHostDevices:     # 哪些主机设备可以交给虚拟机
      pciHostDevices:
        - pciVendorSelector: "10de:2331"     # NVIDIA H100(vendor:device)
          resourceName: "nvidia.com/GH100_H100_PCIE"
          externalResourceProvider: true     # 由 GPU Operator 的插件负责广播

版本特点

KubeVirt 在 2023 年发布 v1.0,宣布生产就绪,此后以每季度一次的次要版本(v1.1、v1.2……)持续演进,处于 v1.x 系列。它是 CNCF 孵化项目,每个版本都会写明所支持的 Kubernetes 版本范围(大体是最新的三个 K8s 版本)。能实际感受到的变化主要围绕三条主线:instancetype/preference API 的成熟(像 T 恤尺码一样声明虚拟机规格)、内存热插拔、CPU 热插拔等实时资源调整,以及实时迁移能力的不断稳固。GPU 直通的虚拟机,由于设备被物理绑定在节点上,无法进行实时迁移——请把这一条当作与版本无关的物理法则记住。

第五部分 — 连接两个世界:GPU Operator × KubeVirt

安装 GPU Operator 时设置 sandboxWorkloads.enabled=true,它就会连虚拟机的 GPU 供给也一并负责。

helm install --wait gpu-operator \
  -n gpu-operator --create-namespace \
  nvidia/gpu-operator --version=v26.3.3 \
  --set sandboxWorkloads.enabled=true

此时节点标签 nvidia.com/gpu.workload.config 就成了开关:

值               节点用途              部署的 operand
──────────────  ────────────────────  ─────────────────────────────
container        普通 GPU 容器          driver、device-plugin……(第一部分的配置)
vm-passthrough   把 GPU 整块交给虚拟机   vfio-manager(vfio-pci 绑定),
                                       kubevirt-gpu-device-plugin
vm-vgpu          给虚拟机分配 vGPU 碎片  vgpu-manager、vgpu-device-manager

在直通节点上,vfio-manager 会把 GPU 绑定到 vfio-pci 驱动(取代主机驱动),kubevirt-gpu-device-plugin 则把该设备以 nvidia.com/GH100_H100_PCIE 这样的资源名广播出去。虚拟机规格中这样请求:

# VirtualMachine 规格节选
spec:
  template:
    spec:
      domain:
        devices:
          gpus:
            - deviceName: nvidia.com/GH100_H100_PCIE
              name: gpu1

总结一下——在同一个集群里,只需切换标签,就能让某些节点服务于容器 GPU,某些节点服务于虚拟机直通,某些节点服务于 vGPU。需要强隔离(安全性、驱动自由度)时选虚拟机直通;需要高密度时选容器 + MIG;vGPU 则介于两者之间,按这个感觉来选就行。

结语

GPU Operator 用一个 ClusterPolicy 统领整支 operand 舰队,KubeVirt 用四个组件把虚拟机编入 Pod 的世界,两者在 sandboxWorkloads 与 workload.config 标签处相遇。部分 MIG 靠的是 mixed 策略 + devices 列表,手动 MIG 靠的是 nvidia-smi mig -cgi/-dgi——只要弄清楚 operator 到底在替你自动化哪些工作,出故障时该看哪里也就一目了然了。Kubernetes 的基础手感可以在Kubernetes 游乐场Kubestronaut 测验里练习。

参考资料