VMware Thin磁盘空间回收:VMFS5打洞与vMotion迁移实战
简介:这份文档面向使用 VMware 虚拟化平台、并受精简磁盘(Thin)空间膨胀困扰的运维人员与虚拟化学习者,聚焦 VMFS5 文件系统不支持自动回收空间这一痛点,讲解手工回收 VM 精简卷容量的可行路径。压缩包内仅含 1 个 docx 文件,约 408KB,篇幅紧凑、以图文步骤为主,便于在故障排查现场直接查阅。文档以实际环境为例(虚拟机实际占用 27.8G、VMDK 却达 36.7G),完整记录了 SDelete 清零空闲空间配合 vmkfstools -K 离线回收,以及通过 Storage VMotion 切换厚置备延迟归零再迁回 Thin 的在线方案,并附有工具下载地址、命令写法与执行过程中的等待要点。已有 2021 人学习下载,适合需要快速定位空间浪费原因、建立回收操作流程的初中级管理员参考。
1. 一台只跑业务系统的虚拟机占用了 36.7G,客机只显示 27.8G
一台 Server 2012 R2 虚拟机,客机里查看磁盘实际使用 27.8G,数据存储上那个 VMDK 却稳稳占着 36.7G。日志清了两轮、安装包删了一批,客机数字降下来了,VMDK 的占用却一动不动。做 VMware 精简磁盘空间回收这件事,几乎都是从这种“账对不上”的场面开始的:Thin 磁盘只在写入时分配块,删除动作在客机文件系统里完成,到 VMFS 这一层就断了线,被删掉的块依然挂在原来那个 VMDK 名下。
这套方案要解决的就是最后这一步:让已经删掉的数据真正在数据存储上退租。它适合两类人:一类是手里攒了一批 Thin 虚拟机、数据存储余量已经开始告警的运维;另一类是要交一份存储空间治理方案的学生或初级工程师,需要既有原理、又能照着自己做一遍、还能拿出验收证据。下面按“空间账怎么算、两条回收路线怎么落地、哪些地方容易翻车、最后怎么证明回收生效”的顺序往下推。
2. Thin 磁盘的空间账本:三块容量口径与 VMFS5 的回收缺口
要判断一次回收到底有没有用,先得把“空间”这个词拆成三份,因为它们根本不是同一个数。三份口径对不上,是所有后续排查的起点,也是最容易被忽略的一步。
2.1 三块容量口径:客机已用、VMDK 占用、数据存储剩余
客机口径是文件系统自己记的账。Windows 的卷属性、Linux 的df -h,报的都是“文件系统认为已分配的块”。文件删掉的那一刻,这个数字就往下走了。
VMDK 口径是 VMFS 记的账。数据存储浏览器里看到的.vmdk文件大小,或者 SSH 上去ls -l出来的字节数,反映的是这块磁盘在 VMFS 上真实占住、已经被分配出去的块。Thin 磁盘的分配只增不减,块一旦分给它,除非显式回收,否则永远算在它头上。
数据存储口径是整卷的账。卷的已用容量 = 所有 VMDK + 快照 delta + swap 文件 + 日志。这一层最容易吓人,因为它把快照的占用也算进去了,经常出现“虚拟机才 40G,数据存储少了 200G”的情况,多数时候是快照链在吃空间。
正常情况下,客机口径 ≤ VMDK 口径 ≤ 数据存储口径,三者不该差太多。差出几个 G 甚至十几个 G,通常来自这几个方向:被删除但还被进程句柄占用的文件、NTFS 的 MFT 保留区、页面文件和休眠文件、系统还原点、以及最关键的——Thin 已经分配出去却再也没被使用的块。前几项在客机侧就能处理,最后一项必须回到存储层动手,这就是回收要解决的问题。
2.2 VMFS5 为什么把回收这件事留给了人工
Thin 的分配逻辑本身没有问题:写多少分多少,不写不占。问题在于“退租”这个反向动作需要有明确的信号告诉存储层“这块我不要了”,而这个信号在 VMFS5 上是缺位的。
客机文件系统删除文件时,只在自己的元数据里把块标记为空闲,不会主动去通知底层存储。要让存储知道,中间需要一条 UNMAP/TRIM 的通道,客机发得出来、虚拟磁盘控制器传得过去、VMFS 收得下并且真的把块还回空闲池。VMFS5 这条链路是不完整的:卷层没有后台回收线程,也没有面向客机的透传能力,唯一可用的入口就是 ESXi 命令行上那个显式的打洞工具vmkfstools -K。它做的事很朴素——扫描 VMDK,把全零的块标记为未分配,还回数据存储的空闲池。
到了 VMFS6,卷层才把自动 UNMAP 做进来,还配了esxcli storage vmfs unmap这样的手动触发入口,可以按数据存储粒度发起回收,并做节流。所以如果你在 VMFS5 的卷上执行 unmap 命令,报“Operation not supported”是正常的版本差异,不是你命令写错了。这也是为什么输入里那套流程必须走vmkfstools -K——它不是绕路,是 VMFS5 上唯一的正路。
提示:先确认卷是 VMFS5 还是 VMFS6,再决定走哪条命令。版本没确认就上手,是这个场景里最常见的无效劳动。
2.3 两条回收路线的取舍:离线清零与在线迁移
明确了缺口在存储层,接下来有两条路可以补。
第一条是离线清零。先在客机内把空闲空间写成零,让“哪些块是垃圾”这件事在 VMDK 内容里变得可识别;关机之后在 ESXi 上跑vmkfstools -K,把这些零块打洞还回去。它的好处是精准、不需要额外的存储空间、对存储阵列没有特殊要求,代价是必须停机,而且停机窗口的长度取决于 VMDK 有多大、后端存储的 IO 能力如何。
第二条是在线迁移。用 Storage vMotion 把虚拟机迁到另一个数据存储,迁移过程中磁盘格式选厚置备,新生成的磁盘会按“真实有数据的块”重新分配;迁完再迁回来,格式选回 Thin,占用自然就降下来了。它的好处是不停机,坏处是目标存储必须能放下按配置大小算的厚置备磁盘,而且来回两趟的迁移时间不短。
两条路不是互斥的,实际项目里经常先用迁移处理不能停机的业务机,再用离线清零处理可以约时间窗的机器。选哪条,看的是“能不能停”和“有没有第二块存储”这两个条件,而不是哪条更先进。
3. 方案一落地:SDelete 清零配合 vmkfstools -K 的完整流程
这条路线四步走:检查、清零、打洞、验证。每一步都有前置条件,跳过任何一步都会在最后一步卡住。
3.1 动手前的四项检查
第一项是快照。虚拟机上挂着快照时,磁盘写入会落到 delta 文件里,vmkfstools -K对基础磁盘打洞要么直接失败,要么打完洞空间也释放不出来,因为占空间的是 delta 不是基础盘。有快照的,先在快照管理器里全部删除并等待合并完成,再往下走。
第二项是客机空闲空间。SDelete -z的做法是往空闲块里写零,空闲空间太少会让这一步跑得极慢,而且写零本身会临时占用更多空间。通常的做法是保证客机至少有两成以上空闲,实在腾不出来,就先扩一块临时盘或者迁走一部分数据。
第三项是 SSH 服务和执行窗口。ESXi 主机默认关闭 SSH,在主机管理界面的服务里启动,并把启动策略改成“随主机启动”或手动启动,用完记得关掉。同时确认这个时间窗内虚拟机可以关机,业务侧已经通知到位。
第四项是记录基线。回收前后要有对比,基线就得提前记。在 ESXi 上把这个虚拟机的目录列一遍,把 VMDK 的字节数抄下来,数据存储的已用容量也截个图。
# 列出所有虚拟机,拿到目标虚拟机的 Vmid vim-cmd vmsvc/getallvms # 查快照,输出里没有有效的 Snapshot Name 才算干净 vim-cmd vmsvc/snapshot.get 12 # 查电源状态,必须是 Powered off 才能进下一步 vim-cmd vmsvc/power.getstate 12 # 记录基线:VMDK 实际占用与数据存储剩余 ls -l /vmfs/volumes/datastore1/VM-APP-01/ df -h /vmfs/volumes/datastore1getallvms输出的第一列就是 Vmid,后面几个命令都要用它。snapshot.get在没有快照时输出里不会出现有效的快照名,只会有分隔行,看输出时别被格式误导。df -h看的是整卷,ls -l看的是单个文件,两个数都要记,因为最后验收时它们要能互相印证。
3.2 SDelete 参数选择与清零执行
SDelete 是 Sysinternals 套件里的一个命令行小工具,用途是清理逻辑磁盘上的空闲空间。它有两个常用模式:-c清理可用空间(会反复覆写,更彻底但更慢),-z清零空闲空间(只写一遍零,速度快,正好适合虚拟化场景)。我们这里的目的是让 VMDK 里的空闲块变成全零,让vmkfstools -K能识别出来,所以选-z。
把sdelete64.exe拷到客机的 C 盘根目录,用管理员权限开命令提示符执行:
:: -z 清零空闲空间,只写一遍零,速度快,适合配合 vmkfstools -K :: -p 1 指定覆写次数,配合 -z 时用默认值即可 :: -s 递归子目录(-z 模式下一般不需要,仅在清理目录时使用) :: -q 安静模式,不输出每个文件的处理信息 sdelete64.exe -z C:执行后会看到一个百分比进度条,走完就结束了。这一步的耗时和空闲空间大小、磁盘 IO 直接相关,几十 G 空闲空间跑十几分钟到半小时都算正常,如果卡在某个百分比长时间不动,先去看客机磁盘队列和存储侧的 IO 延迟,而不是急着重启。
需要强调一点:-z只写空闲块,不动任何有效数据,所以不需要额外的备份动作;但它会大量消耗 IO,跑在业务时段可能拖慢同存储上的其他虚拟机,能约到低峰期就约低峰期。
多分区的虚拟机要逐个处理。测试环境里只有 C 盘,一条命令就够;生产机上常见 C、D 两个分区,-z C:之后还得-z D:,漏掉哪个分区,那个分区对应的那部分 VMDK 空间就回收不掉——这也是后面排查章节里会反复出现的一个坑。
3.3 关机后在 ESXi 上执行 vmkfstools -K
清零完成,客机内的工作就结束了。接下来关机,然后通过 SSH 登录到虚拟机所在的 ESXi 主机,进到虚拟机目录执行打洞。
# 进入虚拟机所在目录,目录名一般就是虚拟机显示名 cd /vmfs/volumes/datastore1/VM-APP-01 # 再确认一次电源状态,必须是 Powered off vim-cmd vmsvc/power.getstate 12 # 执行打洞:-K 等价于 --punchzero,把全零块标记为未分配 # 参数是具体的 vmdk 文件名,不要用 *.vmdk 一把梭 vmkfstools -K VM-APP-01.vmdk-K的作用是把 VMDK 里内容全零的块打洞,还回数据存储的空闲池。这个操作只对 Thin 格式的磁盘有效,厚置备磁盘跑它没有意义。执行时会有百分比进度,进度条走完会打印一句完成信息,中间不要 Ctrl+C 中断——中断之后磁盘文件本身不会损坏,但已经打过的洞不会回滚,下次还得从头扫一遍,白等一轮。
关于*.vmdk:目录里通常不止一个 vmdk 文件,可能还有-flat.vmdk、快照 delta、以及独立磁盘。直接写通配符有风险,最好先用ls -l看清文件名,再逐个指定。有独立磁盘(比如单独挂的数据盘)的,每个都要打一遍。
耗时上,vmkfstools -K的时长和 VMDK 配置大小、后端存储的随机读性能强相关。一个几十 G 的盘跑十几分钟很常见,几百 G 的盘跑一两个小时也不稀奇。判断是不是卡死,看 ESXi 主机上的存储 IO 是否还有活动,别只盯进度条。
3.4 回收结果的三种验证口径
打完洞之后,光看一个数字不够,三个口径都要对一遍,对得上才算真的回收成功。
# 口径一:VMDK 文件实际字节数,回收后应明显小于回收前 ls -l /vmfs/volumes/datastore1/VM-APP-01/VM-APP-01.vmdk # 口径二:数据存储已用容量,回收后应下降 df -h /vmfs/volumes/datastore1 # 口径三:回到 vSphere 客户端的数据存储摘要页,看已用空间的图形变化三个口径的对应关系是:ls -l看到的是这个 VMDK 自己的占用,df -h看到的是整卷,客户端摘要页是给人看的可视化。回收前后各记一次,差值应该大致相当。如果ls -l降了但df -h没怎么动,去查这个卷上是不是还有别的虚拟机在同一时间写了大量数据,把回收出来的空间又吃掉了。
还有一种情况:三个数都降了,但降的幅度远小于预期。多半是客机里还有别的分区没清零,或者 VMDK 里存在大量非零的“垃圾块”(比如被反复覆写过的临时文件区域)。前者补一遍-z,后者只能接受——Thin 回收的是全零块,非零块没法打洞。
4. 方案二落地:Storage vMotion 厚置备往返的在线回收
不能停机的机器走这条。核心思路不是“打洞”,而是“重建”:让 vMotion 在目标存储上重新生成一份磁盘,新磁盘只为真实数据分配块,旧的连同它的历史包袱一起被丢掉。
4.1 磁盘格式三选一:为什么中转站要用厚置备延迟归零
vSphere 里给虚拟磁盘选格式时,有三个选项,含义完全不同。
Thin 是按需分配,写多少分多少,这也是产生回收问题的根源。厚置备延迟归零是按配置大小一次性把空间划出来,但块内容不预先清零,第一次写入时才做零化处理。厚置备置零同样按配置大小划空间,而且在创建时就把所有块写成零,速度最慢、占用最大,但换来了最好的首次写入性能和某些集群特性上的兼容性。
往返迁移的中转站要选厚置备延迟归零,理由是这个格式下“配置大小 = 实际占用”,vMotion 完成后,目标存储上的磁盘块分布完全按真实数据重新生成,原来那些分配出去但没用的块就自然消失了。迁回 Thin 时,新磁盘只会为“确实有内容”的块分配空间,占用自然降下来。
不用厚置备置零的原因很简单:它会全盘写零,耗时更长,而这个场景里我们并不需要零化带来的那些额外好处。也不用 Thin 做中转——从 Thin 迁到 Thin,块分配逻辑不变,回收效果约等于零。
| 磁盘格式 | 空间分配方式 | 首次写入 | 迁移耗时 | 适合做中转 |
|---|---|---|---|---|
| 精简置备(Thin) | 按需分配,只增不减 | 正常 | 短 | 不适合,等于没迁 |
| 厚置备延迟归零 | 按配置大小划出,写入时零化 | 略慢 | 中等 | 适合,最常用 |
| 厚置备置零 | 按配置大小划出并全盘零化 | 快 | 长 | 可用但没必要 |
4.2 往返迁移的完整步骤与耗时预估
界面上操作是最直观的:选中虚拟机,右键迁移,选择“仅更改存储”,目标数据存储挑一块有足够余量的,虚拟磁盘格式选“厚置备延迟归零”,下一步确认,等任务完成。第一程结束后,占用没降反而涨了是正常的——这时候它是厚置备,按配置大小占着。第二程再迁回原数据存储,格式选“精简置备”,完成之后占用才会掉下来。
批量处理的时候用 PowerCLI 更省事:
# 连接 vCenter,凭据按实际环境替换 Connect-VIServer -Server vcenter.example.internal -User administrator -Password ****** $vm = Get-VM -Name "VM-APP-02" $dsA = Get-Datastore -Name "DS-PROD-01" # 原数据存储 $dsB = Get-Datastore -Name "DS-PROD-02" # 中转数据存储,需有足够余量 # 第一程:Thin -> 厚置备延迟归零,把磁盘重建一遍 Move-VM -VM $vm -Datastore $dsB -DiskStorageFormat LazyZeroedThick -Confirm:$false # 第二程:厚置备延迟归零 -> Thin,按真实数据重新分配块 Move-VM -VM $vm -Datastore $dsA -DiskStorageFormat Thin -Confirm:$false Disconnect-VIServer -Confirm:$falseMove-VM的-Datastore指定目标存储,-DiskStorageFormat的取值对应界面上的三个选项,LazyZeroedThick是厚置备延迟归零,Thin是精简置备。两程之间不要加别的操作,尤其别在这中间创建快照,否则第二程的格式转换会被跳过或直接报错。
耗时预估按“实际占用 ÷ 迁移带宽”来算,而不是按配置大小。一条千兆链路上跑几十 G 的实际数据,一两个小时是常态;如果存储侧还有别的负载,时间会更长。批量做的时候建议排个队,一次迁两三台,别一次性把十几台全部推进去,否则存储带宽被打满,所有业务一起变慢。
4.3 迁移期间的四个风险控制点
第一个是目标存储容量。厚置备按配置大小算,不是按实际占用算。一台配置了 500G、实际只用了 80G 的虚拟机,迁过去要占 500G。做之前把目标存储的剩余容量和这批虚拟机的配置大小总和算一遍,留出两成冗余。这是这条路线最常见的翻车点,没有之一。
第二个是快照。虚拟机上有快照时,vMotion 改磁盘格式的行为会受限,轻则格式转换被跳过(白迁一趟),重则任务直接失败。迁之前先合并快照。
第三个是迁移带宽。vMotion 和业务流量共享物理网卡,迁移期间别在同一存储上再安排备份、克隆、批量部署这类重 IO 任务。有条件的话给 vMotion 单独配 vmnic,或者在网络 IO 控制里给迁移流量设个上限。
第四个是迁移完成后的确认。迁移任务显示“已完成”不等于格式换对了,回到虚拟机的磁盘设置里看一眼,确认磁盘格式确实是预期的那个,再看数据存储的已用容量。这一步花不了一分钟,能省掉后面一堆扯皮。
4.4 两种方案怎么选:一张对比表
| 对比项 | 离线清零(SDelete + vmkfstools -K) | 在线迁移(Storage vMotion 往返) |
|---|---|---|
| 是否需要停机 | 需要,打洞期间虚拟机关机 | 不需要,全程在线 |
| 额外存储空间 | 不需要 | 需要,按配置大小算 |
| 单台耗时 | 中等,取决于 VMDK 大小和存储 IO | 较长,来回两趟迁移 |
| 对客机的要求 | 能进系统执行命令,能腾出空闲空间 | 无 |
| 回收彻底程度 | 只回收全零块,非零垃圾块无法打洞 | 按真实数据重建,更彻底 |
| 适用场景 | 可约时间窗的业务机、测试环境 | 不能停机的核心业务、批量治理 |
| 主要风险 | 快照未清、多分区漏做 | 目标存储容量不足、格式选错 |
一般的选择顺序是:能停机、存储没余量的,走离线清零;不能停机、有第二块存储的,走在线迁移;两样都不满足的,先扩存储或者先清理客机内的大文件,再回来做回收。别硬凑条件,这条路没有捷径。
5. 避坑与排查:五条现场最容易翻车的经验
这五条基本覆盖了这个场景里九成以上的“做了但没效果”。
5.1 打完洞空间没降,快照还在
现象:vmkfstools -K跑完了,进度条也走到底,ls -l看 VMDK 一点没变。
原因:虚拟机上有快照,当前磁盘写入全在 delta 文件里,基础 VMDK 的内容并没有变化,打洞自然打不出东西。有些情况下命令会直接报错退出,有些情况下它会“成功”但毫无效果,后者更坑。
解决:先vim-cmd vmsvc/snapshot.get <vmid>确认快照状态,有快照就全部删除并等待合并完成,再重新跑一遍清零和打洞。合并本身也需要时间,别合并完立刻跑,等任务确实结束。
5.2 只处理了 C 盘,D 盘的空间纹丝不动
现象:回收做完了,C 盘对应的那部分空间降了,整体占用只降了一小截。
原因:sdelete64.exe -z C:只处理 C 盘的空闲块,D 盘、E 盘的空闲块没被写成零,vmkfstools -K扫到这些区域时看到的是非零内容,不会打洞。
解决:进客机用磁盘管理确认分区数量,逐个分区执行清零。注意-z后面跟的是盘符加冒号,多分区机器要跑多次。跑完再关机打洞,一次到位。
5.3 vmkfstools -K 报错“Failed to punch zero”或提示文件被占用
现象:命令一执行就报错退出,或者提示无法打开 VMDK。
原因:最常见的是虚拟机没真正关机(挂起状态不算关机),或者虚拟机上还挂着别的进程在读写磁盘。也有可能是路径写错了,或者指定的文件是-flat.vmdk而不是描述符文件。
解决:vim-cmd vmsvc/power.getstate <vmid>确认是 Powered off;cd到目录后ls -l看清文件名,参数用描述符.vmdk而不是-flat.vmdk;确认没有正在运行的克隆、备份、快照合并任务。
5.4 迁移完成后容量不降反升
现象:Storage vMotion 跑完,数据存储占用比迁移之前还高。
原因:第一程迁成了厚置备,按配置大小占空间,这是预期行为,不是故障。真正的问题在于很多人只做了第一程就以为结束了,或者第二程的格式没选回 Thin。
解决:确认两程都执行完,且第二程的-DiskStorageFormat是Thin。如果第二程也做完了还是不降,去查虚拟机是不是还有快照——有快照时格式转换会被跳过,迁移任务依然会显示成功。
5.5 在 VMFS5 上跑 unmap 命令报不支持
现象:esxcli storage vmfs unmap -l DS-PROD-01执行后提示操作不被支持。
原因:自动 UNMAP 是 VMFS6 才有的能力,VMFS5 的卷层没有这条通道。命令本身没写错,是版本不匹配。
解决:确认卷版本,VMFS5 就老老实实走vmkfstools -K。别为了用上新命令去升级卷——升级卷涉及整个数据存储的兼容性,代价远大于收益,而且升级完老的 VMFS5 卷也不会自动变快。
6. 验收与答辩:三条证据链和一套五分钟演示
回收这件事最容易吃的亏,是活干了但说不清。三块口径的数都降了,可要是拿不出前后对比,别人一句“会不会是别的机器删了东西”就能把你问住。所以验收要按证据链来做,不是看一眼数字了事。
第一份证据是数字对齐表。回收前后各记一次,三个口径写在同一张表里,差值能对上。
| 记录项 | 采集位置 | 回收前 | 回收后 | 差值 |
|---|---|---|---|---|
| 客机已用空间 | 客机df -h或卷属性 | 27.8G | 27.6G | -0.2G |
| VMDK 实际占用 | ESXi 上ls -l描述符 | 36.7G | 28.4G | -8.3G |
| 数据存储已用 | df -h /vmfs/volumes/... | 按实际填 | 按实际填 | 与上一行接近 |
| 磁盘格式 | 虚拟机磁盘设置 | Thin | Thin | 未变 |
| 快照数量 | 快照管理器 | 0 | 0 | 0 |
第二份证据是时间线。几点开始清零、几点关机、几点开始打洞、打洞跑了多久、几点完成验证,一条时间线列清楚。这份东西的价值在于出问题时能快速定位是哪一步拖了后腿,答辩时被问到“为什么用了这么久”也有据可依。
第三份证据是复现脚本。把这次用到的命令整理成一个可重复执行的清单,换一台机器改改路径就能跑。我一般会把检查项也写进去,让下一个接手的人不至于从零摸索。
#!/bin/sh # 精简磁盘回收后的快速核对脚本,在 ESXi 主机上执行 VM_DIR="/vmfs/volumes/datastore1/VM-APP-01" VMDK="VM-APP-01.vmdk" echo "== 磁盘格式与容量 ==" ls -lh "$VM_DIR/$VMDK" echo "== 数据存储剩余 ==" df -h /vmfs/volumes/datastore1 echo "== 是否存在快照 delta 文件 ==" ls -1 "$VM_DIR" | grep -i "delta\|-00000" || echo "无快照 delta"脚本里最后那行用grep找 delta 文件,是这套流程里我最常用的一个自查手段——目录里只要出现-000001.vmdk这类文件,就说明还有快照链,这时候别急着做任何回收动作。
答辩环节真正会被追问的通常只有三个问题:为什么不能自动回收(答 VMFS5 卷层没有 UNMAP 通道,只有 VMFS6 才有);为什么打洞前要先清零(答vmkfstools -K只识别全零块,客机里删掉的数据在磁盘上还是原来的内容,不打洞);两条路线怎么选(答看能不能停机、有没有第二块存储)。这三个问题答得清楚,方案就算立住了。
从那以后我每次做这类回收,都会在动手前把那张数字对齐表的表头先建好,回收前一行、回收后一行,中间的过程日志附在下面。吃过一次亏——有回只记了数据存储的剩余容量,结果那段时间同卷上另一台机器正好在灌数据,回收出来的空间被吃掉一大半,数字对不上,查了大半天才发现是隔壁机器干的。希望帮到你。
本文还有配套的精品资源,点击获取