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

字数 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. 1. 拦截 ioctl 调用:
  • • 模块通过替换 nvidiactl 文件操作中的 unlocked_ioctl 和 compat_ioctl 函数指针,插入自定义的 PID 转换逻辑。

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

  1. 2. PID 转换逻辑:
  • • 通过 fixer_0x0ee4fixer_0x1f48fixer_0x2588 和 fixer_0x3848 等函数,根据不同的 NVIDIA 驱动版本(通过 arg.tag 区分)对 PID 进行转换。

  • • 使用内核函数如 find_vpid 和 find_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 进程信息,坏处是它会查看到整个主机上所有的进程信息,而不是当前容器的。

图片

  • • 安装: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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值