VMware Ubuntu 24.04 虚拟机从 124 GB 压缩到 33 GB:快照合并、VMDK 占用与 disk shrink 实战

在这里插入图片描述

摘要:记录 Ubuntu 24.04 在 VMware Workstation 中从约 124 GB 收缩到约 33 GB 的过程,解释快照合并、VMDK 分片、Guest 空闲块与 VMware Tools disk shrink 的关系。

@[toc]
在 VMware Workstation 中长期使用 Ubuntu 后,很容易遇到一个看起来矛盾的问题:Ubuntu 内部明明只用了三十多 GB,Windows 上保存虚拟机的目录却已经膨胀到一百多 GB;即使在 Ubuntu 中删除了大量文件,宿主机上的 VMDK 也不会同步缩小。

这次实际环境就是这样:Windows 上的虚拟机目录一度约 124 GB,而 Ubuntu 根文件系统实际只使用约 29~32 GB。处理中还出现了一个很容易误判的阶段:删除快照,并在 VMware 中执行压缩和碎片整理后,虚拟磁盘的“当前大小”反而变成了 88.4 GB

最终,在确认快照链已经合并、Guest 文件系统只有约 32 GB 有效数据后,通过 VMware Tools 的 disk shrink / 将宿主侧实际占用收缩到约 33 GB

这次问题的关键不是某一条“清理命令”,而是先分清三个完全不同的容量概念:Guest 文件系统使用量、VMDK 已分配空间、虚拟磁盘逻辑容量

1. 先看最终现象:197 GB 的文件系统只用了 32 GB

Ubuntu 内部最终确认到的根文件系统状态是:

1
2
3
$ df -h /
文件系统 大小 已用 可用 已用% 挂载点
/dev/sda2 197G 32G 157G 17% /

也就是说:

  • /dev/sda2 的逻辑容量约 197 GB;
  • 当前有效数据约 32 GB;
  • 文件系统仍有约 157 GB 可用空间。

与此同时,VMware 配置中的虚拟磁盘最大容量是 200 GB,而 Windows 上整个虚拟机目录此前约 124 GB

这几个数字并不冲突,因为它们统计的不是同一层:

数值 所在层 含义 本次案例
df -h / 已用空间 Ubuntu/ext4 当前仍属于有效文件的数据块 约 29~32 GB
VMware“当前大小” VMDK 宿主机已经为虚拟磁盘实际分配的空间 中间阶段 88.4 GB
VMware“最大大小” 虚拟磁盘逻辑容量 Guest 最多能看到/使用的磁盘容量 200 GB
Windows 虚拟机目录 NTFS/宿主机 VMDK、快照、内存状态、日志等文件总和 最初约 124 GB

因此,“最大大小 200 GB”并不意味着 Windows 必须立即占 200 GB。真正的问题是:历史上已经写入过、后来又在 Ubuntu 中释放的块,是否已经被转换成宿主机可以真正回收的空间。

2. 第一处关键线索:当前磁盘并不在基础 VMDK 上

最初查看 VMware 的硬盘设置时,磁盘文件指向的是:

1
E:\ubuntu24.04\Ubuntu24.04-000002.vmdk

而不是基础磁盘:

1
E:\ubuntu24.04\Ubuntu24.04.vmdk

同时目录中存在两组明显不同的 VMDK 分片:

1
2
3
Ubuntu24.04-s001.vmdk
Ubuntu24.04-s002.vmdk
...

以及:

1
2
3
Ubuntu24.04-000002-s001.vmdk
Ubuntu24.04-000002-s002.vmdk
...

这里最容易误判的是 -s001 这种名字。对于“硬盘内容存储在多个文件”的 VMware 虚拟磁盘,Ubuntu24.04-s001.vmdk-s002.vmdk 等本身就是基础 VMDK 的分片,并不等价于快照。

真正表明当前位于快照增量层的是前面的 -000002

下面这张图把当时的磁盘关系拆开来看:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基础 VMDK 提供父磁盘数据;Ubuntu24.04-000002.vmdk 及其 -sxxx 分片记录快照之后的增量变化。当前 Guest 看到的文件系统视图,是基础层与当前增量层组合后的结果。

这也解释了为什么当时 VMware 界面显示的“当前大小 26.6 GB”不能直接拿去和 Windows 整个虚拟机目录比较:26.6 GB 只是当前增量层相关的已分配空间,父磁盘仍然必须保留。

3. 删除快照后,为什么“当前大小”反而变成 88.4 GB

在确认旧快照不再需要后,通过 VMware 的快照管理功能删除快照,并等待其完成合并。

完成后,硬盘设置重新指向:

1
E:\ubuntu24.04\Ubuntu24.04.vmdk

原来的:

1
2
Ubuntu24.04-000002.vmdk
Ubuntu24.04-000002-sxxx.vmdk

不再作为当前增量层存在。

但此时 VMware 显示:

1
2
当前大小:88.4 GB
最大大小:200 GB

表面上看,26.6 GB → 88.4 GB 像是“删除快照把磁盘弄大了”,实际上这是快照合并后的正常现象。

删除快照并不是简单把 delta 文件扔掉。为了保留当前系统状态,快照期间产生的有效数据必须被合并回父磁盘。于是原来分散在“基础层 + 增量层”中的有效块重新收敛到基础 VMDK 中,VMware 此时统计到的基础盘已分配空间自然会变大。

目录中大量 Ubuntu24.04-sxxx.vmdk 在快照删除期间连续更新,也与这一合并过程一致。

所以,88.4 GB 并不代表快照删除失败,而是说明快照链问题已经解决,接下来要处理的是 VMDK 内仍然占用宿主空间的历史数据块。

4. 为什么碎片整理和“压缩”之后仍然远大于 32 GB

在快照删除完成后,已经尝试过 VMware 界面中的碎片整理和压缩,但“当前大小”仍然约 88.4 GB。

原因在于 Linux 文件删除与宿主机空间回收之间还隔着一层。

在 ext4 中删除文件后,文件系统主要做的是把对应块重新标记为空闲。对 Ubuntu 来说,这些块已经可以重新利用;但块里面过去的数据内容并不会因为“文件被删除”就自动变成 VMware 一定可以丢弃的宿主机空洞。

可以把这一关系理解为:

1
2
3
4
5
6
7
8
9
Ubuntu 删除文件

ext4 将对应逻辑块标记为空闲

VMDK 过去已经为这些块分配过宿主机空间

如果没有进一步的 discard / wipe / shrink 信息

宿主机上的 VMDK 文件未必会缩小

所以本案例中真正要解决的是:让 VMware 明确知道哪些 Guest 空闲区域已经没有有效数据,可以安全地从 VMDK 的实际分配空间中回收。

5. fstrim -av 没有输出,但这不是最终阻塞点

Ubuntu 中先尝试执行:

1
sudo fstrim -av

实际结果没有输出。

这里不应该凭空把它解释为“文件系统异常”或者“TRIM 一定失败”。从现有证据只能确认:这次命令没有得到可报告的 TRIM/DISCARD 输出。

更重要的是,本次环境中的 VMware Tools 本身提供了磁盘 shrink 能力,因此最终空间回收并不依赖 fstrim 成功返回某个 trimmed 数值。

确认 VMware Tools 版本:

1
2
$ vmware-toolbox-cmd -v
13.0.10.0 (build-25056151)

查看磁盘相关子命令:

1
2
3
4
5
6
7
8
9
$ vmware-toolbox-cmd help disk
disk: perform disk shrink operations
Usage: vmware-toolbox-cmd disk <subcommand> [args]

Subcommands:
list: list available locations
shrink <location>: wipes and shrinks a file system at the given location
shrinkonly: shrinks all disks
wipe <location>: wipes a file system at the given location

这里最关键的一行是:

1
shrink <location>: wipes and shrinks a file system at the given location

它明确说明 shrink 不只是宿主机侧再执行一次“压缩”,而是会先处理文件系统空闲区域,再完成虚拟磁盘收缩。

首次直接执行:

1
vmware-toolbox-cmd disk list

得到:

1
vmware-toolbox-cmd: You must be root to perform disk operations.

这不是工具损坏,而是权限要求。磁盘操作需要 root,因此后续使用:

1
sudo vmware-toolbox-cmd disk list

以及:

1
sudo vmware-toolbox-cmd disk shrink /

6. 实际空间回收路径:124 GB → 88.4 GB → 33 GB

把整个过程放在一起看,关键步骤不是“反复点压缩”,而是先处理快照链,再把 Guest 空闲区域真正转化为 VMware 可以回收的 VMDK 空间。

在这里插入图片描述

本次实际顺序可以归纳为以下几步。

6.1 先确认 Guest 里确实存在大量空闲空间

1
df -h /

结果:

1
/dev/sda2       197G   32G  157G   17% /

如果 Guest 本身已经使用 150 GB,就不应该期待 VMDK 最终收缩到三十多 GB。这个检查决定了后续空间回收是否有意义。

6.2 处理快照链,不手工删除任何 VMDK

当前磁盘如果仍指向:

1
Ubuntu24.04-00000x.vmdk

应优先通过 VMware 的快照管理器删除不再需要的快照,让 VMware 自己完成增量数据合并。

不要直接从资源管理器删除:

1
2
3
Ubuntu24.04-000002.vmdk
Ubuntu24.04-000002-s001.vmdk
...

也不要因为基础盘分片很多,就误删:

1
2
3
Ubuntu24.04-s001.vmdk
Ubuntu24.04-s002.vmdk
...

这两类文件承担的角色不同,但都不应该靠文件名猜测后手工清理。

6.3 快照合并后重新确认基础盘状态

本次合并完成后:

1
2
3
磁盘文件:Ubuntu24.04.vmdk
当前大小:88.4 GB
最大大小:200 GB

这里的 88.4 GB 是下一步需要回收的 VMDK 已分配空间,而不是 Guest 当前有效文件量。

6.4 用 VMware Tools 从 Guest 侧执行 shrink

确认工具支持 disk shrink 后执行:

1
sudo vmware-toolbox-cmd disk shrink /

执行期间不要强制关闭 VMware、暂停虚拟机或 Ctrl+C 中断。这个操作会处理大量文件系统空闲块,中间过程可能明显慢于普通命令。

本次实际执行最终成功,宿主侧虚拟机占用收缩到约:

1
33 GB

与 Ubuntu 内部约 32 GB 的实际使用量已经处于同一数量级。

7. 为什么最终不是“精确 32 GB”

虚拟机最终约 33 GB,而 df -h / 显示已使用约 32 GB,这已经是非常合理的结果。

不应该把目标理解为:

1
2
Guest 已用 32 GB
=> VMDK 必须精确等于 32 GB

两者之间还可能存在:

  • ext4 文件系统元数据;
  • swap 文件或其他 Guest 数据;
  • VMDK descriptor 与 extent 元数据;
  • VMware 的分配粒度;
  • 虚拟机目录中的配置、日志等其他文件。

判断是否成功,更合理的标准是:VMDK 是否已经从明显偏离 Guest 有效数据量的历史膨胀状态,回落到与当前有效数据量接近的数量级。

本次从约 124 GB 回落到约 33 GB,已经满足这一目标。

8. 为什么没必要把 200 GB 逻辑磁盘继续改成 50 GB

空间回收完成后,VMware 中仍然显示:

1
最大大小:200 GB

这是正常且合理的。

此时的状态可以同时成立:

1
2
Guest 最多可使用:200 GB
宿主机当前实际占用:约 33 GB

动态增长磁盘的价值就在这里:逻辑容量可以预留得较大,而宿主机只为真正写入并仍需保留的数据承担实际空间。

如果继续要求把逻辑容量从 200 GB 改成 50 GB,问题就不再是“回收 VMDK 空闲块”,而会涉及:

  1. ext4 文件系统缩容;
  2. 分区边界缩容;
  3. 虚拟磁盘容器缩容或迁移到更小的新盘;
  4. 分区表、启动链和数据完整性检查。

这条路线的风险和复杂度明显更高,而对当前问题没有实际收益。因此本案例在宿主占用已经回落到约 33 GB 后,就没有继续缩小 200 GB 逻辑上限的必要。

9. 这次排查真正需要记住的三层关系

这次问题表面上是“为什么虚拟机这么大”,真正容易混淆的是不同层的容量数字。

可以把它们分成三层:

层级 关注的问题 本次对应现象
Ubuntu/ext4 哪些逻辑块仍属于有效文件 df -h / 约 32 GB 已用
VMware/VMDK 哪些虚拟磁盘块已经在宿主机实际分配 快照合并后约 88.4 GB
Windows/NTFS 整个虚拟机目录实际占多少宿主机空间 最初约 124 GB,最终约 33 GB

文件在 Ubuntu 中被删除,只完成了第一层状态变化;删除快照解决的是 VMDK 版本链与父子依赖;vmware-toolbox-cmd disk shrink / 才把 Guest 的空闲区域进一步转换成宿主机能够实际回收的 VMDK 空间。

以后再遇到“Ubuntu 明明没用多少空间,VMware 为什么占这么大”的问题,先按这个顺序判断:

  1. 当前看到的数字到底是 Guest 已用空间、VMDK 当前大小,还是虚拟磁盘最大容量;
  2. 当前磁盘是否仍然指向 -00000x.vmdk 快照增量层;
  3. 快照合并完成后,Guest 空闲区域是否真正经过 VMware 能识别的 shrink/回收路径。

只要把这三层拆开,124 GB → 88.4 GB → 33 GB 这组看似反常的数字就能完整解释清楚。