ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

内网GPU服务器离线安装nvidia-container-toolkit,解决Docker容器中nvidia-smi报错

内网GPU服务器离线安装nvidia-container-toolkit,解决Docker容器中nvidia-smi报错 还在纠结容器里跑nvidia-smi老是在内网服务器上报错直接把nvidia-docker2换成nvidia-container-toolkit离线装一遍省掉一堆在线源依赖的麻烦。先交代下背景这类问题最常见的场景是这样的生产线上的 GPU 服务器和公网完全隔离没有 apt/yum 在线源但业务方又明确要求所有推理服务必须容器化容器里必须能用到 GPU。你手上可能只有一块刚装好驱动的机器搜遍全网发现nvidia-docker2早在 2022 年之后就被官方整合到nvidia-container-toolkit一套体系里了直接在线装当然简单离线装就涉及到依赖包、仓库源、daemon 配置这些一连串连锁问题。这篇文章就是我从实际项目里踩完坑之后整理出来的完整离线安装流程面向有内网 GPU 服务器、需要给 Docker 加 GPU 能力的运维和 AI 平台工程师不讲废话直接给能抄作业的命令和踩坑记录。1. 项目概述与核心需求拆解1.1 先说清楚 nvidia-docker2 和 nvidia-container-toolkit 的关系很多老运维还停留在nvidia-docker2时代。2022 年之前NVIDIA 官方提供的是一个叫nvidia-docker2的元包它会自动拉取nvidia-container-runtime和nvidia-container-toolkit这两个组件。2022 年之后官方把组件整合成了nvidia-container-toolkitnvidia-docker2则变成了一个兼容性的过渡包。核心的组件其实有三个组件名作用说明libnvidia-container1底层 C 库负责和 NVIDIA 驱动交互所有组件的依赖基础libnvidia-container-tools命令行工具集提供nvidia-container-clinvidia-container-toolkit将 GPU 能力桥接到 Docker/containerd核心运行时插件nvidia-docker2元包自动配置 Docker daemon老版本兼容包所以离线安装的本质就是把这几个 deb/rpm 包全部找齐传到内网机器上按顺序安装再配置 Docker 的 runtime。1.2 离线安装和在线安装的路径差异在线安装只需要两行命令因为系统会自动从 NVIDIA 官方源拉取所有依赖apt-get install -y nvidia-docker2 systemctl restart docker但离线环境里没有源系统根本不知道去哪里找libnvidia-container1。所以我们要做的是在一台能联网的同版本系统上把所需软件包及它们的所有依赖先下载到本地再做成压缩包或者本地仓库带到内网去用。NVIDIA 官方目前维护了两套仓库deb 系针对 Ubuntu/Debianrpm 系针对 CentOS/RHEL/Rocky。离线机器是哪个发行版就必须在相同版本的联网机器上下包。比如内网是 Ubuntu 22.04你拿 CentOS 7 的包过去大概率会因为 glibc 版本过老或过新直接装不上。2. 离线安装前的三件事确认系统环境、找齐依赖包、规划安装方式2.1 上机器先记下这三个信息不要急着下载任何东西先在内网机器上收集三个关键点cat /etc/os-release # 发行版和版本号 docker version --format {{.Server.Version}} # Docker 版本 nvidia-smi # host 上 NVIDIA 驱动是否正常这三个信息决定了后续所有操作。原因很直接nvidia-container-toolkit对 Docker 版本有最低要求Docker 19.03 以上才支持--gpus参数Docker 20.10 以上配合 toolkit 才能稳定使用 NVIDIA Container Runtime。host 的 NVIDIA 驱动必须已经装好且nvidia-smi能正常输出否则后面容器里就算配置对了也会报 driver 错误。我遇到过一个典型的内网环境系统是 Ubuntu 20.04Docker 版本是 19.03.9一开始直接从官网拿最新的nvidia-container-toolkitdeb 包结果装完 Docker 启动报错 runtime 不识别。查了半天才发现是新版 toolkit 需要 Docker 的--gpus支持而 19.03.9 虽然有这个参数但和 toolkit 新版之间存在兼容缝隙。最终换回nvidia-container-toolkit1.14.5才稳定下来。2.2 在联网机器上把依赖包全部下载下来这里分 deb 系和 rpm 系两套操作目标都是在联网机器上先添加 NVIDIA 官方源然后强制下载全部依赖到本地。deb 系Ubuntu/Debian官方推荐做法curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add - distribution$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sudo tee /etc/apt/sources.list.d/libnvidia-container.list sudo apt-get update下载安装包这一步一定要用--download-onlycd /tmp/nvidia-offline apt-get install --download-only nvidia-docker2下载完成后所有 deb 文件会躺在/var/cache/apt/archives/。整理一下mkdir -p /tmp/nvidia-offline cp /var/cache/apt/archives/*.deb /tmp/nvidia-offline/ tar czf nvidia-offline-deb.tar.gz nvidia-offline/要注意的是apt-get install --download-only只会下载包本身和它声明的依赖但如果系统里已经装过某些依赖包就不会重复下载。理论上没问题因为离线机器一般也是干净的但保险起见我通常还会手动把核心包再apt-get download一遍cd /tmp/nvidia-offline apt-get download libnvidia-container1 libnvidia-container-tools nvidia-container-toolkit nvidia-container-runtime nvidia-docker2rpm 系CentOS/Rocky做法curl -s -L https://nvidia.github.io/libnvidia-container/rpm/libnvidia-container.repo | sudo tee /etc/yum.repos.d/libnvidia-container.repo yum install -y yum-utils cd /tmp/nvidia-offline yumdownloader --resolve nvidia-docker2如果联网机和离线机架构不一致比如你是在 x86 机器上下载但内网是 ARM64 机器记得在配置文件里改baseurl中的x86_64为aarch64或者用--archaarch64指定下载架构。2.3 离线安装方案的三种选择离线环境中实际有三种做法根据服务器数量和维护成本来选择方案适用场景操作方式纯 rpm/deb 本地安装单台或几台机器rpm -ivh *.rpm或dpkg -i *.deb搭建本地 Yum/Apt 仓库批量服务器方便统一管理用createrepo或dpkg-scanpackages生成仓库元数据容器镜像方式和现有 CI/CD 深度集成将 toolkit 打包进基础镜像直接在容器内配置 runtime根据我们实际运维经验3 台以内用第一种超过 5 台建议直接搭本地仓库因为手动rpm -ivh装 5 台以上很容易出依赖不一致的问题而且后续版本升级还要重新走一遍流程。我们团队后来统一做了个内网 apt 源nvidia-container-toolkit相关包定期同步一次各服务器apt-get install -y一条命令就完事。3. 核心安装流程与配置落地3.1 上传解压按依赖顺序安装 deb 包拿到离线压缩包后上传到内网服务器解压后先dpkg -i试装一次tar xzf nvidia-offline-deb.tar.gz -C /tmp/ cd /tmp/nvidia-offline dpkg -i *.deb如果依赖缺失dpkg会明确列出缺哪些包最常见的是libseccomp2、libcap2。这时候不要直接在内网机上去apt-get install -f因为它会尝试访问在线源导致超时。正确的做法是回到联网机上补齐这些包再回到内网机执行dpkg -i。正确的安装顺序建议是dpkg -i libnvidia-container1_*.deb dpkg -i libnvidia-container-tools_*.deb dpkg -i nvidia-container-toolkit_*.deb dpkg -i nvidia-container-runtime_*.deb dpkg -i nvidia-docker2_*.deb为什么强调顺序因为nvidia-docker2这个元包在安装时会触发 Docker daemon 的配置操作如果前面的 runtime 没装好它会直接报错哪怕你把依赖全塞进去了也可能因为 runtime 缺失而失败。我踩过一次坑直接dpkg -i *.deb一次全装结果nvidia-docker2先把 daemon 配置改了发现/usr/bin/nvidia-container-runtime还不存在配置文件生成一半就中断了后面排查起来特别费劲。rpm 系同理rpm -ivh libnvidia-container1-*.rpm rpm -ivh libnvidia-container-tools-*.rpm rpm -ivh nvidia-container-toolkit-*.rpm rpm -ivh nvidia-container-runtime-*.rpm rpm -ivh nvidia-docker2-*.rpm3.2 核心配置文件 daemon.json 的写法装完包后Docker 并不会自动切换 runtime需要手动在/etc/docker/daemon.json里注册。这也是离线安装和在线安装最容易出问题的地方。检查一下/etc/docker/daemon.json是否存在如果不存在就新建。写入内容如下{ runtimes: { nvidia: { path: /usr/bin/nvidia-container-runtime, runtimeArgs: [] } } }如果机器上之前配过镜像加速或者其他参数要追加这部分不要直接覆盖{ registry-mirrors: [https://docker.mirrors.example.com], runtimes: { nvidia: { path: /usr/bin/nvidia-container-runtime, runtimeArgs: [] } } }然后重启 Dockersystemctl restart docker重启后验证 runtime 是否被识别docker info | grep -A 5 Runtimes正常情况下能输出类似Runtimes: nvidia runc的信息。如果没有出现nvidia先检查 daemon.json 的 JSON 格式是否合法然后确认/usr/bin/nvidia-container-runtime这个路径是否存在。我遇到过有的版本安装后二进制在/usr/bin/nvidia-container-runtime-hook真正的入口脚本是/usr/bin/nvidia-container-runtime如果你那台机器缺这个文件可以直接建一个软链过去。3.3 检查 toolkit 自带的 config.tomlnvidia-container-toolkit安装后会自动生成一个配置文件/etc/nvidia-container-runtime/config.toml。一般不需要手动改但内网环境有个特殊点如果宿主机的/dev/nvidia*设备节点没有被 Docker 挂载进去需要确认nvidia-container-runtime-hook的nvidia-container-cli路径是否正常。执行下面的命令看是否能正常查出 GPUnvidia-container-cli info正常输出类似NVRM version: 525.60.11 CUDA version: 12.0 Device Index: 0 Device Minor: 0 Model: Tesla T4如果这里报错后面容器里肯定也是不行的。nvidia-container-cli info是排障的第一入口比直接跑docker run快得多。4. 实际验证 GPU 容器是否可用4.1 从 Docker Hub 拉一个带 CUDA 的镜像到内网验证容器内 GPU 是否可用最直接的方法是跑一个带 nvidia-smi 的基础镜像。但内网没有外网所以需要先在能联网的机器上拉取再导出docker pull nvidia/cuda:11.8.0-base-ubuntu20.04 docker save -o cuda-base.tar nvidia/cuda:11.8.0-base-ubuntu20.04然后把这个 tar 包传到离线机器上docker load -i cuda-base.tar如果你连镜像都不方便传也可以用宿主机自带的方式验证在docker run时挂载宿主机的nvidia-smi二进制和驱动目录。不过这个方案对驱动库依赖很高很容易报库文件找不到我建议还是直接传镜像。4.2 用 --gpus 参数启动容器Docker 19.03 及以上版本支持--gpus参数docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu20.04 nvidia-smi如果配置正确容器内会正常输出nvidia-smi的结果。这验证了两层能力第一层是 Docker 能正确识别并使用nvidiaruntime第二层是 runtime 能成功把宿主机的 GPU 设备文件、驱动库和一个最小化的 CUDA 运行环境挂载进容器。如果你的 Docker 版本刚好是 19.03 以下只能通过环境变量方式触发docker run --rm -e NVIDIA_VISIBLE_DEVICESall nvidia/cuda:11.8.0-base-ubuntu20.04 nvidia-smi这种方式会走nvidia-container-runtime-hook的兼容路径也能达到同样的效果。但我还是建议尽量升级 Docker毕竟 19.03 以下的版本太老和现在多数容器编排系统兼容性不好。4.3 指定哪块 GPU 资源--gpus all在物理机上是全部 GPU也可以精确控制docker run --rm --gpus device0,1 nvidia/cuda:11.8.0-base-ubuntu20.04 nvidia-smi -L也可以通过环境变量方式限定docker run --rm -e NVIDIA_VISIBLE_DEVICES0 nvidia/cuda:11.8.0-base-ubuntu20.04 nvidia-smi -L这里的0是宿主机nvidia-smi里看到的 GPU 序号。需要注意的是如果宿主机上有两块 GPU而你不指定NVIDIA_VISIBLE_DEVICES默认情况下容器内是看不到任何 GPU 的只有显式加上--gpus all或设置设备列表才会暴露进去。新版 toolkit 也支持通过 UUID 指定 GPU写法是docker run --rm --gpus deviceGPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ...这些参数在部署多租户环境时非常有用可以保证每个容器只看得到分配给它的那张卡。5. 常见问题与排查技巧实录5.1 问题速查表离线装完nvidia-docker2后跑容器大概率会碰到下面这些错误我按实际踩坑频率排了个序报错信息可能原因解决办法Unknown runtime specified nvidiadaemon.json 未生效或配置格式错误docker info确认 Runtimes检查 JSON 格式could not select device driver nvidia with capabilities: [[gpu]]Docker 没有注册 nvidia runtime重启 Docker重查 daemon.jsonnvidia-container-cli: initialization error: driver error: failed to process request: unknown宿主机驱动问题先确认nvidia-smi正常检查/dev/nvidia*节点nvidia-container-cli: mount error: failed to mount /dev/nvidia0容器权限或版本限制确认 libnvidia-container 与驱动版本匹配docker: Error response from daemon: Unknown runtime specified nvidia.daemon.json 配置后没重启systemctl restart dockerdpkg: dependency problems - leaving triggers unprocessed依赖关系不完整回到联网机补齐缺失依赖包rpm -ivh报libnvidia-container1 conflicts之前装过旧版本rpm -e卸载旧包或使用--replacefiles5.2 排查技巧先分清是 Docker 层问题还是 toolkit 层问题这是最核心的排障思路。遇到容器报错先看报错来自哪一层。如果是 Docker 层比如Unknown runtime specified nvidia问题一定出在 daemon.json 和 Docker 的 runtime 注册上和 GPU 驱动无关。先跑docker info看 Runtimes 列表。如果是 toolkit 层比如nvidia-container-cli开头的报错问题大概率在驱动和设备文件。这时直接在宿主机上跑nvidia-container-cli info如果这个命令都不通过说明 toolkit 根本无法和驱动正常通信那问题就回到了宿主机驱动侧而不是容器侧。这个思路能帮你节约大量时间不用一头扎进容器日志里去看那些 dump 出来的 NVRM 错误。5.3 需要注意的离线环境坑第一个坑是不要在内网机上直接执行apt-get install -f或yum install -y因为内网没有源命令会卡在连接超时上有时还会把 dpkg 数据库锁住后面想手动修都麻烦。第二个坑是版本匹配。nvidia-container-toolkit非常依赖宿主机的 NVIDIA 驱动版本比如驱动是 470 系列而 toolkit 是最新版本有时会出现NVRM version mismatch的报错。解决方案就是下载时不要盲目追求最新可以到 NVIDIA 官方 GitHub Release 页面找一个和驱动匹配的 toolkit 版本。第三个坑是 Docker 和 containerd 的差异。如果你用的是 Kubernetes 环境节点上跑的是 containerd 而不是 Docker CLI配置方式完全不是daemon.json而是要改 containerd 的 config.toml把nvidiaruntime 注册进去。这个场景不在本文范围内但提醒一下别拿着一套配置到处套。第四个坑是 kernel 模块和/dev/nvidia*设备节点。有些服务器重启后/dev/nvidia*节点可能没有自动创建导致容器运行时找不到设备。可以用nvidia-smi -pm 1开启持久化模式或者检查 udev 规则。内网机器如果装了最新驱动但 udev 规则没配置好重启后很容易出现这个问题。6. 扩展思路从单机安装到批量部署单台服务器离线装好只是开始。如果是管理一个集群所有机器都走手动传包显然不现实。两个方向可以做。方向一是自建内网源。在联网环境定期同步libnvidia-container和nvidia-container-toolkit的包到内网仓库然后各节点配置/etc/apt/sources.list或/etc/yum.repos.d/之后直接在线安装。本质上是把离线问题转成了内网统一的软件源问题这对运维团队来说是最省心的长期方案。方向二是把整个 GPU 容器运行环境做成镜像。在基础镜像里预置nvidia-container-toolkit的相关配置部署时直接跑自定义运行脚本不用在每台宿主机上重复配置。这个方案适合容器平台已经标准化、但宿主机操作系统碎片化比较严重的场景。另外如果你的离线服务器是 composes 环境、容器编排平台直接管理容器运行时那就要跳出 Docker 的概念直接看 containerd 的配置方式。NVIDIA 官方文档里有 containerd 的完整配置示例核心是在/etc/containerd/config.toml里增加一个 runtime 选项指定/usr/bin/nvidia-container-runtime作为 runc 的替代品然后用nerdctl run --gpus all或 Kubelet 配置来触发 GPU 调用。我在交付一个客户现场的 GPU 推理平台时200 多台内网机器就是靠自建源加无人值守脚本完成的离线部署。先在源服务器上停好所有 deb/rpm 包然后写一个安装脚本判断每台机器的系统版本、Docker 版本、是否有 GPU再自动执行对应安装步骤整个过程从原来的单台半小时缩短到十分钟以内。这个思路你可以直接用脚本不用太复杂核心就是判断发行版和架构然后调用对应命令。7. 最后再分享一个小技巧保留一套完整的离线安装包我在实际项目里养成了一个习惯每次给客户交付离线安装方案都会保留一套完整的离线包文件包括所有 deb/rpm 包、daemon.json 示例、验证命令脚本、常见问题说明。用 tar 打成一个包归档。这样下次有同版本的新机器要部署直接复制过去十分钟内就能解决。很多显卡厂商在发布新驱动时也会同步更新nvidia-container-toolkit的版本号。建议把内网仓库中的 toolkit 版本和驱动版本一起更新避免出现驱动新但 runtime 旧这种半新不旧的尴尬状态。如果离线环境里已经有一套跑通的组合我建议你记录下这个组合的精确版本号不要轻易变更。我在生产上见过太多因为 toolkit 小版本升级导致容器起不来的故障内网环境尤其要克制“顺手升级一下”的冲动。按这个流程走下来从收集依赖包到最终 Docker 容器里正常输出nvidia-smi一台新机器最多二十分钟就能搞定。核心就是提前把包备好、顺序装对、daemon 配置准确剩下的就是验证环节的事。
返回列表