字数 1596,阅读大约需 8 分钟
如何解决在容器中执行 nvidia-smi 的系列问题
容器中查看不到 GPU 进程信息
问题:我们在容器中使用 GPU 之后,执行 nvidia-smi
却查看不到进程;这个问题到目前依然没有官方解决办法。
原因我认为有两点:
-
• 我们都知道容器使用了
namespace
技术,它其中有一个叫pid namespace
;Nvidia 的内核模块因为感知不到Pid
的隔离,所以它不能处理这种容器Pid
到主机Pid
的转换。 -
•
nvidia-smi
命令执行的时候在获取进程信息的时候会直接和内核进行交互;这个我们通过执行strace nvidia-smi
命令可以分析出来。导致没有办法从nvml
的api
中进行拦截和处理。
下面提出了三种替代方案来解决这个问题,具体使用根据个人情况来决定。
方案一:设置 hostPid
知道原因之后,第一想到的方案肯定是让容器使用宿主机的 pid namespace
,不要进行隔离。
- • 在
Pod
的Yaml
文件中添加如下内容:
apiVersion: v1
kind: Pod
metadata:
name: view-pid
spec:
hostPID: true
containers:
- name: view-pid
image: ubuntu:22.04
command: ["sh", "-c"]
args: ["while true; do echo 'foo'; done;"]
- • 使用
docker
命令启动的时候指定--pid==host
$ docker run -d --pid==host --gpus all docker.io/pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime
这个方案的优点就是使用简单,没有添加新的组件,不会对系统产生新的问题
;
缺点就是它能看到宿主机上的所有 GPU 进程
,这要是在算力云 Pass
产品里面是不能接受的,这样就没有安全性可言了。
方案二:使用内核拦截处理
因为上述问题是 nvidia
在内核中进行限制的,所以还有一种办法就是写一个内核模块来处理。下面是市面上存在的几个项目:
-
• https://github.com/gh2o/nvidia-pidns
-
• https://github.com/matpool/mpu
-
• https://github.com/gpues/nvidia-pidns
这个内核模块的主要目的是通过拦截 nvidiactl
设备的 ioctl
调用,对进程 PID
在不同 PID
命名空间之间进行转换,从而确保 NVIDIA GPU
在容器化环境或多 PID
命名空间系统中能够正确识别和管理进程。以下是代码的核心功能:
- 1. 拦截
ioctl
调用:
-
• 模块通过替换
nvidiactl
文件操作中的unlocked_ioctl
和compat_ioctl
函数指针,插入自定义的 PID 转换逻辑。 -
• 自定义函数
nvidia_pidns_unlocked_ioctl
和nvidia_pidns_compat_ioctl
在调用原始ioctl
前后执行额外的处理。
- 2.
PID
转换逻辑:
-
• 通过
fixer_0x0ee4
、fixer_0x1f48
、fixer_0x2588
和fixer_0x3848
等函数,根据不同的NVIDIA
驱动版本(通过arg.tag
区分)对PID
进行转换。 -
• 使用内核函数如
find_vpid
和find_pid_ns
在命名空间之间映射PID
。
- 3. 模块生命周期:
-
•
nvidia_pidns_init
:初始化模块,找到nvidiactl
设备,保存原始ioctl
函数指针并替换为自定义函数。 -
•
nvidia_pidns_exit
:退出模块,恢复原始ioctl
函数指针并释放资源。
重点
上面这些项目使用都不太方便,而且年久失修,在高内核版本已经编译不通过了,我在我的的代码仓库[1]中进行了调整:
-
• 我把它修改了可以通过
helm
部署就能把这个内核驱动安装上去的项目。 -
• 修复了之前项目里面说的在内核
5.7+
上运行不了的问题。
使用步骤:
- 1. 只需要执行如下的命令安装到
kubernetes
集群中即可。
$ helm install mpu oci://ghcr.io/lengrongfu/mpu --version 0.0.1
使用有问题请提交 issue
.
现在要求节点能联网,因为它需要去执行 apt 的动作,会动态编译内核模块,然后把它安装到内核中去;更多具体的使用可以参考我的 github
项目中文档。
方案三:使用 nvitop
命令
nvitop
是一款交互式 NVIDIA 设备和进程监控工具。它可以实现在容器中查看 GPU
进程信息,坏处是它会查看到整个主机上所有的进程信息,而不是当前容器的。
-
• 安装:
pip3 install --upgrade nvitop
-
• 使用:
python3 -m nvitop
使用 gpu-operator 安装驱动后宿主机执行不了 nvidia-smi
使用 gpu-operator
安装驱动又很多好处,可以快速的进行版本升级和测试,并且整个集群中节点上的驱动升级是自动化的。
但是使用它安装驱动之后假如想在宿主机上执行 nvidia-smi
命令查看 GPU
的信息就会发现提示没有这个命令。
那其实 gpu-driver
这个 Pod
在安装好驱动之后同时也会把 nvidia-smi
命令安装到宿主机上,只是和我们的 root
用户不在同一个 namespace
下,导致我们不能访问它的文件系统。
知道原因之后想要访问就比较简单了,使用如下命令就可以解决这个问题。
$ chroot /run/nvidia/driver nvidia-smi
下面是一个简单的 pytorch
的测试脚本,主要是持续消耗 GPU
:
import torch
import time
# 检查 GPU 是否可用
if not torch.cuda.is_available():
print("GPU is not available. Please run this script on a machine with a GPU.")
exit()
# 设置设备为 GPU
device = torch.device("cuda")
# 定义矩阵大小
matrix_size = 10240
# 创建两个随机矩阵并移动到 GPU
matrix_a = torch.randn(matrix_size, matrix_size, device=device)
matrix_b = torch.randn(matrix_size, matrix_size, device=device)
# 无限循环进行矩阵乘法
while True:
# 进行矩阵乘法
result = torch.matmul(matrix_a, matrix_b)
time.sleep(0.1)
往期推荐
使用 GPU DVFS 来优化GPU效率 CUDA Fatbin动态解压缩 揭开Nvidia GPU显存超分的面纱 GPU探针:使用 eBPF 来实现 CUDA 内存泄露监控 Kubernetes 中使用 CRIU 实现 GPU进程实时迁移:进阶篇 Kubernetes 中使用 CRIU 实现 GPU进程实时迁移:基础篇 Nvidia 显存缺斤少两? Nvidia MPS深入浅出 GPU远程调用—代码实践篇 GPU远程调用—原理篇 GPU远程调用—代码入门篇 GPU远程调用—入门篇 GPU虚拟化 GPU内核虚拟化-基础篇 GPU内核虚拟化-原理篇 GPU 设备动态挂载到 Pod 原理分析 Nvidia MIG深入浅出
引用链接
[1]
我的的代码仓库: https://github.com/lengrongfu/mpu