最小化安装的 CentOS 7 开机之后,屏幕上只有一行login:,鼠标点不动,分辨率固定在 800x600,从宿主机往虚拟机里拖文件没有任何反应,虚拟机的虚拟磁盘文件只会变大不会变小。这个场景我遇到过太多次了,大多数人的第一反应就是装 VMware Tools。结果把 linux.iso 挂上去、tar 包解压出来、./vmware-install.pl一敲,屏幕上刷刷刷跳出一堆What is the location of the "gcc" program on your machine?、Kernel headers for version 3.10.0-1160.el7.x86_64 were not found,然后安装脚本干脆利落地退出,留下一堆半成品配置。
这个标题看着像一篇很短的教程,实际上它踩的是两件事的交叉点:一是 CentOS 7 最小化安装把编译工具链和内核开发包几乎全砍了,二是 VMware Tools 的安装脚本本质上是一个需要现场编译内核模块的交互式程序。没有桌面环境只是表面现象,真正的麻烦在于依赖链断裂。下面这篇内容,我会把整个安装链路拆开讲清楚——每一步为什么这么做、不做会怎样、装完之后哪几个配置真正影响日常使用,以及我在反复重装虚拟机过程中攒下来的那些排查顺序。不管你是刚接触 Linux 的新手,还是已经能熟练用yum装包但被内核模块编译卡住的人,应该都能从中拿到能直接抄的步骤和能省下几小时的判断依据。
1. 没有桌面的时候,VMware Tools 到底还值不值得装
1.1 最小化 ISO 砍掉了哪些组件
CentOS 7 的最小化安装(Minimal Install)大约只有 900 多个包,相比带 GNOME 的完整安装少了将近一半。被砍掉的东西里,跟 VMware Tools 直接相关的有这么几类:编译工具链中的gcc、make,内核开发包kernel-devel和kernel-headers,以及跟图形相关的xorg-x11-*全家桶、gtk2、gtk3。此外还有一些日常排查会用到但不算必需的net-tools(导致ifconfig命令不存在)、vim-enhanced、wget等等。
很多人以为漏装的是图形界面,其实图形界面在最小化系统里根本不是问题——反正也没打算开 X。真正致命的缺失是kernel-devel。VMware Tools 的安装过程要往当前运行的内核里编译vmw_vmci、vmw_balloon、vmwgfx、vmxnet3这些模块,编译就需要对应内核版本的源码头文件。而最小化系统里一个kernel-devel都没有,脚本自然找不到目标。这是第一个也是最常见的失败点。
另外还有一个容易被忽略的细节:最小化安装的yum源默认指向外部镜像站,如果虚拟机网络还没配通(比如只挂了 host-only 网卡又没开 DHCP),你去yum install gcc会直接超时。所以整个安装流程在动手之前,必须先把"依赖从哪来"这个问题解决掉,而不是一边装一边发现问题。
1.2 无桌面环境下 VMware Tools 的真实收益点
这里我想纠正一个常见的认知偏差。网上讲 VMware Tools 的文章,第一段往往是"实现鼠标无缝切换、分辨率自适应、宿主机与虚拟机拖拽文件"。这些功能全部依赖图形界面,在最小化系统里一个都用不上。所以有人会得出"没有桌面就别装了"的结论,这个结论是错的,因为无桌面环境下 VMware Tools 的价值换了一批。
| 功能点 | 有桌面环境 | 最小化无桌面环境 | 说明 |
|---|---|---|---|
| 鼠标无缝切换 | 需要 | 不需要 | 无 X Server,鼠标本来就在终端里 |
| 分辨率自适应 | 需要 | 不需要 | 控制台文本模式分辨率固定 |
| 宿主机拖拽文件 | 需要 | 不需要 | 依赖图形界面 |
| 共享文件夹 | 需要 | 需要 | 通过vmhgfs或fuse挂载,命令行完全可用 |
| 时间同步 | 建议 | 强烈建议 | 无桌面系统长时间运行容易出现时钟漂移 |
| 内存气球回收 | 建议 | 建议 | 宿主机内存紧张时可回收虚拟机空闲页 |
| 虚拟磁盘收缩 | 需要 | 需要 | vmware-toolbox-cmd disk shrink命令行可用 |
| 优雅关机响应 | 需要 | 需要 | 宿主机发起关机时虚拟机可正常走 shutdown 流程 |
| vmxnet3 网卡优化 | 建议 | 建议 | 高吞吐场景下比 e1000 表现更稳 |
从这张表能看出来,无桌面环境下真正有价值的集中在四个地方:共享文件夹、时间同步、内存与磁盘的资源回收、关机信号的正确响应。特别是磁盘收缩这一条,做实验室环境或者反复打快照的场景里非常实用——虚拟磁盘文件只会随着写入增长,不主动收缩的话,一个原本 20G 的盘用两个月能涨到 60G 以上,宿主机硬盘很快就满了。
1.3 官方 TAR 包与 open-vm-tools 的路线选择
装 VMware Tools 有两条完全不同的路,选错了后面会走很多弯路。
第一条是官方 TAR 包路线:从 VMware Workstation 或 ESXi 挂载的 linux.iso 里取出VMwareTools-x.x.x-xxxxxxx.tar.gz,解压后跑vmware-install.pl。这条路的特点是版本和虚拟化平台严格对齐,功能最全,但需要现场编译内核模块,对依赖环境要求高。
第二条是发行版源路线:yum install open-vm-tools。open-vm-tools 是社区维护的开源实现,CentOS 7 的 base 源里就有,版本通常在 10.3 左右。它的最大优势是打包成了 RPM,内核模块由open-vm-tools-kmod或者内核自带的vmw_*驱动提供,不需要你现场配编译环境。
我的建议很明确:如果是 CentOS 7 这种还在维护期的发行版,优先走 open-vm-tools。原因不在于官方包不好,而在于最小化系统本身缺少编译环境,为了装一个工具再去补一整套 gcc + kernel-devel,链条太长,任何一个环节出问题都要从头排查。只有在两种情况下才考虑官方 TAR 包:一是宿主机平台版本很新,源里的 open-vm-tools 版本明显偏老导致功能缺失;二是需要用到某些 open-vm-tools 没打包进去的特性。
不过既然标题是"安装 VMware Tools",下面两条路我都会完整讲一遍,你可以根据自己的环境挑一条走。
2. 第一步就容易卡住:光驱挂载与离线依赖准备
2.1 虚拟机光驱没识别,后面全是白忙
很多人第一步就失败了,表现是mount /dev/cdrom /mnt/cdrom报no such file or directory,或者lsblk里根本看不到sr0。这个问题的根源不在 Linux 里,而在虚拟机设置里。
VMware 的 CD/DVD 设备有两种连接方式:一种是"使用物理驱动器",一种是"使用 ISO 映像文件"。装 VMware Tools 必须选后者,并且把路径指向 VMware 安装目录下的linux.iso(Workstation 在C:\Program Files (x86)\VMware\VMware Workstation\linux.iso这一类位置)。更关键的是下面两个勾选项:"已连接"和"启动时连接"。只勾第一个,虚拟机关机再开就断了;只勾第二个,当前这次开机不会挂载。两个都要勾。
设置完成之后进系统确认:
lsblk正常应该能看到类似这样的一行:
sr0 11:0 1 146.6M 0 rom如果看不到sr0,回到虚拟机设置重新检查连接状态。如果看得到sr0但没有/dev/cdrom这个符号链接,可以直接用/dev/sr0挂载,效果完全一样。
mkdir -p /mnt/cdrom mount -t iso9660 /dev/sr0 /mnt/cdrom挂载成功会提示mount: /dev/sr0 is write-protected, mounting read-only,这是正常现象,ISO 本来就是只读的。然后ls /mnt/cdrom应该能看到VMwareTools-10.3.23-16594550.tar.gz这个文件(版本号随 VMware 版本变化)。
顺带说一个细节:如果你的虚拟机同时挂了安装 ISO 和 tools ISO,两个都会占sr0sr1,需要确认哪个是哪个。可以用blkid /dev/sr0看卷标,tools 盘的卷标一般是VMware Tools。
2.2 用本地介质搭一个临时 yum 源
最小化系统装完,网络大概率还没通。这时候想补gcc、perl这些包,最快的方式不是折腾网络,而是把 CentOS 7 的安装 ISO 挂上去当本地源用。
假设你已经把 CentOS 7 的 DVD ISO 挂到了/dev/sr1:
mkdir -p /mnt/dvd mount -t iso9660 /dev/sr1 /mnt/dvd然后写一个本地 repo 文件:
cat > /etc/yum.repos.d/local-dvd.repo <<'EOF' [local-dvd] name=CentOS 7 Local DVD baseurl=file:///mnt/dvd gpgcheck=0 enabled=1 EOF这里gpgcheck=0是为了省掉导入 GPG key 的步骤。如果你在意完整性校验,可以把 CentOS 7 的 RPM-GPG-KEY 从 ISO 里复制出来导入,但对于一个临时编译环境来说,省这一步的收益远大于风险。
清一下缓存再验证:
yum clean all yum repolist看到local-dvd出现在列表里,说明源生效了。接下来yum install gcc make perl kernel-devel kernel-headers应该能直接装上。注意这时候如果外部网络源还配置着而且是失效状态,yum可能会在连接外网源的时候卡很久,可以先临时禁用:
yum --disablerepo="*" --enablerepo="local-dvd" install gcc make perl kernel-devel kernel-headers用--disablerepo和--enablerepo这对参数把源精确锁死,比直接改 repo 文件更安全,装完就恢复原状。
2.3 内核版本与 kernel-devel 必须一一对应
这是我在这个问题上踩过最深的一个坑,值得单独拎出来讲。kernel包和kernel-devel包是两个独立的 RPM,版本号必须完全一致,一个字都不能差。但 CentOS 7 的更新机制有一个特性:yum update装新内核时,旧内核会被保留(默认installonly_limit=5),而kernel-devel默认只保留当前最新的一个版本。
结果就是:系统实际运行的是3.10.0-1127.el7.x86_64(因为上次更新后没重启),但kernel-devel已经装成了3.10.0-1160.el7.x86_64。这时候你yum install kernel-devel装的是 1160 的头文件,而 VMware Tools 编译时找的是 1127 的头文件,直接报Kernel headers for version 3.10.0-1127.el7.x86_64 were not found。
确认方式很简单:
uname -r rpm -q kernel-devel第一条输出当前运行的内核版本,第二条输出已安装的头文件版本。两个字符串必须完全一致。不一致的话,有两个解法:
方案一是装精确版本的头文件:
yum --disablerepo="*" --enablerepo="local-dvd" install kernel-devel-$(uname -r)$(uname -r)会自动展开成当前内核版本,这样装出来的包一定对得上。如果源里没有这个版本(比如已经过了保留期),那就走方案二:重启系统进入新内核,让运行版本和头文件版本对齐。
reboot重启后再uname -r确认已经切到新内核,然后重新跑一遍安装。这个顺序千万不要搞反——先装头文件再重启,重启之后运行的内核变了,头文件又对不上了。
另外还有个小细节:光装kernel-devel还不够,kernel-headers也要有。前者是内核源码树的一部分(/usr/src/kernels/$(uname -r)),后者是用户空间编译用的头文件(/usr/include/linux)。VMware Tools 编译内核模块主要需要前者,但gcc编译某些辅助程序时会用到后者。两个一起装最省事。
3. 官方 TAR 包安装的完整链路拆解
3.1 解压位置的选择与目录结构确认
TAR 包解压的位置有讲究。我见过有人在/根目录下直接tar -xzf,结果解压出几百个文件散在根目录里,非常难看。也有人放在/mnt/cdrom里解压,那个目录是只读挂载的,直接失败。
推荐放/tmp或者/root:
mkdir -p /tmp/vmtools tar -xzf /mnt/cdrom/VMwareTools-*.tar.gz -C /tmp/vmtools cd /tmp/vmtools ls正常情况下会看到一个vmware-tools-distrib目录。进去看结构:
ls vmware-tools-distrib应该包含vmware-install.pl、bin、lib、doc、etc这些。bin目录里放的是vmware-uninstall-tools.pl等维护脚本,后面如果要卸载或者重装会用到。lib里是各种预编译的二进制和模块源码。
顺手确认一下磁盘空间,df -h /tmp。这个解压出来大概占 100 多 MB,编译过程中还会临时占用一部分空间。如果/tmp单独分区且只有 50M,就会在解压中途失败。这种情况改到/root下操作。
3.2 安装脚本逐项提问背后的含义
./vmware-install.pl是一个交互式 Perl 脚本,会连续问你十几个问题。第一次装的人容易被这一长串提问吓到,实际上绝大多数问题直接回车用默认值就行。但知道每个问题在问什么,出问题时才知道从哪查。
脚本启动后第一段是路径确认,依次问:
In which directory do you want to install the binary files? [/usr/bin] What is the directory that contains the init directories (rc0.d/ to rc6.d/)? [/etc/rc.d] In which directory do you want to install the daemon files? [/usr/sbin] In which directory do you want to install the library files? [/usr/lib/vmware-tools] The path "/usr/lib/vmware-tools" does not exist currently. ... In which directory do you want to install the documentation files? [/usr/share/doc/vmware-tools]这些默认值都是 FHS 标准路径,没有特殊需求全部回车。第二次安装时脚本会检测到目录已存在,会多问一句The path "..." already exists. Would you like to use it?,答yes。
接下来是配置目录和临时目录,同样回车。然后就到了关键的一段——编译环境检测:
Trying to find a suitable vmmon module for your running kernel. None of the pre-built vmmon modules for VMware Tools is suitable for your running kernel. Do you want this program to try to build the vmmon module for your system (you need to have a C compiler installed on your system)? [yes]这一段就是告诉你:预编译的模块不匹配当前内核,需要现场编译。这里必须答yes,然后脚本开始找gcc、cc、make和内核头文件路径。这一段是失败高发区,具体处理见下一节。
编译通过之后会问是否启用网络相关的功能、是否启动服务等等,一路回车即可。最后会提示Enjoy, --the VMware team之类的字样,表示安装完成。
如果你希望全程无人值守,可以直接用-d参数走默认值:
./vmware-install.pl -d但第一次装不建议用-d,因为你根本不知道它在哪一步出了问题。等把整个交互流程跑通一遍,再用-d批量部署会稳得多。
3.3 编译阶段的报错与逐条应对
编译阶段的报错可以按出现顺序分成三类,处理方式完全不同。
第一类:找不到编译器
What is the location of the "gcc" program on your machine? The answer "" is invalid. Please enter an answer.这说明gcc没装。不要在这里手动填路径,直接Ctrl+C退出,去装gcc和make再重来。手动填路径的做法在这个脚本里没有意义,因为它找的不是路径而是能不能执行。
第二类:找不到内核头文件
What is the location of the directory of C header files that match your running kernel? [/lib/modules/3.10.0-1160.el7.x86_64/build/include]这个默认路径/lib/modules/$(uname -r)/build其实是个软链接,指向/usr/src/kernels/$(uname -r)。如果kernel-devel没装或者版本不对,这个软链接是断的,脚本就会在默认路径下找不到linux/version.h。这时候可以手动输入一个路径试试,但根本解决还是要回到 2.3 节,把版本对齐。
确认软链接是否有效:
ls -l /lib/modules/$(uname -r)/build ls /lib/modules/$(uname -r)/build/include/linux/version.h第一条如果显示No such file or directory,说明链接指向的目标不存在。第二条如果文件不存在,说明头文件确实缺失。
第三类:编译过程中的 C 语法错误
/tmp/vmware-tools-distrib/lib/modules/source/vmnet-only/vnetUserListener.c: In function 'VNetUserListener': /tmp/.../vnetUserListener.c:55:2: error: unknown field 'ioctl' specified in initializer这类错误通常出现在宿主机平台版本和 CentOS 7 内核差距特别大的时候(比如用很新的 Workstation 版本去装很老的 CentOS 7),本质是 VMware 的模块源码用了旧内核的 API,而新内核改了接口。遇到这种情况基本无解,只能改走 open-vm-tools 路线,或者升级 CentOS 7 到较新的小版本内核。
提示:编译阶段的完整日志会被写到
/tmp/vmware-root/目录下,文件名类似vmware-config-tools-0.log。报错时不要只看终端最后两行,进这个目录翻完整日志才能定位到真正的失败点。
3.4 安装完成后的验证动作
安装脚本跑完不代表模块真的加载上了。必须做三层验证,缺一不可。
第一层,检查服务状态:
/etc/init.d/vmware-tools status正常输出vmware-guestd is running。如果提示not running,先看启动日志:
tail -50 /var/log/vmware-tools.log tail -50 /var/log/vmtoolsd.log第二层,检查内核模块:
lsmod | grep vmw预期能看到这么几行:
vmw_vmci 71262 1 vmw_balloon 13991 0 vmwgfx 177671 0 vmxnet3 59263 0vmw_vmci是虚拟机和宿主机通信的基础通道,共享文件夹和时间同步都依赖它。vmw_balloon是内存气球驱动。vmxnet3是高速虚拟网卡驱动,如果虚拟机网卡类型是 vmxnet3,装好之后这个模块会自动接管。这几个模块只要有一两个缺失,就说明编译或加载有问题。
第三层,检查工具本身能否执行:
vmware-toolbox-cmd -v能输出一串版本号就说明主程序可用。再跑一个实际功能的探测:
vmware-toolbox-cmd stat speed vmware-toolbox-cmd stat mem前者输出 CPU 频率,后者输出内存使用情况。这两个能出数,说明 guest 和 host 的通信链路是通的。
4. open-vm-tools:更贴合最小化系统的另一条路
4.1 发行版源里的版本为什么更省心
open-vm-tools 和官方 VMware Tools 的差别不在于功能多少,而在于打包方式。open-vm-tools 把内核模块单独拆成了open-vm-tools-kmod包,这个包由发行版的构建系统针对每一个发布的内核版本预编译好,随内核更新一起发布。你在 CentOS 7 上yum install的时候,拿到的就是已经针对当前内核编译好的二进制模块,不需要现场跑一遍 gcc。
这个差异对最小化系统来说意义非常大。第一,不需要装gcc、make、kernel-devel那一整套编译链,省了几百 MB 空间和一堆排查工作。第二,内核升级之后,只要yum update时把对应的kmod包一起升级,模块会自动匹配新内核,不会出现"升级完内核 VMware 工具就挂了"的情况。第三,卸载干净——yum remove open-vm-tools之后目录、服务、配置全部清掉,不像 TAR 包需要一个专门的vmware-uninstall-tools.pl脚本,而且那个脚本偶尔还会留残渣。
代价是版本会落后一些。CentOS 7 的 base 源里 open-vm-tools 大概在 10.3 这个段位,跟官方最新的 12.x 有代差。但 10.3 对 CentOS 7 来说完全够用,因为 CentOS 7 的内核本身就是 3.10,太新的工具反而可能不兼容。
4.2 子包选择与最小化安装的取舍
open-vm-tools 被拆成了好几个子包,装错了会带进来一堆不需要的东西。用yum info open-vm-tools或者yum list open-vm-tools*看全貌:
| 包名 | 作用 | 最小化系统是否必装 |
|---|---|---|
open-vm-tools | 核心守护进程和命令行工具 | 必装 |
open-vm-tools-desktop | 桌面集成,含分辨率自适应、拖拽 | 不装 |
open-vm-tools-devel | 开发头文件 | 不装 |
open-vm-tools-test | 测试工具 | 不装 |
open-vm-tools-kmod | 内核模块(部分源里叫kmod-open-vm-tools) | 视内核而定 |
最小化系统只需要装核心包:
yum --disablerepo="*" --enablerepo="local-dvd" install open-vm-tools注意open-vm-tools-desktop千万别装。这个包会依赖gtk2、gtk3、libX11一整套图形库,在最小化系统上一装就是几十个依赖包,纯粹浪费空间,而且装了也用不上——没有 X Server,桌面集成的功能一个都跑不起来。
关于kmod包,CentOS 7 的情况稍微特殊一点。3.10 内核里其实已经内置了一批 VMware 相关驱动(vmw_vmci、vmwgfx、vmxnet3都在里面),所以很多时候不装kmod包也能跑起来,只是vmhgfs(共享文件夹)和vmw_balloon(内存气球)这两个不在主线内核里,需要额外提供。CentOS 7 的处理方式是把这两个模块放在open-vm-tools主包里通过 DKMS 或者预编译的方式提供,具体看源里的打包策略。
装完之后先看模块有没有自动加载:
lsmod | grep vmw modprobe vmw_vmci modprobe vmw_balloon如果modprobe报Module not found,说明这个内核版本没有对应的预编译模块,需要回到 TAR 包路线,或者升级内核到有对应模块的版本。
4.3 与官方 TAR 包共存的冲突处理
一个非常容易踩的坑:机器上之前用 TAR 包装过 VMware Tools,后来想换成 open-vm-tools,直接yum install会失败,报文件冲突:
file /usr/bin/vmware-toolbox-cmd from install of open-vm-tools-... conflicts with file from package VMwareTools-...原因是两套东西装了完全相同的路径:/usr/bin/vmware-toolbox-cmd、/usr/sbin/vmtoolsd、/etc/vmware-tools/这些目录两个包都要写。RPM 检测到文件归属冲突就直接拒绝安装。
正确顺序是先用 VMware 自带的卸载脚本清掉旧版本:
/usr/bin/vmware-uninstall-tools.pl这个脚本会问你几个确认问题,一路yes。跑完之后检查残留:
ls /etc/vmware-tools 2>/dev/null ls /usr/lib/vmware-tools 2>/dev/null rpm -qa | grep -i vmware如果这几个路径还在,手动清掉:
rm -rf /etc/vmware-tools /usr/lib/vmware-toolsrpm -qa | grep -i vmware应该没有任何输出。有些人会把 TAR 包用rpmbuild打成 RPM 装进去,这种情况就要用rpm -e卸载。确认干净之后再yum install open-vm-tools就不会冲突了。
反过来的顺序也一样:如果先用 open-vm-tools,想换成官方 TAR 包,必须先yum remove open-vm-tools,否则 TAR 包安装脚本会在检查阶段发现已有服务在运行,直接退出。
注意:切换过程中虚拟机最好处于关机状态,或者至少先停掉服务(
systemctl stop vmtoolsd或/etc/init.d/vmware-tools stop)。服务在运行的时候文件被占用,卸载脚本可能删不干净。
5. 装完之后真正影响日常使用的几个配置
5.1 共享文件夹的挂载与开机自动生效
共享文件夹是装完工具之后第一个要配的东西,但它的挂载方式和你想的可能不一样。VMware 的共享文件夹不是自动出现在某个目录下的,需要你主动挂载一次。
先在虚拟机设置里配置好共享目录(Options 标签页 → Shared Folders → Always enabled,然后添加一个宿主机目录)。进系统确认共享名被识别到了:
vmware-hgfsclient这个命令会列出所有已配置的共享文件夹名称。如果有输出,说明通信链路是通的。如果没有输出,先检查服务状态,再检查虚拟机设置里的共享是否启用。
创建挂载点并挂载:
mkdir -p /mnt/hgfs mount -t vmhgfs .host:/ /mnt/hgfs注意挂载源写的是.host:/,不是具体的共享名。这样会把所有共享目录作为一个整体挂进来,访问/mnt/hgfs/共享名就能看到内容。如果你想只挂某一个:
mount -t vmhgfs .host:/共享名 /mnt/hgfs如果vmhgfs文件系统类型报错no such device,说明vmhgfs模块没加载,先modprobe vmhgfs试试。如果模块不存在,那就走 FUSE 方案——open-vm-tools 提供的是vmhgfs-fuse:
mount -t fuse.vmhgfs-fuse .host:/ /mnt/hgfs -o allow_otherallow_other这个参数很关键。不加的话,只有执行挂载的用户能访问挂载点,其他用户(比如 nginx 运行用户)访问会被拒绝。这在跑服务的时候会非常困扰,因为排查时会以为是自己权限给错了,实际是 FUSE 的默认行为。
要让开机自动挂载,写进/etc/fstab:
.host:/ /mnt/hgfs fuse.vmhgfs-fuse allow_other,defaults 0 0如果是vmhgfs方案:
.host:/ /mnt/hgfs vmhgfs defaults 0 0写完执行mount -a验证不报错。这里有个细节:fstab里的挂载源.host:/在系统启动早期可能还没准备好,导致启动时报错。可以加上x-systemd.automount或者nofail选项让它失败时不阻塞启动:
.host:/ /mnt/hgfs fuse.vmhgfs-fuse allow_other,defaults,nofail 0 05.2 时间同步与 chronyd 的冲突
时间漂移在虚拟机里是个老问题。宿主机 CPU 睡眠、快照恢复、负载突增都会导致 guest 的系统时钟偏移,短时间看不出来,长时间跑下来能差出几分钟,然后各种日志时间戳对不上、定时任务执行时间错乱,排查起来非常痛苦。
VMware Tools 自带时间同步功能,命令是这样的:
vmware-toolbox-cmd timesync status vmware-toolbox-cmd timesync enable第一句查看状态,第二句启用。启用后宿主机每隔一段时间会向虚拟机推送一次时间。
但这里有个严重的冲突风险:CentOS 7 默认装了chronyd服务,它也在做时间同步。两个同步机制同时开启,会互相打架——VMware Tools 把时间往一个方向拨,chronyd又把它往另一个方向拨,表现为时间反复跳动。
处理原则是二选一,绝不能两个都开。我的建议是:
- 如果虚拟机只做本地实验,不连外网,用 VMware Tools 的时间同步就够了,把
chronyd停掉:systemctl disable --now chronyd - 如果虚拟机能访问外网,或者需要和外部时间源保持一致,用
chronyd,把 VMware 时间同步关掉:vmware-toolbox-cmd timesync disable
关掉chronyd时还要注意一点:chronyd停掉后,内核的时钟同步参数可能还残留着。可以用timedatectl set-ntp false确保 NTP 同步彻底关闭,然后手动对一次时间:
date -s "2024-06-01 10:00:00" hwclock -whwclock -w是把系统时间写回硬件时钟。虚拟机里硬件时钟基本没有实际作用,但写一下能避免重启后出现诡异的时间跳变。
5.3 内存回收、磁盘收缩与快照前准备
前面说过,无桌面环境下 VMware Tools 最大的价值之一是资源回收,这部分值得单独讲清楚。
内存气球(balloon):这个机制的原理是宿主机内存紧张时,通过vmw_balloon驱动向虚拟机"借"内存——驱动会在 guest 内部申请一批页并告诉内核"这些页你可以回收了",从而减少虚拟机的实际内存占用,把物理内存让给其他虚拟机。这个过程是完全自动的,不需要任何配置。你只需要确认vmw_balloon模块加载了:
lsmod | grep balloon看不到的话手动modprobe vmw_balloon。如果报 module not found,说明这个内核没有编译该模块,需要回到第 3 节用 TAR 包重新编译。
磁盘收缩:这个需要手动触发。虚拟磁盘文件只会增长,虚拟机里删了 10G 文件,.vmdk文件并不会变小,被删的数据变成空洞留在里面。要真正收回空间,需要两步配合:
第一步,在 guest 里把空闲块标记出来。用zerofree或者直接dd写零:
dd if=/dev/zero of=/zero.fill bs=1M sync rm -f /zero.fill这个操作会把分区里所有空闲块填成零。注意需要分区有足够的空闲空间,而且要确保你写的是真正的空闲区域——在根分区上直接写会有风险,最好在单用户模式下做:
systemctl isolate rescue.target第二步,在宿主机上对虚拟机执行 shrink。Workstation 是虚拟机 → 管理 → 清理磁盘,ESXi 是在命令行用vmkfstools --punchzero。TAR 包安装的情况下也可以用命令行工具:
vmware-toolbox-cmd disk shrink /这个命令会先统计可回收空间,然后逐步收缩。收缩过程中虚拟机会比较卡,别在生产环境里做。
快照前的准备:打快照之前最好先停掉服务、清空缓存、把内存气球调到一个稳定值。不然快照里保存的是一个"内存正在被回收"的瞬时状态,恢复后可能出现各种奇怪的资源占用。简单做法:
sync echo 3 > /proc/sys/vm/drop_caches然后停掉数据库之类的服务,再打快照。恢复后重新启动服务。
6. 反复踩到的几个坑与完整排查链路
6.1 挂载失败时的确认顺序
mount /dev/cdrom /mnt/cdrom失败是最高频的问题,但报错信息往往很模糊。我总结了一个从外到内的确认顺序,按这个顺序走基本能定位到根因,不用瞎猜。
第一步,确认虚拟机层面设备存在:
lsblk lspci | grep -i idelsblk看不到sr0,说明光驱在虚拟机层面就没挂上,问题在虚拟机设置,进 Linux 里怎么折腾都是白费。这一步先做,能省掉 80% 的无用功。
第二步,确认设备节点存在:
ls -l /dev/sr0 /dev/cdrom/dev/cdrom是个软链接,指向/dev/sr0。如果软链接丢了但/dev/sr0在,直接用后者。
第三步,确认设备上有没有可挂载的文件系统:
blkid /dev/sr0正常应该输出UUID="..." TYPE="iso9660" LABEL="VMware Tools"。如果输出空白,说明 ISO 文件本身有问题或者光驱读到的是空盘。
第四步,确认挂载点状态:
mountpoint /mnt/cdrom如果显示is a mountpoint,说明已经挂载了,mount会报already mounted。这时候先umount再重新挂。
第五步,确认内核有没有报错:
dmesg | tail -20光驱相关的问题会在这里留下痕迹,比如sr 11:0:0:0: [sr0] Device not ready之类的。这条信息比mount的报错信息详细得多。
按这五步走完,问题一定在某个具体环节,不会出现"什么都对了但就是不行"的情况。
6.2 kernel headers 找不到的定位过程
这个报错的原始信息是:
The kernel defined by this directory of header files does not have the same address space size as your running kernel.或者前面提到的:
Kernel headers for version 3.10.0-1160.el7.x86_64 were not found.这两种报错看着不一样,其实根因都是版本不匹配。完整的定位链路是这样的。
先看当前运行内核:
uname -r假设输出3.10.0-1127.el7.x86_64。
再看头文件包装的是哪个版本:
rpm -qa | grep kernel-devel如果输出kernel-devel-3.10.0-1160.el7.x86_64,两个版本号一对比就发现问题了:运行的是 1127,装的头文件是 1160。这就是典型的"更新后没重启"场景。
再确认软链接指向:
ls -l /lib/modules/$(uname -r)/build如果显示build -> /usr/src/kernels/3.10.0-1127.el7.x86_64,但ls /usr/src/kernels/里只有3.10.0-1160.el7.x86_64,那这个软链接就是断的,编译必然失败。
修复有两条路。第一条是装精确版本:
yum install kernel-devel-$(uname -r) kernel-headers-$(uname -r)第二条是重启切到新内核。这里我要强调一个顺序问题:如果你决定重启,就不要先去装旧版本的头文件。很多人先装了 1127 的头文件,然后一重启,运行内核变成 1160,头文件又对不上了,白折腾一圈。先想清楚是"迁就当前内核"还是"升级到新内核",选一条走到底。
重启之后再验证一遍:
uname -r rpm -q kernel-devel ls /lib/modules/$(uname -r)/build/include/linux/version.h三个命令的输出必须自洽。第三条能列出文件,才说明头文件真的到位了。
6.3 内核升级后工具失效的补救
这个坑很隐蔽,因为它的表现不是"装不上",而是"装好了,用了一段时间,突然不好使了"。典型症状是共享文件夹里ls报Transport endpoint is not connected,或者vmware-toolbox-cmd执行返回Error: Could not get stat for the host。
根因是内核升级之后,原来的vmw_*模块是针对旧内核编译的,加载不上。而升级的时候如果没注意,kernel-devel也可能跟着变了,但模块没有重新编译。
确认方式:
uname -r lsmod | grep vmw如果lsmod里一个vmw模块都没有,说明全部加载失败了。再看dmesg | grep -i vmw,通常会有module verification failed或者version magic '3.10.0-1127.el7.x86_64' should be '3.10.0-1160.el7.x86_64'这样的报错,明确指出模块版本和内核版本不一致。
TAR 包安装的情况,重新跑一遍配置脚本就能重新编译:
/usr/bin/vmware-config-tools.pl这个脚本和vmware-install.pl的区别是它只做配置和编译,不改动安装路径,跑起来更快。它会重新检测当前内核,重新编译模块。跑完之后modprobe一下验证:
modprobe vmw_vmci modprobe vmw_balloon lsmod | grep vmwopen-vm-tools 的情况更简单,升级内核的时候把 kmod 包一起带上:
yum update kernel open-vm-toolsyum会自动处理依赖关系,把对应新内核的模块包装上。如果用的是 DKMS 方案(open-vm-tools依赖dkms的情况),内核升级时会自动触发重新编译,可以看 DKMS 的日志确认:
dkms status输出里每个模块后面应该有installed字样。如果是built但没installed,说明编译成功但没装进新内核,手动跑:
dkms autoinstall我个人在实际操作中的体会是,虚拟机里的内核能不升级就不升级。CentOS 7 本身已经进入维护末期,新内核带来的收益很有限,但每次升级都要重新处理一遍 VMware 模块的兼容问题。如果确实需要打安全补丁,可以在升级前先确认kernel-devel和工具包都有对应新版本可装,一次性升完,别分几次做。另外,给虚拟机打快照之前先把工具链的问题处理干净,恢复快照之后就能直接继续用,省得每次都要从头排查一遍模块加载状态。