
批量给 FusionCompute 上的 Linux 虚机装 vmtools最让人上火的不是装不上而是明明看着一切正常回车下去却弹出一行Unsupported linux distribution脚本直接退出。尤其是 Ubuntu 22.04、openEuler、Kylin、UOS 这类比较新的发行版网卡和磁盘在系统里跑得好好的偏偏工具脚本不认人。这篇就把这个报错从头到尾拆一遍这行字是谁抛出来的、它凭什么判断你的系统不受支持、改脚本的正确姿势是什么、装完之后怎么验证真的生效了以及我在这条路上踩过的几个不大不小的坑。1. 先分清是谁在说话这行报错来自脚本不是内核很多人第一反应是驱动不兼容然后去翻dmesg翻半天什么都没找到。原因很简单Unsupported linux distribution是安装脚本自己echo出来的属于发行版识别失败跟驱动能不能加载、内核认不认硬件完全是两码事。先把这个认知摆正后面的排查方向才不会跑偏。1.1 挂载工具 ISO先看清里面到底有什么FusionCompute 的 vmtools 是以 ISO 形式挂到虚拟机光驱上的。进系统后先确认光驱设备名通常是/dev/sr0lsblk mkdir -p /mnt/cdrom mount /dev/sr0 /mnt/cdrom ls -l /mnt/cdrom挂上之后你会看到一个典型的目录骨架不同版本名字略有差异但大体跑不出这几类路径作用install.sh/install_vmtools.sh主安装入口报错就是它抛的check_*/precheck*.sh环境预检发行版判断一般在里面driver/或src/需要编译的内核模块源码packages/agent 守护进程、通信组件的二进制或 rpm/deb 包readme/*.txt支持列表往往写得很老readme里那份支持列表就是问题的源头。它通常停留在脚本写成的那个年代比如只列了 CentOS 6/7、RHEL 6/7、SUSE 11/12、Ubuntu 14/16/18 这几档。你手上是 22.04它自然摇头。1.2 用 grep 把抛出错误的那几行精准揪出来与其一行行读脚本不如直接反查grep -rn Unsupported /mnt/cdrom如果脚本做了英文大小写混排或者从别的文件 source 进来可以放宽一点grep -rni unsupported /mnt/cdrom --include*.sh grep -rn os-release\|redhat-release\|lsb_release /mnt/cdrom --include*.sh第二条命令特别有用它告诉你脚本到底从哪儿读发行版信息。实操经验是绝大多数这类脚本都读/etc/os-release少数老脚本读/etc/redhat-release或者调lsb_release -i。搞清楚它读哪个文件你就知道它在对比哪个字段改起来才有方向。注意先别急着复制出来改。ISO 是只读挂载直接原地改会失败。正确做法是把整个工具目录cp -a到本地磁盘再动手保留原始 ISO 作为回退。2. 白名单为什么认不出你的系统三个字段的口径错位知道是谁在判断之后下一个问题是它凭什么。这类脚本的识别逻辑通常长这样读发行版 ID拿去和一个硬编码的枚举列表比命中就继续不命中就报错走人。看着挺合理但实际有三处特别容易出问题。2.1 发行版 ID 的写法根本不统一同一个Ubuntu在不同来源里写法五花八门。而/etc/os-release里真正权威的是ID字段不是NAMEcat /etc/os-release典型输出里IDubuntu、VERSION_ID22.04。而 Kylin 系可能是IDkylinopenEuler 是IDopenEuler注意大小写UOS 是IDuos或IDdeepin。脚本作者当年写case分支时只写了ubuntu|centos|rhel|sles后面这些一个都没覆盖所以不管你系统多正常一律落到*)分支。这就是第一层错位枚举不全。不是版本太新是压根没进名单。2.2 版本号比较是个隐藏雷区有些脚本过了 ID 检查紧接着做版本比较。如果你看到类似[ ${OS_VER} -ge 7 ]的写法那在22.04上会直接崩因为 shell 的-ge只吃整数22.04会被当成非法整数报integer expression expected。这时候你看到的可能不是Unsupported而是别的一行错但根子是同一个。还有些脚本用字符串比较[[ ${OS_VER} 9 ]]这更阴险22.04 9按字典序比结果是假因为字符2小于9。逻辑上22 应该比 9 新脚本却认为你太老。这是第二层错位比较方式错误而且它不报错只是静默走了错误分支。2.3 发行版名和版本号之外的第三个来源少数脚本会去读/etc/redhat-release或者lsb_release。在 Ubuntu 上/etc/redhat-release根本不存在脚本读空字符串然后拿着空值去匹配自然不中。如果你发现脚本明明判断的是 Ubuntu但你系统上对应的文件不存在就得考虑是不是这个原因。排查方法很直接把三个来源都打出来对一遍cat /etc/os-release 2/dev/null | head -5 cat /etc/redhat-release 2/dev/null lsb_release -a 2/dev/null uname -r四个信息一次性摆出来脚本在读哪个、拿到了什么值一目了然。这一步花三分钟能省掉后面半小时的瞎猜。3. 动手之前先想清楚你到底需不需要这个 vmtools看到报错就一头扎进去改脚本是最常见的冲动但不一定是最优解。先把缺了它到底损失什么想明白再决定走哪条路。3.1 FusionCompute 上缺了 Tools究竟少哪些能力FusionCompute 底层是虚拟化平台虚机的磁盘和网卡走的是 virtio 通道。关键点在于Linux 5.x 及以后的内核virtio 相关驱动基本都已内置。也就是说磁盘读写、网络收发这些生存必需能力跟装不装 vmtools 没关系。真正需要 agent 的是下面这些能力是否依赖 vmtools说明磁盘 I/O否内核自带 virtio-blk/virtio-scsi网络收发否内核自带 virtio-net心跳上报与 HA 判活是平台侧靠 agent 上报心跳优雅关机/重启是平台发起 ACPI 之外的软关机通道时间同步是宿主与虚机之间的时钟校正内存气球回收是超分场景下的内存动态回收性能采集CPU/内存细粒度是平台监控面板的数据来源所以决策逻辑很清楚如果你的场景需要 HA、需要从平台控制台优雅关机、需要做内存超分那 agent 必须装如果只是跑个测试环境、能 SSH 能 ping 通那可以先不动。3.2 三条路线的取舍对比我把常见做法列成表你可以直接对号入座路线做法优点代价A 改脚本装原生工具把发行版塞进白名单功能最全和平台联动最好动了厂商脚本后续升级要重做B 只用发行版仓库组件装qemu-guest-agent一类通用 guest agent稳定、好维护、随系统更新心跳等平台专有联动可能不全C 混合驱动走内核自带agent 单独装兼顾稳定与部分联动需要确认平台认不认关于路线 B 要多说一句FusionCompute 不是某些以open-vm-tools为主的那套体系工具是平台自有的所以不要照搬别的平台的教程去apt install open-vm-tools那条路在 FusionCompute 上跑不通。对应的通用方案是发行版仓库里那个通用的 guest agent 组件它提供的是标准化的宿主-虚机通信通道功能覆盖度因发行版而异。3.3 什么时候必须改脚本只要你的业务依赖平台侧心跳、依赖控制台优雅关机、或者准备开内存超分那路线 A 就跑不掉。这种情况下改脚本是绕不过去的一步下面的操作就是冲这个来的。4. 把 Ubuntu 22.04 塞进白名单一次最小化改动改脚本的原则只有一条补分支不删判断。很多教程教你直接把那行exit 1注释掉图省事但这等于把整个发行版校验逻辑关掉了。后续平台升级、脚本更新你根本不知道中间还漏了什么检查出问题极难定位。补一个明确的分支风险可控、可回退、可追溯。4.1 把工具目录拷出来并做好备份mkdir -p /root/vmtools_work cp -a /mnt/cdrom/. /root/vmtools_work/ cd /root/vmtools_work cp install.sh install.sh.bak.$(date %F)备份文件名带上日期这条习惯很重要。你改了三版之后回头看能立刻分清哪份是原始的。4.2 找到校验函数读一遍再动手用grep -n拿到行号然后用sed -n打印上下文不要用编辑器盲改grep -n Unsupported linux distribution install.sh sed -n 40,90p install.sh读的时候重点看三件事它读哪个文件、拿哪个字段、拿完怎么比。这三件搞清楚了改动范围就锁定了。假设你看到的逻辑大致是这样各版本命名不同结构类似check_os() { if [ -f /etc/os-release ]; then . /etc/os-release OS_ID${ID} OS_VER${VERSION_ID} fi case ${OS_ID} in centos|rhel|sles|ubuntu) check_version ${OS_ID} ${OS_VER} ;; *) echo Unsupported linux distribution exit 1 ;; esac }问题一目了然case里没有kylin、uos、openEuler而且即使进了ubuntu分支后面的check_version大概率还会因为22.04被卡。所以改动要分两处。4.3 补分支 绕过整数比较第一处在case里加兼容分支case ${OS_ID} in centos|rhel|sles|ubuntu) check_version ${OS_ID} ${OS_VER} ;; kylin|uos|openEuler|debian) echo compat mode: skip distro whitelist check, OS_ID${OS_ID} OS_VER${OS_VER} ;; *) echo Unsupported linux distribution exit 1 ;; esac第二处如果ubuntu分支下的check_version也存在整数比较问题最稳的做法是在调用前做个短路check_version() { # 新版内核的 virtio 已内置版本上限不做拦截 case $1 in ubuntu) return 0 ;; esac # 原有逻辑保留 [ $2 -ge 7 ] || { echo version too old: $2; exit 1; } }这里的关键是只放宽上限不取消下限。低版本系统上工具确实可能真的跑不起来让它继续拦高版本系统反正驱动在内核里拦它没意义。提示用patch或直接改文件都行但改完务必跑一次bash -n install.sh做语法检查。脚本改错一个引号报的错和原来完全无关能让你多折腾一小时。4.4 依赖和内核头文件别等编译失败才补这一步是新手最容易栽的地方。工具里的内核模块需要在目标机上现场编译而编译需要三件套编译器、make、以及和当前运行内核完全对应的头文件。在 Ubuntu 系上apt update apt install -y build-essential gcc make apt install -y linux-headers-$(uname -r)在 RHEL/CentOS 系上yum install -y gcc make kernel-devel-$(uname -r)$(uname -r)这个括号千万别省。我见过不止一次头文件装了但版本差一位uname -r是5.15.0-91-generic装的是5.15.0-88编译时头文件找不到报的错和发行版识别毫无关系排查方向直接跑偏。4.5 执行安装把日志完整留下来cd /root/vmtools_work sh install.sh 21 | tee /tmp/vmtools_install.logtee是必备动作。安装脚本的输出往往几百行终端里刷过去就没了失败时你想复盘都无从下手。有了这份日志grep -i error\|fail\|warn一下重点就出来了。装完立刻验证模块是否真的加载lsmod | grep -Ei huawei|uvp|virtio modinfo 模块名 2/dev/null | head -5如果lsmod里没有对应模块但日志里也没报错八成是模块装到了/lib/modules/$(uname -r)/extra/下但没触达加载。这时候depmod -a一下再手动modprobe试试。5. 装完不等于生效四类能力的逐个验证方法脚本退出码是 0只能说明流程走完了不能说明功能能用。这部分我建议按下面顺序一项项过。5.1 agent 进程和服务是否活着先看进程再看服务状态ps -ef | grep -i agent | grep -v grep systemctl list-units --typeservice | grep -i -E vmtools|agent|uvp如果安装脚本注册了 systemd 服务正常应该看到active (running)。如果是failedjournalctl -u 服务名 -n 50看最后几十行通常能直接看到原因比如缺依赖库、或者配置文件里的网卡名对不上。注意有些老脚本注册的是 SysV init 脚本放在/etc/init.d/下用systemctl查不到。碰到这种情况直接service 名字 status或看/etc/rc.d/rc.local里有没有追加启动项。5.2 网卡和磁盘是不是真的跑在 virtio 上这一项决定了你需不需要担心驱动问题lspci -nn | grep -i virtio ethtool -i ens3 lsblk -o NAME,SIZE,TRAN,ROTAethtool -i的driver:一行如果显示virtio_net说明网卡走的是内核标准通道跟 vmtools 里的驱动没关系。磁盘在lsblk里TRAN显示virtio或直接看不到部分 virtio-blk 不填TRAN可以用ls -l /sys/block/vda/device判断。这一步的意义在于如果网卡磁盘本来就跑在内核 virtio 上那你装 vmtools 图的是心跳和关机通道别再为了驱动去折腾。方向分清楚力气才不白费。5.3 心跳和优雅关机要在平台侧确认这一项只能从 FusionCompute 管理侧看虚机内部看不出来。观察点管理界面上该虚机的运行状态是否显示运行中且带工具状态标识在控制台点关机看系统是否能收到信号并正常走 shutdown 流程而不是硬断电。如果平台侧一直显示未安装工具或者心跳超时回去看两条agent 进程在不在、agent 配置里的通信通道地址对不对。有些环境里 agent 配置文件写死了某个 IP 或端口虚机换网段之后就失联了日志里会有连接超时的痕迹。5.4 时间同步千万别和系统自带服务打架这是个特别隐蔽的坑。如果虚机里跑着chrony或ntpd同时 vmtools 又做宿主时间同步两边同时往系统时钟里写结果就是时钟来回跳journalctl里能看到大段的时间跳变记录某些对时间敏感的数据库或证书校验会莫名其妙失败。处理原则二选一。要么关掉虚机内的 NTP 客户端让工具同步要么保留 NTP把工具的时间同步功能关掉timedatectl status systemctl status chrony 2/dev/null我一般倾向于保留 NTP因为跨主机集群的时间一致性要求更高而工具的同步是跟着宿主走的宿主自己的时钟精度未必比 NTP 源好。6. 踩过的坑与应急处置从编译失败到卸载残留下面这些是我在多个环境里反复遇到的基本都是文档里不会写的东西。现象根因处理方式模块编译报No such file or directory: /lib/modules/.../build内核头文件没装或版本不匹配装linux-headers-$(uname -r)确认/lib/modules/$(uname -r)/build是有效软链安装成功但心跳一直不通agent 配置里通信地址写死换网段后失联检查 agent 配置文件改成正确地址后重启服务系统升级内核后功能失效模块是为旧内核编译的新内核下不加载升级后重新执行一次安装流程或引入 DKMS 思路托管模块快照回滚后平台显示工具异常回滚点早于安装时间agent 状态与平台记录不一致回滚后重装一次或让平台侧重新采集一次虚机状态时钟频繁跳变应用报证书错误工具时间同步和 chrony 同时启用二选一关掉其中一个重复安装后残留旧版本行为诡异卸载没清干净新旧配置混用用脚本自带卸载入口清理再检查/etc/下残留配置6.1 内核升级之后怎么办Linux 发行版的日常补丁经常带内核升级升级完重启你之前编译的模块就跟新内核对不上了。表现是虚机还能跑网还能通但平台侧工具状态变异常。两个处理思路。省事的做法是每次内核升级后重跑一遍安装脚本。稳妥的做法是把编译出来的模块用 DKMS 托管让它在每次内核更新时自动重编apt install -y dkms # 把模块源码按 dkms 目录规范放好后执行 dkms add -m 模块名 -v 版本 dkms build -m 模块名 -v 版本 -k $(uname -r) dkms install -m 模块名 -v 版本 -k $(uname -r)DKMS 的价值在于把人肉记忆变成系统自动执行。对批量虚机来说这个差别就是几十台和几百台的运维成本差异。6.2 克隆和快照场景下的状态错乱从模板克隆出来的虚机往往继承了模板里 agent 的某些标识。如果 agent 有心跳 ID 或和平台绑定的唯一标识克隆出来的几十台机器可能带着同一个标识上报平台侧看到的就是状态飘忽。我的处理方式是模板里先不装工具或者装完之后清理掉标识文件等虚机克隆并完成主机名、网络配置的个性化之后再统一安装。多花一道工序省掉后面反复排查为什么这台心跳时有时无的时间。6.3 改脚本带来的风险要提前想好回退前面说了改脚本必须留原始备份。还想补一个更省事的做法不要改install.sh本体而是写一个包装脚本先做环境适配比如临时导出识别字段、补好依赖再调用原始脚本。这样原始文件一个字节都没动厂商出补丁时你直接覆盖就行自己写的适配层还能复用。#!/bin/bash # wrapper.sh —— 环境适配层不改动厂商脚本 set -e export COMPAT_OS_IDubuntu export COMPAT_OS_VER22.04 bash ./install.sh $6.4 卸载与切换路线的清理清单如果你是从路线 A 切到路线 B或者干脆不想装了卸载要清干净不然残留服务会一直报错刷日志systemctl stop 服务名 2/dev/null systemctl disable 服务名 2/dev/null rm -f /etc/systemd/system/服务名.service rm -rf /etc/工具配置目录 lsmod | grep -E huawei|uvp depmod -a systemctl daemon-reloaddepmod -a别漏它重建模块依赖表不做这一步下次开机可能还会尝试加载一个已经被删掉的模块日志里一堆红字。7. 最后几点个人体会真正让这个报错变简单的不是记住某一行sed命令而是先建立一个判断顺序先看报错是谁抛的脚本还是内核再看脚本读哪个字段做判断再看你依赖的功能是不是真的需要 agent。这三步走完大部分不受支持的情况你会直接判断出该改脚本还是该换路线。另外提一个容易被忽略的操作习惯任何一次安装tee一份完整日志装完立刻lsmod和进程状态各查一遍。我在批量部署时吃过脚本返回 0 但实际没生效的亏——几十台机器里混了几台悄悄失败的等到平台侧心跳告警才发现那时候再回头找日志早就被后续操作冲没了。留下日志、当场验证这两条比任何技巧都管用。