在 Kubernetes 中使用 GPU,NVIDIA Device Plugin 是必不可少的组件。它支持多种 GPU 注入策略,其中
envvar
是最早、最简单的一种。本文将带你深入理解envvar
策略的原理、优缺点以及适用场景。
一、背景
在 Kubernetes 中,Device Plugin 框架 为 GPU、FPGA、RDMA 等特殊硬件提供了标准化的管理接口。
NVIDIA Device Plugin 是 NVIDIA 官方实现的 GPU 设备插件,负责:
- 向 kubelet 注册
nvidia.com/gpu
资源 - 汇报节点上可用的 GPU 列表及健康状态
- 在 Pod 启动时,将分配的 GPU 注入到容器中
其中Nvidia Device Plugin比较核心的一个配置是DEVICE_LIST_STRATEGY
,用来设置容器分配网卡的策略。 而今天要介绍的**envvar
策略,是最早支持也是现在默认的策略。
二、什么是 envvar
策略
envvar
策略的核心思想是:
通过设置环境变量
NVIDIA_VISIBLE_DEVICES
,将分配的 GPU 列表传递给nvidia的容器运行时,由运行时在容器启动时注入对应的/dev/nvidia*
设备。 这种方式实现简单、兼容性好,是最早的 GPU 注入方案。
三、工作原理
假设一个 Pod 申请了 2 张 GPU:
3.1 Pod 请求 GPU
yaml
resources:
limits:
nvidia.com/gpu: 2
3.2 kubelet 调用 Device Plugin
- kubelet 调用 Device Plugin 的
Allocate()
方法 - Device Plugin 选择可用的 GPU(例如 UUID=GPU-abc123, GPU-def456)
3.3 Device Plugin 返回 AllocateResponse
json
"envs": {
"NVIDIA_VISIBLE_DEVICES": "GPU-abc123,GPU-def456"
}
- 值是 GPU UUID 或 index(取决于配置)
- kubelet 将这个环境变量写入容器 spec
3.4 容器运行时解析
nvidia-container-runtime
在容器启动的 prestart hook 中读取NVIDIA_VISIBLE_DEVICES
- 根据 UUID 找到对应的
/dev/nvidia*
设备 - 注入设备到容器,并更新 cgroup
devices.allow
四、优点
- 实现简单
- 只需设置一个环境变量,不需要修改 CRI API
- 容易在不同容器运行时(Docker、containerd、CRI-O、Podman)中实现
- 兼容性好
-
即使不在 Kubernetes 中,直接:
arduinodocker run --gpus '"device=0,1"' nvidia/cuda:12.2-base nvidia-smi
也能用类似方式工作
-
- 早期唯一可行方案
- 在没有 CDI(Container Device Interface)之前,这是最容易落地的方式
五、缺点与安全风险
5.1 容易被绕过
- 用户可以在 PodSpec 中手动设置:
yaml
env:
- name: NVIDIA_VISIBLE_DEVICES
value: all
- 即使只申请了 1 张卡,也能访问所有 GPU
5.2 默认镜像可能全卡开放
- 很多 NVIDIA 官方镜像(如
nvidia/cuda:*
)默认带:
ini
ENV NVIDIA_VISIBLE_DEVICES=all
- 如果 runtime 没有额外限制,这会覆盖 Device Plugin 的分配结果
5.3 不受 Kubernetes 资源模型约束
- Kubernetes 无法阻止用户自己设置这个环境变量
- 在多租户/HPC 场景下可能导致 GPU 越权使用
5.4 systemd daemon-reload会掉卡
这是一个致命的问题 在pod运行过程中,如果节点运行了systemd daemon-reload
,pod内会丢失显卡的操作权限,遇到如下报错
vbnet
/# nvidia-smi
Failed to initialize NVML: Unknown Error