针对在Kubernetes (K8s) + containerd环境下容器内执行 nvidia-smi 出现 OOM (Out-Of-Memory) 错误的问题,根源通常在于容器环境与宿主机GPU驱动运行状态之间的隔离与资源映射不一致。结合提供的【参考资料】和常见故障模式,解决方案需从容器运行时配置、驱动服务状态和K8s资源限制三个层面进行排查。

1. 问题根因分析

物理机 nvidia-smi 正常而容器内OOM,表明宿主机GPU驱动基础功能正常,但容器运行时(containerd)在将GPU设备映射到容器时,或者容器内GPU工具库在访问底层资源时,发生了异常。主要可能原因包括:

  • 容器内nvidia-container-runtime或相关库版本不匹配:容器内使用的GPU工具链(如 nvidia-container-toolkit、CUDA库)与宿主机驱动版本不兼容,导致初始化时内存访问越界。
  • containerd配置未正确启用GPU支持:未在containerd配置文件中启用 nvidia-container-runtime 作为默认运行时或为特定容器配置,导致容器无法正确初始化NVIDIA GPU环境。
  • nvidia-persistenced 服务状态在容器上下文中的影响:虽然宿主机服务已启动,但容器内进程无法正确连接到该服务的持久化状态或相关UNIX套接字,导致工具初始化失败 。
  • 容器资源限制(Cgroups)导致内存访问冲突:K8s Pod或容器的Cgroup内存限制设置不当,与 nvidia-smi 等工具尝试访问GPU显存或系统内存时发生冲突,被内核误判为OOM 。
  • 显存碎片化或工具链Bug:特定版本的GPU驱动或容器工具链存在Bug,在容器环境中触发异常的内存分配。

2. 系统性解决方案推演

2.1 验证与修正 containerd 运行时配置

确保containerd已配置为使用NVIDIA容器运行时。编辑 /etc/containerd/config.toml 文件。

# 在 containerd 配置文件中,确保有以下内容
version = 2

# 1. 在 plugins."io.containerd.grpc.v1.cri".containerd 部分,设置默认运行时为 nvidia
[plugins."io.containerd.grpc.v1.cri".containerd]
  default_runtime_name = "nvidia"

# 2. 在 runtimes 部分,定义 nvidia 运行时
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
  runtime_type = "io.containerd.runc.v2"
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
    BinaryName = "/usr/bin/nvidia-container-runtime"

# 或者,如果使用 runc 作为基础,可能需要通过 runtime_engine 配置,但更常见的是上述方式。

修改后,重启containerd服务。

systemctl restart containerd

2.2 检查容器镜像内的NVIDIA工具链

确认用于运行 nvidia-smi 的容器镜像包含了与宿主机驱动兼容的NVIDIA工具包。一个简单的方法是使用NVIDIA官方提供的基础镜像,如 nvidia/cuda:12.2.0-base-ubuntu22.04,它已包含匹配的CUDA工具链。

可以在K8s的Pod配置中指定该镜像进行测试:

# pod-test-nvidia-smi.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nvidia-smi-test
spec:
  containers:
  - name: test
    image: nvidia/cuda:12.2.0-base-ubuntu22.04 # 使用与宿主机驱动兼容的CUDA版本
    command: ["nvidia-smi"]
    resources:
      limits:
        nvidia.com/gpu: 1 # 申请GPU资源,这需要预先安装NVIDIA Device Plugin
  restartPolicy: Never

如果无法使用官方镜像,请确保您自定义的镜像中正确安装了 nvidia-utils-xxxcuda-toolkit 包,版本需与宿主机驱动相匹配。

2.3 排查 nvidia-persistenced 的容器内访问

虽然宿主机服务已启动,但容器可能需要访问特定的UNIX套接字。nvidia-persistenced 通常会在 /var/run/nvidia-persistenced/nvidia-persistenced.sock 创建套接字。您需要确保容器内有权限访问该路径。这通常由 nvidia-container-runtime 自动处理,但如果问题依然存在,可以尝试在Pod定义中将其作为卷挂载(仅用于调试)。

# 非标准做法,主要用于诊断
spec:
  containers:
  - name: test
    # ... 其他配置
    volumeMounts:
    - mountPath: /var/run/nvidia-persistenced
      name: nvidia-persistenced-socket
  volumes:
  - name: nvidia-persistenced-socket
    hostPath:
      path: /var/run/nvidia-persistenced
      type: Directory

更标准的做法是确保宿主机已正确安装 nvidia-container-toolkit,并且 nvidia-container-runtime-hook 能正常执行。检查日志:

# 查看容器创建时的相关日志,特别是来自 nvidia-container-runtime 的
journalctl -u containerd -f | grep -i nvidia

2.4 调整K8s Pod资源限制

nvidia-smi 执行时除了加载自身二进制文件,还会加载用户态驱动库(如libnvidia-ml.so),并可能与GPU显存进行交互。如果Pod的limits.memory设置得过小(例如仅几百MiB),可能不足以容纳这些库的加载和初始化过程,从而触发容器OOM Killer。尝试在Pod定义中增加内存限制。

spec:
  containers:
  - name: test
    # ... 其他配置
    resources:
      limits:
        nvidia.com/gpu: 1
        memory: "2Gi" # 显著增加内存限制,例如2GiB
      requests:
        memory: "1Gi"

2.5 启用详细日志与诊断

为了精确定位OOM发生的具体原因,可以启用更详细的日志。

  • 检查容器OOM日志

    # 查看内核日志中关于容器OOM的条目
    dmesg | grep -i "oom-killer\|killed process" | grep -i `容器名或ID`
    # 或者查看K8s事件
    kubectl describe pod <pod-name> | grep -A5 -B5 "OOM"
    
  • 在容器内运行strace跟踪系统调用(如果容器允许):

    # 在Pod配置的command中,可以尝试
    command: ["strace", "-f", "-o", "/tmp/strace.log", "nvidia-smi"]
    

    之后从容器中取出日志文件,分析在OOM发生前的最后几个系统调用,可能会发现是哪个内存访问(如mmap)失败。

3. 快速修复流程(SOP)

结合上述分析,建议按以下步骤进行快速诊断与修复:

步骤操作预期结果/检查点
1. 验证运行时检查 /etc/containerd/config.tomlnvidia 运行时配置,并重启containerd。kubectl create -f pod-test-nvidia-smi.yaml 创建的测试Pod应能启动。
2. 验证Device Plugin确认K8s集群已部署 nvidia-device-plugin DaemonSet且Pod正常。`kubectl get pods -n kube-system
3. 测试官方镜像使用步骤2.2中的Pod定义,将镜像替换为NVIDIA官方CUDA镜像(如nvidia/cuda:12.2.0-base)。容器内 nvidia-smi 应能正常输出GPU信息。
4. 增加内存限制若步骤3仍失败,将Pod的 memory limit增加到 ` 
2Gi` 或更高。观察OOM错误是否消失。 
5. 检查驱动与服务在宿主机检查驱动版本 (nvidia-smi),并确保 nvidia-persistenced 服务活跃 (systemctl status nvidia-persistenced)。驱动版本与容器镜像CUDA版本兼容;服务状态为 active (running)
6. 收集诊断日志执行步骤2.5中的命令,收集容器OOM的内核日志和K8s事件。从日志中确定OOM触发的具体进程和内存申请大小。

典型成功路径:通常,问题出在步骤1(运行时配置)步骤3(镜像工具链不匹配)。正确配置containerd并使用与宿主机驱动版本匹配的NVIDIA官方基础镜像,在配合 nvidia-device-plugin 和适当的GPU资源申请后,容器内 nvidia-smi 应能正常运行 。

如果以上步骤均无法解决,问题可能涉及更深层次的兼容性问题或特定版本的Bug。此时,需要详细审查宿主机NVIDIA驱动版本、nvidia-container-toolkit 版本、containerd版本以及K8s版本的兼容性矩阵,并考虑升级或回退到稳定版本组合。同时,仔细分析步骤6收集到的系统日志,查看是否有关于 libnvidia-ml.socgroup 内存控制或 PCIe 访问的特定错误信息 。


参考来源

 

更多推荐