CANN安装升级避坑:version.cfg、银河麒麟驱动与nnrt无输出排查
1. 从一次版本死活查不出来的现场说起1.1 三个看似不相关的现象其实是同一条链上的问题拿到CANN 安装升级避坑这个题目的时候我脑子里第一反应不是文档里那些安装命令而是过去几次在国产化环境里折腾 CANNCompute Architecture for Neural Networks昇腾异构计算架构时遇到的三种典型报错场景装完了想确认版本结果version.cfg里的内容和实际装的版本对不上在银河麒麟系统上按流程装完驱动lsmod死活看不到相关模块执行nnrt --version光标闪一下就回到提示符什么都不打印。这三个现象看起来很分散一个属于查版本一个属于装驱动一个属于命令无输出但真正在鲲鹏/昇腾平台上踩过几轮的人会知道它们往往指向同一个根源链条环境的实际状态和安装脚本的预期状态之间出现了偏差而这个偏差通常藏在环境变量、软链接、依赖包和配置文件这四层里。所以这篇内容我不打算照着官方安装手册的顺序念一遍而是反过来从出问题之后怎么定位这个角度切入。原因也很简单官方手册假设的是一个干净的、从头开始的环境但现实里的服务器和开发板往往是别人装过一半、或者装过一版又覆盖安装的环境里残留的旧配置比新装的还多。这种情况下会查、会排、会判断到底哪一层的状态不对比会装重要得多。这篇内容适合三类读者第一类是第一次在银河麒麟 V10 这类国产化操作系统上部署 CANN 的开发或运维装完之后不确定到底装没装上、装对了没有第二类是遇到过nnrt --version无输出、驱动加载失败这类诡异问题网上搜到的答案都对不上号的人第三类是做交付或者现场支持的同学需要在客户现场快速判断环境是否可用不能花两小时重装。文中涉及的路径、包名以常见实践为准不同 CANN 版本之间会有差异具体以你手上的安装包为准我会在关键处标注这一块版本差异比较大的地方。注意本文只讨论常规的软件安装、环境配置、依赖排查思路所有命令都应在你有权限、有授权的前提下执行生产环境变更前建议先在测试机验证并做好快照或备份。1.2 排查之前先建立一张层次图在动手敲命令之前我习惯先在纸上或者脑子里画一张四层的状态图因为 CANN 这类软件栈的安装本质上不是复制文件而是四层状态同时要对上层级内容对应现象常用检查手段内核层昇腾相关的内核驱动模块、设备节点看不到设备、驱动加载失败lsmod、dmesg、ls /dev用户态运行时层nnrt 等运行时组件、动态库nnrt --version无输出ldd、LD_LIBRARY_PATH配置层各类.cfg、version.cfg、环境脚本版本显示不对cat、md5sum、对比安装包应用层框架插件、算子库、示例程序跑示例报错运行官方样例这张图的价值在于当你遇到任何一个具体报错时先判断它属于哪一层然后沿着层与层之间的接口去查而不是漫无目的地在整个系统里翻。比如nnrt --version无输出表面上是用户态问题但你得先确认内核层是好的因为运行时可能因为找不到设备而在初始化阶段静默退出——注意是静默没有报错、没有输出、退出码也许是 0这才是最坑的地方。2. version.cfg 到底该信哪一份查版本的五个靠谱姿势2.1 version.cfg 是什么它为什么容易骗人先解释清楚version.cfg这个东西。在 CANN 的安装目录结构里通常会有一个记录版本信息的配置文件常见的路径形态类似安装根目录/version.cfg或者各组件的独立配置文件。它的内容非常朴素一般就是键值对形式写着组件名、版本号、构建日期之类的信息。很多人查版本的第一反应就是cat这个文件然后看到一行版本号就放心了。问题恰恰出在这里。version.cfg是一个静态的文本文件它记录的是安装这个包的时候声明的版本而不是当前系统里实际生效的版本。这两者什么时候会不一致我遇到过至少三种情况第一种是覆盖安装。先装了 A 版本后来没卸载干净就装了 B 版本或者反过来降级安装。此时新包的version.cfg覆盖了旧的但某些组件目录下可能还残留着旧版本的库文件因为安装脚本不一定做彻底的清理。这时候version.cfg显示的是 B实际被程序加载的可能是 A 的.so。第二种是多版本共存。有些环境里为了兼容不同框架会同时安装两套 CANN通过环境变量切换。version.cfg只反映其中一套如果你查的是另一套目录下的文件看到的版本自然对不上你当前PATH里生效的那套。第三种是只装了一部分组件。CANN 工具箱包含很多子组件安装时可以选择性安装。如果只装了运行时没装工具链某些version.cfg可能压根不存在或者存在但信息不全你cat一下看到空文件或者只有两三行就容易误判成没装上。所以我的结论是version.cfg可以作为第一手线索但绝不能作为唯一判据。它告诉你这个目录自认为是什么版本你需要用其他手段去交叉验证。2.2 交叉验证版本的四条命令链路下面这几条是我实际排查时最常用的组合按从粗到细的顺序排列。第一看环境变量和安装根目录。CANN 安装后一般会提供一个环境设置脚本需要source之后才能用。所以在查版本之前先确认你当前 shell 里的环境变量到底指向哪个目录echo $ASCEND_HOME_PATH echo $LD_LIBRARY_PATH | tr : \n | grep -i ascend如果ASCEND_HOME_PATH是空的说明你根本没source环境脚本这时候你去查的任何版本信息都可能是错的——因为你查的可能是一个早已不在使用的旧安装目录。这个坑我踩过当时在一台别人交接的机器上/usr/local/Ascend下有两套目录我cat的是旧的那套折腾了半小时才发现环境变量指向的是另一套。第二用find定位所有 version.cfg看看到底有几套find / -name version.cfg -path *scend* 2/dev/null注意这里加了-path过滤避免扫出无关应用的同名文件。输出如果有多条每一条都值得看一眼对比它们的修改时间和内容for f in $(find / -name version.cfg -path *scend* 2/dev/null); do echo $f ls -l --time-stylelong-iso $f cat $f done第三查包管理记录。如果你是用.run或.rpm、.deb包安装的包管理器里会有安装记录这个是客观事实# RPM 系银河麒麟服务器版常见 rpm -qa | grep -i ascend # DEB 系 dpkg -l | grep -i ascend用包管理器查的好处是它能告诉你到底装了哪些包、什么版本、装到哪个路径不依赖任何配置文件的自述。如果rpm -qa里什么都查不到那基本可以确认这套环境不是通过 RPM 装的要么是.run自解压安装要么就是别人手工拷的目录——后一种情况最麻烦因为没有任何安装记录只能靠目录结构和ldd反推。第四看动态库的实际版本符号。这是最硬的证据直接问系统里被加载的库文件# 找到运行时核心库 find $ASCEND_HOME_PATH -name libascend*.so* 2/dev/null | head # 查看库文件里记录的版本信息 strings /path/to/libascend_rt.so | grep -i version | head # 用 ldd 看某个可执行文件实际链接到哪个库 ldd $(which nnrt 2/dev/null) 2/dev/null | grep -i ascendstrings输出里往往能看到构建版本号、Git 提交号之类的字符串。这比version.cfg可靠因为它是编译进二进制里的。缺点是可读性差输出需要过滤。把上面四条链路的结果放在一起对照基本就能确定真实版本是什么了。我一般会做成一个小表格记录环境变量指向的目录、version.cfg 声明的版本、包管理器记录的版本、动态库字符串里的版本。四者一致环境大概率是干净的不一致就说明有残留或混装接下来的问题排查都要带着这个前提。提示如果发现四者不一致最省事的做法不是想办法让它们对上而是把可疑的旧目录整体改名备份比如mv成xxx.bak.日期重新source环境脚本再测。很多诡异问题会因为这一步就消失了。这个操作比卸装重装安全得多也快得多。2.3 一个具体的版本对不上排查实例说个我印象比较深的例子。某次在一台银河麒麟 V10 服务器上同事反馈说装完 CANN 之后框架跑不起来提示的版本和文档要求的对不上。我上去之后的操作顺序是这样的第一步echo $ASCEND_HOME_PATH输出了一个路径 A。cat路径 A 下的version.cfg显示版本 X。第二步find一下发现系统里除了路径 A还有路径 B路径 B 下的version.cfg显示版本 Y而且路径 B 的修改时间更早。两个目录都叫类似的组件名区别只在路径里有一层版本号目录名不同。第三步rpm -qa | grep -i ascend返回空。说明不是 RPM 装的是.run方式装的。查安装日志发现在路径 B 安装之后中间失败过一次然后有人在没清理的情况下又跑了一次安装装到了路径 A。所以是两套并存。第四步ldd一下框架侧那个报错的可执行文件发现它链接的库来自路径 B。而环境变量指向的是路径 A。这就是根因环境变量指新、二进制链旧。处理方式就很简单了把路径 B 备份走重新source路径 A 的环境脚本再ldd确认链接指向问题消失。整个过程十几分钟比重新安装一遍快得多而且不影响其他已配置好的东西。这个例子说明的道理是版本问题的本质往往不是版本不对而是路径没对上。你盯着版本号数字看是看不出问题来的得去看谁在引用谁。3. 银河麒麟上找不到驱动内核层的排查逻辑与常见卡点3.1 为什么在银河麒麟上这个问题特别高发银河麒麟系统在国内的服务器和桌面环境里用得很多它本身是基于 Linux 内核的发行版但因为其安全加固策略、内核版本管理方式和软件源策略导致在它上面安装带内核模块的驱动时比在通用发行版上更容易出问题。我总结下来主要有这么几个原因。内核版本与驱动预编译包不匹配。很多驱动包是按特定内核版本预编译好的.ko文件。如果银河麒麟的内核做过安全补丁升级内核版本号变了那么针对旧内核编译的.ko要么加载时报invalid module format要么根本不会出现在预期位置。这一点在装完系统补丁之后、没有重启确认内核版本的情况下尤其容易发生。内核头文件和编译工具链缺失。如果驱动需要现场编译很多情况下确实如此而系统里没装对应内核版本的头文件包和编译器编译就会失败。失败的日志可能被安装脚本吞掉最后只给一句驱动安装失败或者更糟——脚本返回 0你以为成功了实际上模块根本没生成。安全机制拦截模块加载。一些系统会启用模块签名强制校验或者安全启动策略未签名的第三方内核模块无法加载。这种情况下modprobe会报 module verification failed 或者直接 Operation not permitted而lsmod里什么都不显示。安装脚本的依赖检查不充分。有些安装脚本在检查环境时检查的是发行版名称而不是内核 API 兼容性看到是麒麟就认为兼容实际内核 ABI 未必对得上。所以在这类系统上排查驱动问题第一件事不是看驱动包本身而是先确认内核环境和编译环境是不是满足条件。3.2 从设备节点反推驱动是否真的在工作很多人判断驱动是否安装成功习惯用lsmod | grep xxx。这个方法有用但它有个盲区有些驱动是编译进内核的built-in不会出现在lsmod列表里有些模块加载后名字被改过或者带了前缀后缀你grep的关键词可能正好不匹配。我更倾向用设备节点来判断因为这是从应用视角看的最直接证据# 看昇腾相关设备节点 ls -l /dev/ | grep -i ascend ls -l /dev/davinci* 2/dev/null ls -l /dev/devmm_svm 2/dev/null # 看内核日志里驱动加载的痕迹 dmesg | grep -i -E ascend|davinci|drv | tail -50 # 看模块加载状态带更宽的关键词 lsmod | grep -i -E ascend|davinci|drv为什么要三个一起看因为它们的组合能区分出不同的失败形态lsmod 结果设备节点可能的状态下一步动作有模块有节点驱动正常转向用户态排查有模块无节点模块加载了但初始化失败查dmesg找初始化报错无模块无节点模块压根没加载查模块文件是否存在、能否手动insmod无模块有节点模块已编译进内核正常继续用户态这个表是我实际排查时脑子里跑的判断逻辑列出来是想说明lsmod没结果不等于驱动没装得配合设备节点和内核日志一起看。特别是dmesg它是内核层最重要的证据来源。驱动加载失败时报错几乎一定会在dmesg里留下痕迹通常是版本不匹配、符号找不到、权限不足这几类。我遇到过最典型的一种情况是lsmod里能看到模块但设备节点不存在dmesg里对应的是 Failed to initialize device 之类的错误再往前翻能看到 version magic mismatch。这就非常明确了——模块文件在但编译时的内核版本和当前运行的内核版本不一致加载过程走到了初始化阶段才失败。解决方式就是重新编译模块而重新编译的前提是装上正确的内核头文件。3.3 手动编译安装驱动模块的完整链路当确认是模块版本不匹配导致的问题时现场编译是标准解法。我把这条链路完整写一遍因为中间有几个容易漏的步骤。先确认当前运行内核版本和内核头文件版本是否一致uname -r ls -l /lib/modules/$(uname -r)/build 2/dev/null ls /usr/src/ | grep -i kernel/lib/modules/$(uname -r)/build这个软链接如果不存在或者指向了一个不存在的目录说明当前内核的头文件没装或者装错版本了。这是编译驱动最常见的失败原因。通过系统包管理器装上匹配的头文件包再重试。然后确认编译器可用gcc --version make --version如果系统里只装了精简运行时、没装开发工具make可能不存在。这种情况下需要从系统源里装构建工具组。准备工作做完进入驱动源码目录执行编译。具体的编译命令随驱动包而异常见的是make或者./build.sh关键是要保留完整的编译日志不要用 /dev/null把输出丢掉出问题的时候日志就是唯一线索cd /path/to/driver/src make 21 | tee /tmp/driver_build.log编译完成后模块文件.ko通常生成在源码目录或者output子目录里。加载前先检查一下它的内核版本标记modinfo ./your_driver.ko | grep -E vermagic|name|version把vermagic那行和uname -r对比如果不一致说明编译出来的模块还是不对别急着加载先解决编译环境问题。加载测试insmod ./your_driver.ko # 或者 modprobe your_driver dmesg | tail -30 lsmod | grep your_driver这里有个细节insmod和modprobe的行为不同。insmod需要你给完整路径不会自动处理依赖modprobe会去标准模块目录找并自动加载依赖模块。测试阶段我建议先用insmod加完整路径因为这样绕开了模块没装到标准目录这个干扰因素能直接验证模块本身能不能加载。等验证通过了再用make install装到标准目录用modprobe测试这样才符合生产环境的加载方式。注意手动insmod加载的模块如果是要开机自动加载的通常还需要配置/etc/modules-load.d/下的配置文件或者发行版对应的模块加载配置否则重启后就没了。这一步在交付环境里经常被漏掉导致测试时好好的重启就失效。3.4 模块签名与安全策略导致的加载失败如果编译没问题、版本也匹配但insmod报 Operation not permitted 或 module verification failed那就要考虑模块签名或者安全策略的问题了。先看内核是否开启了模块强制签名cat /proc/sys/kernel/modules_disabled 2/dev/null dmesg | grep -i -E signature|verification|taint如果dmesg里出现模块被拒绝加载且提到签名相关的信息就是签名问题。处理路径有两条一是对模块做签名需要用到系统里的私钥这个过程比较繁琐二是确认当前环境是否允许在测试阶段临时调整相关策略——这个动作在生产环境里需要慎重必须经过环境和安全评估不能随意改。我这里想强调的是遇到这类问题时先别急着去找怎么关掉校验的教程。在国产化服务器场景里安全策略通常是刻意开启的正确的做法是走正规流程联系环境负责人确认策略是否可调或者让驱动提供方给出已签名的模块。我之前见过有人为了图快直接改了内核参数结果在一台上了审计的机器上留下了异常记录后续解释起来很麻烦。3.5 找不到驱动的三种误判除了真的驱动没装好还有一种情况是其实装好了但你判断方式不对。我总结三种常见误判。第一种只查了lsmod没查设备节点。前面说过模块可能内建或者名字不匹配这时候lsmod自然是空的但设备是能正常用的。第二种查错了设备类型。昇腾设备的节点命名有若干种不同的软件版本下可能不同。如果只grep一个固定名字很容易漏。我习惯用ls /dev/ | grep -i -E ascend|davinci|svm这样的宽匹配把相关的都列出来。第三种用户权限不足。某些设备节点只有特定用户组可访问普通用户ls是能看到的但打开会失败于是应用侧报找不到设备。这时候要检查设备节点的权限位和当前用户所属的组ls -l /dev/davinci* id groups如果是组权限问题把用户加入对应的组重新登录即可不需要动驱动本身。这个坑很典型尤其是在多用户共享的开发机上。4. nnrt --version 无输出一个静默失败的排查范式4.1 无输出比报错更难查为什么nnrt --version敲回车之后什么都不显示直接回到提示符——这可能是整篇里最让人抓头的一种情况。因为正常的失败会给你错误信息你能顺着信息去搜而无输出意味着程序在某个阶段悄悄退出了你连它是从哪个环节退出的都不知道。要理解这种现象得先知道nnrt这类命令在启动阶段经历了什么。一个典型命令行工具的执行流程是加载器加载可执行文件 → 解析动态库依赖 → 进程初始化 → 加载运行时配置 → 检查设备 → 打印信息。这个链条上任何一环失败按常理都应该报错但实际情况是有些环节的失败处理代码里错误被吞掉了或者错误信息写到了它自己的日志文件里而不是标准输出或者进程收到信号后直接退出什么也不打印。所以排查这类问题的核心思路是把无输出这个黑盒拆开逐个环节去验证它的前置条件是否满足。我一般用下面这个顺序。4.2 先看退出码再决定往哪个方向查第一条命令永远是nnrt --version echo $?退出码能告诉你很多。0 表示程序认为自己正常结束了那可能是逻辑问题比如它把版本信息写到了别处非 0 表示程序异常退出这个数字对应着系统错误码能反映大致原因。比如退出码 127 通常是命令找不到或依赖库缺失退出码 1 是通用错误退出码 139 是段错误。接下来看它到底链接了什么库有没有缺失which nnrt ldd $(which nnrt)ldd输出里如果有 not found 的行那就是直接原因——动态库缺失。但这里有个陷阱libascend_hal.so not found这类信息可能不是库真的不存在而是LD_LIBRARY_PATH没设置。所以紧接着要查环境变量echo $LD_LIBRARY_PATH echo $ASCEND_HOME_PATH find $ASCEND_HOME_PATH -name libascend_hal.so* 2/dev/null如果库里明明存在但ldd说找不到那就是LD_LIBRARY_PATH的问题。这时候把库所在目录加进去再试export LD_LIBRARY_PATH/path/to/lib:$LD_LIBRARY_PATH nnrt --version这一步能解决相当一部分无输出问题。原因是nnrt在动态链接阶段就失败了而某些情况下动态链接器的错误信息没输出到终端——虽然这不常见但在特定 shell 和终端组合下确实会发生。4.3 strace 把静默失败翻译成可读的调用链如果ldd没发现缺失、环境变量也设了、还是没输出那就要上strace了。它能把程序执行的所有系统调用打印出来让静默退出变得可见strace -f -o /tmp/nnrt_trace.log nnrt --version tail -80 /tmp/nnrt_trace.log看日志的时候重点看两处一是最后几十行的系统调用序列程序在退出的前一步做了什么二是open、openat、access这些调用里返回ENOENT文件不存在的那些路径往往能直接指出它在找哪个配置文件没找到。我举一个我实际遇到的例子。某次的strace日志最后几行是这样的内容做了简化openat(AT_FDCWD, /usr/local/Ascend/xxx/cfg/config.cfg, O_RDONLY) -1 ENOENT openat(AT_FDCWD, /etc/ascend/config.cfg, O_RDONLY) -1 ENOENT write(2, , 0) 0 exit_group(1) ?程序的逻辑是先找配置文件找不到就退出。但代码里那个write(2, , 0)——往标准错误写了个空字符串——说明它本来是想报错的但错误信息是空的所以终端上什么都看不到。这就完美解释了无输出。而根因是配置文件路径不对环境变量没指对安装目录。这个例子说明strace的威力它不告诉你应该怎么修但它告诉你程序到底在干什么剩下的就是按图索骥。4.4 日志文件往往比终端更诚实很多运行时组件不会把信息打到终端而是写到自己的日志目录里。CANN 相关的日志常见位置包括安装目录下的log目录、$HOME下的隐藏目录、以及/var/log下的一些文件。可以在执行命令后按时间排序找最新被修改的日志find /var/log $HOME /usr/local/Ascend -name *.log -mmin -5 2/dev/null这个命令找的是最近 5 分钟内被修改的日志文件执行完nnrt --version之后立刻跑能快速定位到它偷偷写了日志的那个文件。打开看内容往往就能看到被终端隐藏起来的真实错误。我特别想强调这个技巧因为在现场支持时终端无输出 日志有内容是最常见的组合。养成命令执行完立刻找最近的日志的习惯能省掉大量猜测时间。4.5 设备不可用导致的初始化退出还有一类nnrt --version无输出的原因是运行时在初始化时检查设备发现设备不可用于是直接退出。这种情况的特征是驱动层的问题还没解决你就来测用户态命令了。这也呼应了前面说的层次图——用户态命令的正常工作前提是内核层是好的。验证方法很简单先回过头去看设备节点和驱动状态用第 3 节的命令确认设备正常了再测nnrt --version。如果设备这边本来就有问题那么在用户态折腾多久都是白费。这里可以做一个排查顺序的总结确认环境变量指向正确的安装目录确认动态库依赖完整ldd无 not found确认内核驱动和设备节点正常前面三步都过了还没输出用strace和日志文件定位提示把这三步做成一个脚本每次部署完 CANN 后跑一遍能提前发现 80% 的环境问题。脚本内容不复杂就是echo环境变量、ldd、ls设备节点、dmesg过滤这几条命令的组合但比事后救火划算得多。5. 覆盖安装与升级场景下的残留清理清单5.1 为什么升级比全新安装更容易出问题全新安装面对的是干净环境升级面对的是还有旧版本残留的环境。而残留的影响面比大多数人以为的要大得多。旧版本留下的东西至少有这几类旧的可执行文件和库文件、旧的环境变量配置写在了/etc/profile.d/或者用户.bashrc里、旧的内核模块可能还在被加载、旧的配置文件、旧的日志和缓存。这些东西不是可有可无的历史文件它们会在特定条件下被系统优先使用从而造成新版本装了但没生效的现象。最典型的是环境变量如果旧的环境脚本写在/etc/profile.d/里你新装的时候只是往用户.bashrc里追加了一行那对于使用 login shell 的场景比如通过 SSH 登录/etc/profile.d/里的旧路径会先被加载新路径可能被覆盖或者顺序混乱最终生效的是旧的。5.2 一份可复用的升级前检查与升级后验证清单升级之前我习惯做这么几件事列成清单方便对照记录当前环境的所有关键变量和路径env | grep -i -E ascend|path输出存成文件留档记录当前版本信息把第 2 节那四条命令的结果存档记录当前加载的模块lsmod | grep -i ascend记录设备节点ls -l /dev/ | grep -i -E ascend|davinci|svm找出所有相关的环境脚本位置grep -rl scend /etc/profile.d/ /etc/bash.bashrc 2/dev/null备份旧的安装目录改名即可不要删除升级之后按同样的清单再采一遍把两份记录对比。差异点就是需要重点验证的地方。这个做法看起来很笨但它是排查升级后行为变化最有效的手段因为人的记忆不可靠尤其是隔着几天再回头看的时候。关于环境脚本这一点有个具体建议统一用一个独立的环境脚本文件来管理比如/etc/profile.d/ascend.sh升级时直接覆盖这个文件的内容。不要东一处西一处地追加否则时间久了没人知道环境变量是从哪来的。这个规范看着简单但在多人协作的机器上价值巨大。5.3 卸载不干净时的保守清理法如果有人问怎么彻底卸载 CANN我的建议是不要追求彻底追求可控。原因很简单CANN 安装时会往系统里写不少东西卸载脚本不一定能全部清掉而你手工删又容易误删系统文件或者别的应用依赖的文件。更稳妥的做法是保守清理第一步用包管理器卸载如果是包安装的。让包管理器先处理它能处理的# RPM 系 rpm -qa | grep -i ascend | xargs -r rpm -e --nodeps # 或按包名精确卸载 rpm -e 包名注意--nodeps的使用要谨慎它会跳过依赖检查可能留下悬空依赖。第二步把安装目录整体改名备份而不是删除mv /usr/local/Ascend /usr/local/Ascend.bak.$(date %Y%m%d)这样即使新装出了问题也能随时把旧的改回来对比。第三步清理环境变量引用。把/etc/profile.d/和相关 shell 配置文件里指向旧目录的 export 语句注释掉或者删除。注意不要误删其他应用的环境变量只动跟目标目录相关的行。第四步重启确认内核模块状态如果涉及驱动。旧的驱动模块如果还在内存里重启是最干净的清理方式。重启后用lsmod确认。这个保守清理法的核心思想是把删除替换成隔离。在排查问题的过程中隔离永远比删除安全因为你随时可以撤销。5.4 升级后必做的三项功能验证升级完成后别急着交给业务先做三项验证按顺序来第一项版本一致性验证。重新跑一遍第 2 节的四条链路确认环境变量、version.cfg、包管理记录、动态库字符串指向的是同一个版本。第二项驱动与设备验证。跑第 3 节的设备节点和模块检查最好再跑一个官方提供的最简单的设备查询命令确认设备能正常识别。第三项运行时命令验证。跑nnrt --version或者对应的版本查询命令确认有正常输出退出码为 0。这三项都过了基本可以认为升级成功的概率很高。如果其中任何一项不过按前面的层次图往对应的层去查通常很快能定位。我最想提醒的是不要跳过版本一致性验证直接跑业务程序。因为版本不一致的问题在业务程序里表现为各种莫名其妙的错误反而更难查。在环境层面花五分钟确认清楚比在业务层花五小时调试划算得多。6. 几个年复一年被问到的实操细节6.1 环境变量生效范围与登录方式的关系这个细节坑过很多人值得单独说。你在当前终端里source了环境脚本命令能用了但换一个终端、或者用别的用户登录、或者通过计划任务执行就又不行了。原因就是环境变量的作用范围。简单梳理一下export只对当前 shell 及其子进程有效写进~/.bashrc只对该用户的交互式 bash 有效写进~/.bash_profile或~/.profile只对 login shell 有效写进/etc/profile.d/对全局 login shell 有效写进/etc/environment是更底层的全局变量。所以你做部署的时候要先想清楚这个环境变量是给谁用的。如果是给交互用户用的放/etc/profile.d/基本够如果是给服务进程用的那可能要在服务的启动脚本或者 systemd 单元文件里显式声明因为服务进程通常不加载/etc/profile.d/。我见过多次手动执行没问题做成服务就不行的案例根因都在这里。6.2 磁盘空间和权限的隐藏影响这两个也是老生常谈但总有人踩的点。磁盘空间不足时安装脚本可能装到一半失败但失败信息不明显最后留下一个看起来装了实际残缺的环境。安装前检查一下目标分区的可用空间df -h /usr/local /opt $HOME权限问题则更微妙。如果用 root 安装然后用普通用户运行或者反过来会出现两类问题一是普通用户读不到某些配置文件或设备节点二是以 root 运行时路径解析到 root 的$HOME而不是你自己的环境变量就全乱了。处理原则是安装和运行尽量用同一种身份如果必须不同把权限和组配置做清楚并在两个身份下都验证一遍。共用一台开发机的时候这一点尤其重要。还有一个小细节是路径里的空格和特殊字符。虽然不常见但如果$HOME或者安装路径里有空格某些脚本会因为引号处理不当而出错。排查这类问题时可以先用绝对路径、避开空格目录来测试能排除这个因素。6.3 命令找不到和命令无输出不是一回事最后澄清一对容易混淆的现象这也是我一开始想强调的。命令找不到command not found是 shell 层面的问题PATH里没有这个命令或者命令没装。这个好解决which nnrt echo $PATH | tr : \n | grep -i ascend ls -l /path/to/nnrt而命令无输出是命令找到了、执行了但在内部某个环节静默退出了属于运行时问题要用ldd、strace、日志那一套去查。两种现象的处理路径完全不同别混在一起。区分的方法很简单执行which nnrt如果有路径输出就是无输出类问题如果没有就是找不到类问题。先做这一步判断能避免在错误的方向上浪费时间。我就见过有人拿着无输出的问题去反复检查PATH怎么查都查不出所以然因为方向从一开始就错了。6.4 关于版本升级节奏的一点个人看法做这类平台软件的环境维护我的体会是不要盲目追新版本。新版本往往带了新的功能但也可能引入新的依赖要求、新的配置方式甚至新的已知问题。特别是当你的业务框架和当前 CANN 版本已经稳定适配的时候升级的收益可能远小于风险。比较稳妥的节奏是先在一个隔离环境里跑通新版本把第 5 节那套检查清单完整走一遍确认没问题之后再规划正式环境的升级窗口。升级窗口内留足回滚方案也就是前面说的目录改名备份那套。这个流程看着保守但在实际工作中它能帮你避免的麻烦比它带来的额外工作量要多得多。我个人踩过几次为了用某个新特性仓促升级、结果业务侧出问题回滚的坑后来就基本按这个节奏来了。如果这篇内容里只能记住一句话我希望是那句版本问题的本质常常是路径问题而无输出的问题要从退出码和系统调用看起。把这两条抓在手里CANN 这类环境里的大多数安装升级问题都能有条理地查下去而不是靠碰运气重装。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →