从云原生走向 AI 原生:一套面向未来的架构方法论 → 阅读《AI 原生基础设施》

NVIDIA Container Toolkit:四个组件与 CDI 深度解析

草稿

NVIDIA Container Toolkit(NCT)是其他一切的地基:设备插件宣告的资源、GPU Operator 部署的组件,最终都靠它把 GPU 真正放进容器。

什么是 NVIDIA Container Toolkit

NVIDIA Container Toolkit(NCT)是一组软件组件的集合,让 NVIDIA GPU 在所有主流容器运行时(Docker、containerd、CRI-O 和 Podman)中都可用。它由四个各司其职的组件组成。理解每一个都至关重要,因为 GPU 容器出问题的时候(它们一定会出问题),你需要确切知道故障发生在哪一层。

组件一:libnvidia-container

libnvidia-container 是最底层的组件:一个 C 库和 CLI 工具,执行向容器注入 GPU 的实际动作。被调用时,它做以下事情:

  • 读取容器的 cgroup 和进程命名空间
  • 通过 NVML(NVIDIA 管理库)发现主机上的全部 NVIDIA GPU 设备
  • 把所需的 GPU 设备文件(/dev/nvidia*)绑定挂载进容器的 /dev 命名空间
  • 把主机上的 NVIDIA 驱动用户态库(libcuda.solibnvidia-ml.so 等)绑定挂载进容器的库路径
  • 注入必要的环境变量(如 NVIDIA_VISIBLE_DEVICES
  • 可选地配置统一虚拟内存(UVM)与管理文件
为什么挂载主机库而不是打包进镜像
CUDA 用户态库必须与内核驱动版本精确匹配。如果把库烧进镜像,主机上每次驱动更新都会弄坏所有容器。运行时从主机挂载,库就永远与正在运行的驱动匹配,无需重建镜像。
# nvidia-container-cli(libnvidia-container 的 CLI 包装,很少直接调用)
nvidia-container-cli --load-kmods configure \
  --ldconfig=@/sbin/ldconfig \
  --require=cuda>=12.0 \
  --pid=<PID> \
  --device=all

# 查看工具包可见的 GPU
nvidia-container-cli info
nvidia-container-cli list

组件二:NVIDIA Container Runtime Hook

nvidia-container-runtime-hook(同样以 nvidia-container-toolkit 包发布)是 OCI 预启动钩子可执行文件,是 OCI 规范与 libnvidia-container 之间的粘合剂。执行流程:

  1. runc 创建容器(命名空间、cgroups、文件系统),但尚未启动进程
  2. runc 读取 OCI 规范的 hooks.prestart 数组,找到 NVIDIA 钩子
  3. runc 携带容器的 config.json 和进程 ID 执行钩子
  4. 钩子从容器环境读取 NVIDIA_VISIBLE_DEVICESNVIDIA_DRIVER_CAPABILITIES
  5. 钩子调用 nvidia-container-cli configure,后者执行 libnvidia-container 注入 GPU 设备与库
  6. 钩子退出,runc 启动容器进程,容器现在拥有 GPU 访问
# 控制 GPU 可见性与能力的环境变量
NVIDIA_VISIBLE_DEVICES=all                   # 所有 GPU
NVIDIA_VISIBLE_DEVICES=0,1                   # 仅 GPU 0 和 1
NVIDIA_VISIBLE_DEVICES=none                  # 无 GPU(纯 CPU 容器)

NVIDIA_DRIVER_CAPABILITIES=compute,utility   # 最常用
NVIDIA_DRIVER_CAPABILITIES=all               # 全部(含显示)
NVIDIA_DRIVER_CAPABILITIES=compute,compat32  # 32 位 CUDA 兼容

NVIDIA_REQUIRE_CUDA=cuda>=12.0               # 驱动 CUDA < 12.0 则失败

设备插件(见设备资源抽象)在 Allocate 阶段设置的正是 NVIDIA_VISIBLE_DEVICES,两条链路在这里汇合。

组件三:NVIDIA Container Runtime

nvidia-container-runtime 是一个兼容 OCI 的运行时包装器,位于 Docker/containerd 与真正的 runc 之间,唯一职责是在把 OCI 规范传给 runc 之前注入 NVIDIA 钩子。自 2019 年起它实现为一层薄垫片:

# 配置 nvidia-container-runtime 后 Docker 的执行流:

Docker(守护进程)
→ 调用 nvidia-container-runtime(而非 runc)
→ nvidia-container-runtime 读取 OCI config.json
→ 注入预启动钩子:nvidia-container-runtime-hook
→ 把修改后的 config.json 交给 runc
→ runc 创建容器
→ runc 执行预启动钩子(nvidia-container-runtime-hook)
→ 钩子调用 libnvidia-container → GPU 设备注入完成
→ runc 启动容器进程
→ GPU 可用!

对 CRI-O(用于 OpenShift 和许多生产 K8s 集群)来说,这个包装器并非必需,CRI-O 原生支持 OCI 钩子,可直接调用钩子。

组件四:nvidia-ctk CLI

nvidia-ctk 是工具包的管理接口,负责容器运行时配置、CDI 规范生成和诊断:

# 配置 containerd 使用 nvidia-container-runtime
nvidia-ctk runtime configure --runtime=containerd

# 配置 Docker / CRI-O
nvidia-ctk runtime configure --runtime=docker
nvidia-ctk runtime configure --runtime=crio

# 生成 CDI 规范(现代方式)
nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

# 列出 CDI 设备 / 验证安装
nvidia-ctk cdi list
nvidia-ctk --version

CDI:容器设备接口(现代路径)

OCI 钩子方案能用,但有个根本缺陷:每个容器运行时都必须知道 NVIDIA 的钩子,在厂商中立的规范里塞进了一个厂商专属的钩子。2021 年,NVIDIA、Red Hat 等共同创建了容器设备接口(Container Device Interface,CDI)标准,现由 CNCF 托管。

CDI 是一份声明式 YAML 规范,描述让一个设备在容器中可用所需的一切:设备节点、库挂载、环境变量和钩子。规范生成一次、存放在 /etc/cdi/,任何支持 CDI 的运行时读取即可注入设备,无需任何 NVIDIA 专属代码:

# CDI 设备命名
nvidia.com/gpu=all            # 所有 GPU
nvidia.com/gpu=0              # 仅 GPU 0
nvidia.com/mig-1g.10gb=0:0    # MIG 实例

# 在 Docker 中使用 CDI(需要 Docker 25+)
docker run --device=nvidia.com/gpu=0 nvidia/cuda:12.6.0-base nvidia-smi
CDI 与 DRA 的关系
DRA 路径(见数据平面·DRA)的设备注入同样依赖 CDI 兼容的运行时。CDI 把“注入”标准化,DRA 把“声明与调度”标准化,两者是上下游关系,不是竞争关系。

完整安装实践

在 GPU 节点上的四步(Kubernetes 场景通常由 GPU Operator 代劳,但手工走一遍能建立完整心智模型):

# 第 1 步:在主机上安装 NVIDIA 驱动(Ubuntu 22.04/24.04)
sudo apt-get install -y linux-headers-$(uname -r)
sudo apt-get install -y nvidia-driver-570
sudo reboot
# 验证:nvidia-smi 应显示 Driver Version: 570.x.x, CUDA Version: 13.x
# 第 2 步:安装 NVIDIA Container Toolkit
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

sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
# 第 3 步:配置容器运行时(containerd 为 Kubernetes 默认)
sudo nvidia-ctk runtime configure --runtime=containerd
sudo systemctl restart containerd

# 第 4 步:验证
docker run --rm --gpus all nvidia/cuda:12.6.0-base-ubuntu22.04 nvidia-smi
# 应打印容器内的 nvidia-smi 输出

关键 NVIDIA 基础镜像选型

镜像标签模式包含内容适用场景
nvcr.io/nvidia/cuda:<ver>-base-<os>仅 CUDA 运行时(libcudart),无开发工具最小推理容器
nvcr.io/nvidia/cuda:<ver>-runtime-<os>基础版 + cuBLAS、cuDNN 运行时库运行预编译的 CUDA 应用
nvcr.io/nvidia/cuda:<ver>-devel-<os>完整工具链:nvcc、cuDNN、全部头文件从源码构建 CUDA 应用
nvcr.io/nvidia/pytorch:<ver>-py3完整 PyTorch + CUDA + cuDNN + NCCL开箱即用的深度学习训练/推理
nvcr.io/nvidia/tensorflow:<ver>-tf2-py3TF2 + CUDA + cuDNN 栈TensorFlow 工作负载
nvcr.io/nvidia/tritonserver:<ver>Triton 推理服务器二进制生产模型服务
表 1: NVIDIA 官方基础镜像速查

选型原则与显存管理的教训一致:devel 镜像体积数倍于 base,运行时镜像只装运行时库;除非你要编译,否则不要用 devel。

总结

NVIDIA Container Toolkit 的四个组件构成一条注入链:nvidia-container-runtime 把钩子写进 OCI 规范,runc 在容器启动前执行钩子,钩子调用 libnvidia-container 完成设备与库的绑定挂载,nvidia-ctk 负责配置与诊断。CDI 把这套厂商机制标准化,成为现代运行时与 DRA 的共同底座。排障时按“容器里有没有 /dev/nvidia* → 有没有 NVIDIA_VISIBLE_DEVICES → 运行时配置对不对”的顺序自底向上检查,绝大多数“容器里看不见 GPU”的问题都能定位。

创建于 2026/08/30 更新于 2026/08/30 2381 字 阅读约 5 分钟