gpu-computing

GPU 容器虚拟化层完整详解

·16 分钟阅读·6396 字

GPU 容器虚拟化层完整详解 的详细笔记

📋 目录

GPU 容器虚拟化层完整详解

核心概念 GPU 容器虚拟化层通过 NVIDIA Container Toolkit 实现 GPU 设备挂载和驱动隔离,是 K8s GPU 调度的基础设施。 GPU容器虚拟化层是宿主机NVIDIA驱动 ↔ 容器内CUDA应用之间的完整中间抽象栈,核心目标:让容器安全、隔离、透明使用物理GPU/MIG虚拟卡,统一适配Docker/containerd/K8s,实现设备挂载、驱动注入、资源隔离、多卡调度。
整套官方标准化组件统称 NVIDIA Container Toolkit,替代老旧nvidia-docker2,是K8s AI/GPU集群唯一生产标准虚拟化方案。

1. 整体分层架构(自底向上5层,从硬件到应用)

1.1. 层级1:底层硬件&宿主机驱动层(虚拟化基础底座)

  1. 物理GPU硬件:A100/H100/3090,暴露设备文件 /dev/nvidia0~nvidiaN/dev/nvidiactl/dev/nvidia-uvm
  2. NVIDIA内核驱动 nvidia.ko:内核态管理GPU硬件、显存、中断、MIG硬件分片
  3. 用户态驱动库libcuda.solibnvidia-ml.so(NVML,nvidia-smi/DCGM底层依赖) 核心约束:容器镜像仅携带CUDA运行时,不包含驱动;驱动全部由宿主机动态挂载注入,彻底解决镜像与驱动版本耦合问题。

1.2. 层级2:核心底层库 libnvidia-container(虚拟化基石)

C语言实现的底层核心库,无任何容器引擎依赖,所有上层组件全部调用它完成硬件交互。

1.2.1. 核心能力

  1. GPU设备枚举:扫描宿主机全部物理GPU、MIG虚拟实例、NVLink拓扑;
  2. 动态挂载管理:生成绑定挂载列表,自动挂载宿主机驱动库、设备节点、工具二进制;
  3. 命名空间隔离:修改OCI Spec,配置cgroup设备白名单,容器只能访问分配到的GPU;
  4. 环境变量注入:自动写入CUDA_VISIBLE_DEVICES,隔离容器可见显卡;
  5. MIG/vGPU适配:支持虚拟实例单独绑定、资源QoS限制;
  6. 对外提供C API与命令行工具nvidia-container-cli,供runtime、device-plugin调用。

1.3. 层级3:运行时扩展层 nvidia-container-runtime(OCI标准封装层)

符合OCI容器运行时规范,是标准runc的包装器,拦截容器创建流程,注入GPU虚拟化逻辑后再调用原生runc启动容器进程。

1.3.1. 工作逻辑

  1. containerd/docker配置runtime: nvidia,创建容器时优先调用nvidia-container-runtime
  2. 解析GPU资源请求(--gpus、K8s CDI设备声明);
  3. 触发prestart OCI Hooknvidia-container-runtime-hook);
  4. Hook调用libnvidia-container修改OCI配置:
  • 追加/dev/nvidia*设备白名单;
  • bind mount宿主机驱动库到容器/usr/lib/x86_64-linux-gnu
  • 注入LD_LIBRARY_PATHCUDA_VISIBLE_DEVICES
  1. 完成GPU配置后,代理调用原生runc启动容器。

1.3.2. 两种运行模式

  1. 传统legacy模式:通过环境变量传递GPU卡号,早期K8s device-plugin配套;
  2. CDI标准模式(生产推荐):基于CNCF CDI规范声明设备,解耦runtime与硬件逻辑,GPU Operator默认启用。

1.4. 层级4:工具与标准规范层(nvidia-ctk + CDI)

1.4.1. 1)nvidia-ctk 命令行工具

NVIDIA Container Toolkit CLI,统一管理虚拟化配置:

  • 生成CDI设备描述文件;
  • 自动修改containerd/docker runtime配置;
  • 校验驱动、容器镜像兼容性;
  • 调试容器GPU挂载故障。

1.4.2. 2)CDI Container Device Interface(容器设备标准,K8s调度核心)

CNCF通用硬件设备规范,彻底解决传统runtime硬编码问题,是现代GPU虚拟化、K8s DRA/Device Plugin底层标准。

CDI核心规则
  1. 以声明式YAML/JSON定义设备资源:设备节点、挂载路径、环境变量、启动钩子;
  2. 设备唯一标识格式:vendor.com/class=name,GPU示例:nvidia.com/gpu=0nvidia.com/gpu=mig-1g.5gb-0
  3. 宿主机/etc/cdi/nvidia.yaml预生成所有GPU/MIG设备描述;
  4. K8s device-plugin分配GPU时,直接返回CDI设备名称,kubelet+containerd自动解析完成挂载,runtime无需硬编码GPU逻辑。
CDI优势
  • 容器运行时无关:Docker/containerd/CRI-O/Podman一套标准;
  • 天然支持MIG、vGPU、异构GPU;
  • K8s解耦调度与设备注入,适配新一代DRA动态资源分配。

1.5. 层级5:上层编排与应用层

  1. 容器引擎:containerd(K8s默认)、docker;
  2. K8s调度组件:nvidia-device-plugin DaemonSet、GPU Operator、Volcano调度器;
  3. 容器应用:PyTorch/TensorFlow/CUDA推理/训练程序,调用容器内挂载的宿主机驱动完成GPU计算。

2. 完整容器GPU启动全流程(containerd+K8s标准链路)

  1. 用户提交Pod,声明limits.nvidia.com/gpu:1
  2. kube-scheduler筛选有空闲GPU扩展资源的节点;
  3. 目标节点kubelet调用nvidia-device-plugin的Allocate接口;
  4. device-plugin分配空闲GPU卡号,返回CDI设备ID nvidia.com/gpu=GPU-UUID
  5. kubelet更新Pod OCI Spec,追加CDI设备引用;
  6. containerd收到创建请求,使用nvidia-cdi runtime;
  7. nvidia-container-runtime触发prestart hook;
  8. hook调用libnvidia-container读取CDI配置,完成三件核心注入:
  • 挂载宿主机/dev/nvidiaX、驱动so库、nvidia-smi二进制;
  • 设置cgroup设备权限,容器仅能读写分配的GPU;
  • 写入CUDA_VISIBLE_DEVICES=卡号,屏蔽其他显卡;
  1. 代理调用runc创建隔离容器进程;
  2. 容器内程序调用CUDA API,透明访问宿主机物理GPU,nvidia-smi仅显示分配到的设备。

3. 三大核心虚拟化隔离机制(容器GPU安全保障)

3.1. 设备文件隔离(CUDA_VISIBLE_DEVICES)

最基础隔离手段,由虚拟化层自动注入环境变量:

  • 分配GPU 0、2 → CUDA_VISIBLE_DEVICES=0,2
  • CUDA运行时只会枚举变量内的卡号,容器完全看不到宿主机其他GPU设备;
  • MIG场景自动填入MIG实例ID,实现虚拟分片隔离。

3.2. Linux Namespace + Cgroup设备白名单隔离

  1. Mount Namespace:容器独立文件系统视图,仅挂载指定驱动库,无法读取宿主机完整驱动目录;
  2. Device Cgroup v2:容器进程仅允许访问分配的/dev/nvidiaX设备节点,无权限读写其他GPU,杜绝越权访问;
  3. PID Namespace:容器进程无法看到宿主机其他占用GPU的进程。

3.3. 硬件分片隔离(上层虚拟化扩展,分两类)

3.3.1. (1)MIG 硬件级虚拟化(A100/H100专属,底层硬件切分)

属于GPU硬件原生虚拟化,虚拟化层仅做挂载转发:

  • 单物理GPU硬件切分为多个独立MIG实例,每个实例独占显存、SM核心、L2缓存;
  • libnvidia-container识别MIG实例,单独生成CDI设备、独立注入环境变量;
  • 隔离最强:一个实例OOM/崩溃,不影响同卡其他容器。

3.3.2. (2)软件vGPU虚拟化(HAMi/Time-Slicing,所有NVIDIA显卡通用)

在标准容器虚拟化层之上叠加劫持层:

  1. Time-Slicing分时:单卡虚拟多份,仅做时间片轮转,无显存隔离
  2. HAMi细粒度vGPU:劫持libcuda.so调用,软件硬限制Pod最大显存占用,调度层基于显存容量筛选节点,虚拟化层配合拦截CUDA内存申请,超出直接OOM。

4. 核心组件详细拆解(面试高频)

4.1. libnvidia-container(底层核心库)

  • 定位:无依赖C库,所有GPU容器能力底层实现;
  • 核心API:设备枚举、挂载生成、OCI Spec补丁、MIG管理;
  • 输出工具:nvidia-container-cli,可手动调试容器GPU挂载。

4.2. nvidia-container-runtime-hook(OCI预启动钩子)

容器创建阶段的核心执行单元,在runc创建容器命名空间前执行:

  1. 读取GPU分配信息(卡号/CDI设备);
  2. 调用libnvidia生成挂载列表;
  3. 写入环境变量、修改设备权限;
  4. 无hook则容器无法识别任何GPU。

4.3. CDI 标准化层(现代集群必备)

解决传统runtime三大痛点:

  1. 不用为每种容器引擎单独开发GPU runtime;
  2. K8s device-plugin无需传递复杂挂载参数,仅返回CDI设备名;
  3. 统一支持MIG、vGPU、多卡拓扑、异构显卡;
    GPU Operator部署时自动执行nvidia-ctk cdi generate生成设备文件。

4.4. NVIDIA GPU Operator(集群一体化虚拟化运维组件)

不直接参与容器虚拟化,但统一管理整套虚拟化栈:

  1. 自动安装宿主机NVIDIA驱动;
  2. 配置containerd启用nvidia-cdi runtime;
  3. 部署nvidia-device-plugin、DCGM监控;
  4. 批量配置MIG、Time-Slicing虚拟化分片规则;
  5. 统一升级全套虚拟化组件,避免多节点版本不一致。

5. 传统nvidia-docker2 vs 新一代Container Toolkit+CDI

5.1. 老旧nvidia-docker2(废弃方案)

  1. 仅适配Docker,不原生支持containerd/K8s;
  2. 包装docker客户端命令,侵入式封装,兼容性差;
  3. 无CDI标准,硬编码GPU挂载逻辑,MIG适配繁琐;
  4. 已停止维护,新集群全部弃用。

5.2. 新一代NVIDIA Container Toolkit(生产标准)

  1. 兼容Docker/containerd/CRI-O/Podman全容器引擎;
  2. OCI标准runtime,无侵入包装;
  3. CDI标准化设备管理,完美对接K8s Device Plugin/DRA;
  4. 原生MIG、vGPU、DCGM监控联动;
  5. GPU Operator一键批量部署运维。

6. GPU容器虚拟化层常见故障排查

6.1. 容器内nvidia-smi找不到GPU

  1. containerd未配置nvidia-cdi runtime;
  2. Pod未声明limits.nvidia.com/gpu,device-plugin未分配GPU;
  3. CDI设备文件缺失,执行nvidia-ctk cdi generate重建;
  4. 宿主机驱动损坏,libnvidia-container扫描不到设备。

6.2. 容器启动提示权限不足,无法访问/dev/nvidia*

  1. cgroup设备白名单未注入,hook执行失败;
  2. 容器安全上下文privileged=false,缺少设备访问capability;
  3. libnvidia-container版本与驱动不匹配。

6.3. MIG容器仍能看见整张物理GPU

虚拟化层未识别MIG实例,未生成独立CDI设备;需重启nvidia-device-plugin、重新生成CDI配置。

6.4. HAMi vGPU显存限制不生效

容器虚拟化层正常,但缺少劫持库libvgpu.so注入,HAMi MutatingWebhook未修改Pod OCI挂载配置。

7. 与K8s GPU调度协同完整链路总结

  1. 调度层:nvidia-device-plugin扫描GPU,上报nvidia.com/gpu扩展资源,分配GPU/CDI设备;
  2. 虚拟化层:containerd + nvidia-container-runtime + CDI + libnvidia-container完成设备、驱动、环境变量隔离注入;
  3. 监控层:DCGM基于宿主机驱动采集硬件指标,与虚拟化层解耦;
  4. 分片扩展层:MIG硬件虚拟化 / HAMi软件vGPU,在标准虚拟化之上实现单卡多Pod共享。

8. 面试高频问答

  1. GPU容器虚拟化为什么驱动放在宿主机,不打包进镜像?
    CUDA镜像仅包含上层运行时库,驱动与宿主机内核强绑定,内核升级驱动必须同步更新;镜像内置驱动会出现内核ABI不兼容、版本冲突,宿主机统一挂载驱动实现镜像与硬件解耦,一套镜像适配所有同架构GPU节点。
  2. CDI在GPU虚拟化中起到什么作用?
    CDI是容器硬件设备通用声明标准,统一描述GPU需要挂载的设备、库、环境变量,解耦K8s device-plugin与container runtime;设备插件仅需返回CDI设备名称,runtime自动解析完成虚拟化注入,同时原生支持MIG、异构GPU、新一代DRA调度。
  3. CUDA_VISIBLE_DEVICES是谁注入的,原理是什么?
    nvidia-container-runtime-hook调用libnvidia-container自动注入,基于分配到的物理/MIG卡号生成;CUDA运行时读取该变量,仅枚举指定显卡,实现容器GPU视图隔离。
  4. MIG和HAMi vGPU分别属于哪一层虚拟化?
    MIG是硬件底层虚拟化,GPU硬件原生切分,容器虚拟化层仅做转发挂载;HAMi是软件上层虚拟化,在标准容器虚拟化基础上劫持CUDA调用,实现显存软隔离。
  5. libnvidia-container和nvidia-container-runtime的区别?
    libnvidia-container是底层C核心库,负责硬件扫描、挂载生成;nvidia-container-runtime是OCI运行时封装程序,拦截容器创建流程,调用libnvidia完成虚拟化配置,再调用原生runc启动容器。

关联文档

Yanche Blog

记录云原生、Linux、数据库等技术领域的学习心得,以及日常生活的思考与感悟。

© 2026 Yanche Blog. All rights reserved.

Powered by Astro