写点什么

如何解决在容器中执行 nvidia-smi 的系列问题

作者:Infra研习社
  • 2025-03-11
    四川
  • 本文字数:2376 字

    阅读完需:约 8 分钟

如何解决在容器中执行 nvidia-smi 的系列问题

字数 1596,阅读大约需 8 分钟

如何解决在容器中执行 nvidia-smi 的系列问题

容器中查看不到 GPU 进程信息

问题:我们在容器中使用 GPU 之后,执行 nvidia-smi 却查看不到进程;这个问题到目前依然没有官方解决办法。


原因我认为有两点:


  • • 我们都知道容器使用了 namespace 技术,它其中有一个叫 pid namespace;Nvidia 的内核模块因为感知不到 Pid 的隔离,所以它不能处理这种容器 Pid 到主机 Pid 的转换。

  • nvidia-smi 命令执行的时候在获取进程信息的时候会直接和内核进行交互;这个我们通过执行 strace nvidia-smi 命令可以分析出来。导致没有办法从 nvmlapi 中进行拦截和处理。


下面提出了三种替代方案来解决这个问题,具体使用根据个人情况来决定。

方案一:设置 hostPid

知道原因之后,第一想到的方案肯定是让容器使用宿主机的 pid namespace,不要进行隔离。


  • • 在 PodYaml 文件中添加如下内容:


apiVersion: v1kind: Podmetadata:  name: view-pidspec:  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. 1. 拦截 ioctl 调用:


  • • 模块通过替换 nvidiactl 文件操作中的 unlocked_ioctlcompat_ioctl 函数指针,插入自定义的 PID 转换逻辑。

  • • 自定义函数 nvidia_pidns_unlocked_ioctlnvidia_pidns_compat_ioctl 在调用原始 ioctl 前后执行额外的处理。


  1. PID 转换逻辑:


  • • 通过 fixer_0x0ee4fixer_0x1f48fixer_0x2588fixer_0x3848 等函数,根据不同的 NVIDIA 驱动版本(通过 arg.tag 区分)对 PID 进行转换。

  • • 使用内核函数如 find_vpidfind_pid_ns 在命名空间之间映射 PID


  1. 3. 模块生命周期:


  • nvidia_pidns_init:初始化模块,找到 nvidiactl 设备,保存原始 ioctl 函数指针并替换为自定义函数。

  • nvidia_pidns_exit:退出模块,恢复原始 ioctl 函数指针并释放资源。


重点


上面这些项目使用都不太方便,而且年久失修,在高内核版本已经编译不通过了,我在我的的代码仓库[1]中进行了调整:


  • • 我把它修改了可以通过 helm 部署就能把这个内核驱动安装上去的项目。

  • • 修复了之前项目里面说的在内核 5.7+ 上运行不了的问题。


使用步骤:


  1. 1. 只需要执行如下的命令安装到 kubernetes 集群中即可。


$ helm install mpu oci://ghcr.io/lengrongfu/mpu --version 0.0.1
复制代码


使用有问题请提交 issue.


现在要求节点能联网,因为它需要去执行 apt 的动作,会动态编译内核模块,然后把它安装到内核中去;更多具体的使用可以参考我的 github 项目中文档。

方案三:使用 nvitop 命令

nvitop是一款交互式 NVIDIA 设备和进程监控工具。它可以实现在容器中查看 GPU 进程信息,坏处是它会查看到整个主机上所有的进程信息,而不是当前容器的。


null


  • • 安装: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 torchimport time
# 检查 GPU 是否可用if not torch.cuda.is_available(): print("GPU is not available. Please run this script on a machine with a GPU.") exit()
# 设置设备为 GPUdevice = torch.device("cuda")
# 定义矩阵大小matrix_size = 10240
# 创建两个随机矩阵并移动到 GPUmatrix_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

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2019-04-10 加入

Infra研习社, 分享学习笔记、前沿技术、 职场交流. 关注我:【Infra研习社】

评论

发布
暂无评论
如何解决在容器中执行 nvidia-smi 的系列问题_#GPU_Infra研习社_InfoQ写作社区