ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux下NVIDIA驱动与CUDA runfile安装及多版本共存实战

Linux下NVIDIA驱动与CUDA runfile安装及多版本共存实战 1. 为什么我把 apt 装的驱动换成了 runfile第一次在 Linux 上装 NVIDIA 驱动我用的是最省事的 apt 方式一条apt install nvidia-driver-535下去重启nvidia-smi出来一张表看着挺美。真正出事是在两个月后系统推送了一次内核更新重启之后图形界面直接进了低分辨率兜底模式nvidia-smi报 couldnt communicate with the NVIDIA driver。事后复盘是 DKMS 在编译新内核模块时失败了失败了还不报错只是静默跳过把问题留到重启那一刻爆炸。这类问题的根源在于 apt 装的是发行版打包好的驱动它跟着内核版本走本质上是内核一升级、模块就得重编。在单机开发环境里这通常没事但在有几十台节点的 GPU 集群上一次内核升级就能让你在群里被 一整天。runfile 是 NVIDIA 官方提供的自解压安装包它把驱动、内核模块、用户态库一次性装到宿主系统里不依赖发行版的打包策略内核升级后也可以手动重编可控性高得多。我这篇要讲的就是 runfile 这条路的完整走法包括安装、卸载、CUDA 对接以及我在生产环境里反复踩过的那些坑。需要先说明一点runfile 不是更高级的安装方式它只是更可控。它的代价是手动依赖管理、手动处理 nouveau、手动处理 Secure Boot以及升级内核之后你得记得跑一次nvidia-installer --rebuild或者重新执行安装包。如果你只是在自己笔记本上跑个图形界面打游戏apt 或发行版自带的驱动仓库完全够用别折腾。而下面这几种场景我更倾向于 runfile需要精确锁定某个驱动分支比如 470 系列跑老架构卡、需要 CUDA 版本与驱动版本严格配对、需要在没有外网的机器上离线部署、以及需要在一台机器上并存多个 CUDA Toolkit 版本。这些场景下 apt 的自动反而是障碍因为它的自动逻辑和你想要的版本组合经常不一致。这篇适合两类人一类是刚接手机器、被 nouveau 和 Secure Boot 卡住的运维或算法同学另一类是已经装上了但卸载不干净、重装总出问题的老手。下面的流程我在 CentOS 7、Ubuntu 20.04、Ubuntu 22.04 上都验证过命令有发行版差异的地方我会标出来。2. 动手之前先把这几项信息摸清楚2.1 显卡型号、架构与驱动分支的对应关系这一步偷懒的人特别多直接下载最新版驱动往上怼。结果 470 才能支持的老卡装了 550安装程序在编译阶段就报 The NVIDIA driver is not compatible with this kernel。第一件事是确认卡的具体型号和计算能力Compute Capability用lspci | grep -i nvidia看设备名或者装好基础工具后跑nvidia-smi --query-gpuname,compute_cap --formatcsv需要驱动已装。拿到型号之后去对照 NVIDIA 官网的驱动支持矩阵。大体规律是GeForce 10 系及更早Pascal 之前的 Maxwell、Kepler要留在 470 甚至 390 分支Turing、Ampere、Ada 这些新架构用最新分支没问题数据中心卡Tesla/V 系列/A 系列/H 系列走数据中心驱动分支现在叫 Data Center Driver它和桌面分支的版本号不一定同步。计算能力这一项决定了后面 CUDA 版本的上限比如计算能力 6.1 的卡用 CUDA 12 是没问题的但计算能力 3.5 的 K80 就只能停在 CUDA 11 之前。我建议把这几项信息记到一个部署文档里格式可以是这样项目命令示例结果设备型号lspci | grep -i nvidiaGA102 [GeForce RTX 3090]计算能力nvidia-smi --query-gpucompute_cap --formatcsv8.6内核版本uname -r5.15.0-91-generic发行版cat /etc/os-releaseUbuntu 22.04当前驱动cat /proc/driver/nvidia/version535.129.03顺手确认一下驱动和 CUDA 的版本对应关系。CUDA Toolkit 有一个最低驱动版本要求比如 CUDA 12.3 要求 Linux 驱动不低于 545.23.06。反过来装了新驱动跑旧 CUDA 通常是兼容的因为 CUDA 有向前兼容机制minor version compatibility但这个兼容性有边界跨大版本11 到 12时还是老老实实配对比较省事。这个表官网有搜 CUDA Toolkit Release Notes 里的 Table 1. CUDA Toolkit and Corresponding Driver Versions 就能找到。2.2 Secure Boot 与 nouveau两个最容易卡住的地方nouveau 是内核自带的 NVIDIA 开源驱动。只要它在安装程序就会检测到有内核模块占用了设备直接拒绝安装。禁用方式是写一个 blacklist 文件把 nouveau 加入黑名单同时确保它没有在 initramfs 里被提前加载。Ubuntu 上操作如下sudo tee /etc/modprobe.d/blacklist-nouveau.conf EOF blacklist nouveau options nouveau modeset0 EOF sudo update-initramfs -uCentOS/RHEL 系把update-initramfs -u换成dracut --force。写完之后重启用lsmod | grep nouveau验证没有任何输出才算干净。我遇到过一种情况文件写了、重启了nouveau 还在。原因是内核命令行里带了nouveau.modeset1或者有个/etc/modules-load.d/下的文件显式加载了它得一并清掉。Secure Boot 是另一个拦路虎。开启 Secure Boot 的机器只加载有签名的内核模块而 runfile 装出来的nvidia.ko是本地编译的没有签名加载会被拒。有两个方案一是进 BIOS 关掉 Secure Boot这是最省事的做法适合内网可控的机器二是在安装过程中让安装程序帮你生成密钥并注册Ubuntu 上会弹出一个 MOKMachine Owner Key设置界面让你设置一个密码重启后在蓝色界面里选 Enroll MOK 输入密码完成注册。第二条路在无人值守部署时不方便所以我在机房一般直接关 Secure Boot。判断当前状态可以用mokutil --sb-state输出 SecureBoot enabled 或 disabled。注意如果 Secure Boot 是开启状态并且你没走 MOK 注册流程驱动会安装成功但重启后nvidia-smi报 No devices were found。这个报错很容易被误判成显卡故障实际去看dmesg | grep -i nvidia就能看到模块签名验证失败的信息。2.3 内核头文件和编译链路runfile 安装驱动时会现场编译内核模块所以需要gcc、make和当前内核对应的头文件。Ubuntu 上sudo apt update sudo apt install -y build-essential gcc make sudo apt install -y linux-headers-$(uname -r)CentOS 7 上装kernel-devel-$(uname -r)和kernel-headers还要注意kernel-devel的版本必须和uname -r完全一致否则编译会找不到符号。离线环境里这一步最麻烦我一般的做法是提前把linux-headers的 deb 包和依赖linux-headers-common、linux-kbuild等一起下好用dpkg -i本地装。还有一个常被忽略的点如果机器上装了gcc的多个版本runfile 默认用cc指向的那个。某些新内核配老版本 gcc 会编译失败报一堆语法错误。这时候用CC/usr/bin/gcc-11显式指定或者在安装时加参数让安装程序忽略编译器检查后面会讲。3. runfile 驱动安装从下载到跑通 nvidia-smi3.1 下载与校验别跳过校验这一步runfile 的官方下载入口在 NVIDIA 驱动下载页面选择对应的产品类型和型号下载得到一个形如NVIDIA-Linux-x86_64-535.129.03.run的文件。文件名格式是NVIDIA-Linux-架构-驱动版本.run架构一般是x86_64ARM 平台是aarch64。传输到目标机器之后先校验完整性。这个步骤看起来多余但我在内网用跳板机传大文件的经历告诉我文件被截断太常见了。官方页面会给出 sha256 值sha256sum NVIDIA-Linux-x86_64-535.129.03.run拿到的哈希和页面上给的对比不一致就重新下载。这一步能帮你挡掉后面那个gzip: stdin: invalid compressed>sudo systemctl isolate multi-user.target # 或者老一点的系统 sudo telinit 3如果systemctl isolate之后还有残留的 X 进程ps aux | grep -i xorg确认一下必要时sudo systemctl stop gdm或者lightdm、sddm看你用的 display manager。然后执行安装参数选择是这块的核心。我常用的组合sudo ./NVIDIA-Linux-x86_64-535.129.03.run \ --no-opengl-files \ --no-drm \ --silent逐个解释为什么。--no-opengl-files表示不安装 OpenGL 库文件这是服务器场景的标配——服务器不需要本地渲染装了这些库反而会覆盖系统自带的 Mesa 库导致图形界面起不来或者glxinfo报错。如果是带桌面、要用显卡输出显示的工作站这个参数不能加否则桌面渲染会退回软件渲染。--no-drm是不装 DRM 内核模块服务器上同样不需要。--silent是静默安装适合写进自动化脚本第一次装建议去掉它看着安装程序的提示走出问题能第一时间发现。还有一个--dkms参数它会帮你注册 DKMS 服务内核升级后自动重编模块——这一点恰好能缓解我开头说的那个痛点代价是系统里得有 DKMS 服务并且能正常工作。安装过程会问几个问题是否注册内核模块签名Secure Boot 开启时会问、是否安装 32 位兼容库、是否更新 X 配置。我的选择是签名按前面的 Secure Boot 方案决定32 位库服务器上不需要X 配置在没有图形界面的机器上让它自己写就行。装完之后reboot重启起来先看模块加载情况lsmod | grep nvidia nvidia-smi3.3 安装后的验证与持久化配置nvidia-smi能出那张表说明驱动基本没问题了。表里要关注三处驱动版本号、CUDA Version这是驱动支持的最高 CUDA 版本不代表你装了 CUDA、以及每张卡的显存和功耗状态。进一步验证用cat /proc/driver/nvidia/version看内核模块版本用nvidia-smi -q看详细信息。多卡机器上检查一下卡有没有全部识别到nvidia-smi -L会列出所有 GPU 的 UUID 和型号这个 UUID 在后面做容器挂载和 MIG 配置时要用到。有一个配置我建议顺手加上持久化模式。sudo nvidia-smi -pm 1开启之后驱动会常驻内存避免每次调用 GPU 时重新初始化带来的延迟在频繁启停任务的场景下感受明显。持久化模式默认重启后失效要永久生效得靠 systemd 服务或开机脚本简单做法是写一个/etc/systemd/system/nvidia-persistenced.service不过大部分发行版装完驱动后会自带nvidia-persistenced这个服务systemctl enable --now nvidia-persistenced就行。4. CUDA Toolkit 的 runfile 安装与驱动解耦4.1 为什么 CUDA 的 runfile 要加 --toolkitCUDA 的 runfile 下载下来文件名形如cuda_12.3.1_545.23.06_linux.run注意文件名里那串545.23.06——它自带了一个驱动版本。如果你直接sudo sh cuda_12.3.1_545.23.06_linux.run一路回车它会问你要不要装驱动默认是装的于是你前面辛苦装好的驱动被覆盖掉。这就是为什么必须明确指定只装 toolkitsudo sh cuda_12.3.1_545.23.06_linux.run \ --toolkit \ --toolkitpath/usr/local/cuda-12.3 \ --no-opengl-libs \ --silent \ --override--toolkit只装编译器、库和头文件不动驱动。--toolkitpath指定安装路径明确写成带版本号的目录为后面多版本共存做准备。--no-opengl-libs同上服务器不需要。--override是忽略编译器版本检查——新内核配较老的 gcc或者反过来安装程序可能报 unsupported compiler version加上这个参数它就跳过检查。这个参数名字听着危险实际含义只是不因为编译器版本不匹配而中断安装不是覆盖系统文件可以放心用。安装完之后目录结构是/usr/local/cuda-12.3/下面有bin、lib64、include、nvvm等。bin里最关键的是nvcc和cuda-uninstaller后者待会儿卸载要用。4.2 环境变量配置的坑装完不代表能用。必须在 shell 里配PATH和LD_LIBRARY_PATH否则nvcc找不到编译出来的程序运行时报 libcudart.so.12: cannot open shared object file。写进/etc/profile.d/cuda.sh是全局生效的做法sudo tee /etc/profile.d/cuda.sh EOF export CUDA_HOME/usr/local/cuda-12.3 export PATH$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH$CUDA_HOME/lib64:$LD_LIBRARY_PATH EOF注意LD_LIBRARY_PATH的写法是$CUDA_HOME/lib64而不是$CUDA_HOME/lib64 位系统上库文件在lib64里写成lib是个非常隐蔽的错误表现为编译没问题但一运行就找不到库。配完之后source /etc/profile或者重新登录用which nvcc和nvcc -V验证。提示LD_LIBRARY_PATH在 systemd 服务里是不继承的。如果你用 systemd 拉起训练任务得在 service 文件里显式加Environment或者EnvironmentFile不然服务里跑的程序找不到 CUDA 库。这个坑我在部署推理服务时踩过排查了两小时才发现是环境变量没传进去。4.3 cuDNN 的对接方式CUDA 装完了深度学习框架还需要 cuDNN。官方下载渠道给的是压缩包tar或者 deb 包压缩包的方式最干净解压后把文件拷进 CUDA 目录tar -xzvf cudnn-linux-x86_64-8.9.7.29_cuda12-archive.tar.xz sudo cp cudnn-*-archive/include/cudnn*.h /usr/local/cuda-12.3/include/ sudo cp -P cudnn-*-archive/lib/libcudnn* /usr/local/cuda-12.3/lib64/ sudo chmod ar /usr/local/cuda-12.3/include/cudnn*.h /usr/local/cuda-12.3/lib64/libcudnn*这里有个细节cuDNN 的版本必须和 CUDA 主版本对应给 CUDA 12 用的 cuDNN 装在 CUDA 11 的目录里框架运行时会直接崩报符号未定义。cp -P的-P参数是为了保留符号链接cuDNN 的库目录里有一堆libcudnn.so.8 - libcudnn.so.8.9.7这样的软链不加这个参数会把软链拷成实体文件占空间还容易版本混乱。验证方式是用cuDNN自带的样例程序或者更简单——装完 PyTorch 的 GPU 版本跑torch.backends.cudnn.version()看能不能取到版本号。5. 卸载与回滚把残留清干净的完整动作5.1 驱动卸载runfile 装的驱动自带了卸载程序位置在/usr/bin/nvidia-uninstall。执行之前同样要切到文本模式退出图形界面sudo systemctl isolate multi-user.target sudo /usr/bin/nvidia-uninstall它会问你确认几个问题是否删除nvidia-xconfig写的 X 配置、是否删除nvidia-persistenced。如果打算重装X 配置可以删掉让它重新生成。卸载完成后lsmod | grep nvidia应该已经没有输出如果还有说明是nvidia-uvm、nvidia-drm之类的关联模块没卸干净可以sudo rmmod nvidia_uvm nvidia_drm nvidia注意顺序先卸依赖再卸主模块。5.2 CUDA 卸载CUDA 的 runfile 安装自带卸载脚本在安装目录的bin下sudo /usr/local/cuda-12.3/bin/cuda-uninstaller这个脚本会删掉它装进去的文件。执行完之后手工检查几个可能残留的位置/usr/local/cuda-12.3/目录本身可能还在里面剩一些空目录、/etc/profile.d/cuda.sh里的环境变量、以及/etc/ld.so.conf.d/下如果有 CUDA 相关的配置也要清掉。清完记得sudo ldconfig刷新库缓存。5.3 残留检查清单卸载完最怕的是看着干净了装新版本时报冲突。我习惯用下面这几条命令过一遍# 检查内核模块是否还挂着 lsmod | grep -i nvidia # 检查驱动相关文件是否残留 ls /usr/lib/x86_64-linux-gnu/ | grep -i nvidia ls /etc/modprobe.d/ | grep -i nvidia # 检查环境变量 env | grep -i cuda # 检查动态库缓存里有没有旧版本 ldconfig -p | grep cudnn ldconfig -p | grep cudaldconfig -p这条特别有用它能告诉你系统实际能找到哪些库、路径是什么。我遇到过 apt 装过一次驱动、后来手工换 runfile 的情况ldconfig -p里同时存在两个路径下的libcuda.so运行时加载了旧的那个导致版本号对不上。处理方式是找到/etc/ld.so.conf.d/下对应的配置文件删掉再ldconfig。一个完整的卸载流程表格方便对照步骤命令验证方式切换文本模式systemctl isolate multi-user.targetps aux | grep Xorg无输出卸载驱动/usr/bin/nvidia-uninstalllsmod | grep nvidia无输出卸载 CUDA/usr/local/cuda-X.Y/bin/cuda-uninstaller目录已清空清环境变量删除/etc/profile.d/cuda.shenv | grep CUDA无输出刷新库缓存sudo ldconfigldconfig -p | grep cuda无输出重启验证rebootnvidia-smi报 command not found6. 多版本 CUDA 共存与切换6.1 目录规划与环境切换策略跑模型的时候经常会碰到这个情况A 项目依赖 CUDA 11.8 编译的 PyTorchB 项目要用 CUDA 12.1。这时候重装 CUDA 是最蠢的做法正确方式是让两个版本并存按需切换。前提是安装时就用了--toolkitpath/usr/local/cuda-11.8这种带版本号的路径两个版本各占一个目录互不干扰。然后有两种切换方案。第一种是手工改环境变量把CUDA_HOME指向不同目录适合临时切换第二种是用update-alternatives建立统一的/usr/local/cuda软链适合需要保持路径稳定的场景sudo update-alternatives --install /usr/local/cuda cuda /usr/local/cuda-11.8 118 sudo update-alternatives --install /usr/local/cuda cuda /usr/local/cuda-12.3 123 sudo update-alternatives --config cuda最后一条命令会列出所有候选版本让你选。这种方案的好处是构建脚本里写死/usr/local/cuda也能跟着切换。而最干净的做法是根本不用全局切换在 Python 层面解决。PyTorch 的 pip 包自带 CUDA 运行时pip install torch --index-url ...时选对应的 cu118 或 cu121 版本它会把所需的 CUDA 库打包进site-packages运行时不依赖系统的/usr/local/cuda。这就是为什么很多人系统里只有 CUDA 11但装个 cu121 的 PyTorch 照样能跑。理解这一点能省掉大量无谓的版本升级工作。6.2 驱动版本与多 CUDA 的兼容边界多版本 CUDA 共存的隐含前提是驱动版本要足够新能覆盖所有已装 CUDA 的最低要求。驱动是向后兼容的535 的驱动能跑 CUDA 11.8 和 12.3 编译的程序因为驱动里包含的是 CUDA 运行时libcuda.so而不是 SDKSDK 的编译产物在运行时通过驱动提供的接口执行。但反过来装了 12.3 的 CUDA 却用 470 的驱动运行时就会报 CUDA driver version is insufficient for CUDA runtime version。判断兼容性的方法很直接nvidia-smi右上角那个 CUDA Version 就是当前驱动支持的最高 CUDA 版本。只要各版本 CUDA 都不超过这个数字就都能跑。所以在规划多版本环境时先看驱动版本能顶到哪个 CUDA再决定装哪几个 Toolkit。还有一个容易忽略的点nvcc 编译出来的程序如果用了-archsm_90这样的架构标志而驱动不支持该架构运行时会报 no kernel image is available for execution on the device。这个错误的本质是编译目标架构和硬件/驱动不匹配解决办法是查清楚卡的计算能力用-archsm_86这类正确的标志或者用-archnative让编译器自动探测。7. 踩坑实录从 gzip 报错到显卡跑不满7.1 gzip: stdin: invalid compressed data 到底怎么来的先说说被搜得最多的这个报错。它的完整形态是执行 runfile 时输出gzip: stdin: invalid compressed>git clone https://github.com/wilicc/gpu-burn cd gpu-burn make ./gpu_burn 600跑的过程中用另一个终端开nvidia-smi dmon持续监控看功耗、温度、利用率、以及是否有 ECC 错误增长。输出里的Fault计数如果一直往上涨说明显卡的显存有问题这张卡就该报修了。测试通过的标准是所有卡都跑完全程没有报错温度稳定在安全区间内功耗能顶到设定的上限。顺手再看一眼nvidia-smi -q -d ECC里的 Volatile 计数。显存 ECC 错误在数据中心卡上是常态少量可纠正错误Correctable无所谓但如果不可纠正错误Uncorrectable出现并且增长那就是硬件问题的前兆。这个指标比跑分更能反映卡的真实健康度我见过跑分正常但 ECC 错误持续增长的卡几周之后就彻底挂了。7.4 几个反复出现的小问题nvidia-smi报 No devices were found前面说过 Secure Boot 是一个原因另一个原因是显卡没有正确供电或者没插稳去dmesg里搜 nvidia 看有没有 GPU has fallen off the bus 之类的信息。这个报错意味着卡在运行中掉线了通常是供电或者 PCIe 插槽的问题。nvidia-persistenced启动失败日志里报 Failed to query NVIDIA devices一般是权限问题或者驱动版本和 persistenced 版本不匹配。这个服务失败不影响基本功能只是多卡机器上会有额外的初始化开销。装了驱动之后图形界面分辨率不对多半是--no-opengl-files用在了带桌面的机器上或者 X 配置没生成。用sudo nvidia-xconfig重新生成/etc/X11/xorg.conf重启后一般能好。如果启用后黑屏把 xorg.conf 删掉让系统自动探测多数情况下也能恢复正常。离线机器的部署还有个细节CUDA runfile 在安装时会尝试联网检查更新离线环境下会卡住或者报网络错误。加--silent参数并且确保不加任何联网相关的选项它就不会去探网。这一点我第一次做离线部署时不知道看着安装程序停了十几分钟以为死机了。版本信息随时核对nvcc -V看编译器版本cat /usr/local/cuda/version.json看 JSON 格式的详细信息nvidia-smi看驱动和运行时支持的最高版本。这三个数字之间的关系理清楚排查版本问题时能少走一大半弯路。
返回列表