简介:面向VMware运维人员的一份精简磁盘空间回收文档,聚焦VMFS5环境下Thin磁盘不会自动释放空间这一常见困扰,适合需要处理存储容量虚高、VMDK占用偏大问题的管理员与虚拟化学习者参考。压缩包内仅1个docx文件,大小约408KB,内容以图文记录方式组织,便于按场景查阅。文档围绕两种回收路径展开:一是借助SDelete清零Windows虚拟机空闲空间,再停机通过ESXi命令行执行vmkfstools回收;二是利用Storage VMotion迁移并转换磁盘格式,在不影响业务运行的前提下完成空间释放。文中还结合Server2012R2实例,对磁盘实际使用27.8G、VMDK却占用36.7G的现象做了对照说明,并提示两种方法的停机要求、目标存储容量与格式选择等注意事项,可帮助读者判断适用条件、理解回收原理并形成排错思路。目前已有2021人学习。
1. 别等存储告警才想起它:Thin 盘"删了不释放"卡在哪
在 vSphere 里用 Thin 格式给 VMware 虚拟机分配磁盘,本意是把存储超分出去、让容量跟着真实用量走。可很多人第一次遇到这个场景都会愣一下:客户机里明明删掉了十几个 G 的文件,磁盘属性里可用空间也回来了,回头一看数据存储,那个-flat.vmdk一点没瘦,甚至客户机里只用了 27.8G,VMDK 却实打实占了 36.7G。删掉的空间像被吞进了黑匣子,既不在客户机里,也没还给存储。
根子在 VMFS5:它不会自动回收精简卷的空闲块,删除动作只发生在客户机文件系统这一层,底层块是否为空,VMFS 并不知道。要让它知道,只有两条路——在客户机里把空闲空间写成零,再让主机把这些零块打洞释放;或者借一次存储迁移,让新落盘的磁盘按真实用量重新分配。前一条要停机,后一条可以在线做但吃目标存储容量。这份文档把两条路都写成了可照抄的步骤,适合正在管 ESXi 集群、被 Thin 盘膨胀拖着的运维,也适合在自己实验环境里踩过这个坑的人。
2. 先把账算清:Thin、VMFS5 与三个对不上的数字
动手之前先别急着敲命令。同一块磁盘上会同时出现三个数字:客户机操作系统报告的已用空间、数据存储里 VMDK 文件的实际占用、以及虚拟磁盘的配置大小。这三个数字对不上是常态,搞清楚它们各自代表什么,后面才知道回收有没有真的生效,也才不会在迁移时把目标存储算爆。
2.1 配置大小、实际占用与客户机用量
Thin 格式的虚拟磁盘在创建时会得到一个"配置大小",比如 200GB,这是客户机看到的最大容量;但落在数据存储上的只有一个描述符文件和一个-flat.vmdk数据文件,后者初始很小,随客户机写入逐步增长。增长的单位是 VMFS 的块,写多少分配多少,所以早期它看起来非常省。
问题出在"只增不减"。客户机删除文件时,改的是自己文件系统里的元数据位图,那些数据块在客户机看来空闲了,但 VMFS 层面这些块仍被标记为已分配,-flat.vmdk自然不缩小。于是出现正文里那个典型差值:客户机实际使用 27.8G,VMDK 占了 36.7G,中间差出来的部分就是历史上写过、后来删掉、但从未被回收的块。
判断一块盘值不值得回收,我一般看两个指标:客户机内已用空间与 VMDK 实际占用之差,以及这个差值占配置大小的比例。差值只有几个 G,回收收益有限,不如等下一次维护窗口;差值到几十 G 甚至上百 G,那就值得排一次停机或迁移。
2.2 VMFS5 为什么不会自己把空间还回来
自动回收依赖一套叫 UNMAP 的机制:客户机文件系统在删除文件时向下发一条"这些块不要了"的通知,虚拟化层再转给数据存储,数据存储把对应块标记为空闲。这条链路要客户机、虚拟磁盘、文件系统、后端存储四层都支持才走得通。
VMFS5 这一层不支持自动 UNMAP,通知到它这里就断了。所以哪怕客户机是支持 UNMAP 的系统,删除动作也不会传导到数据存储,-flat.vmdk只涨不跌。这也解释了为什么很多环境里 Thin 盘用了一两年之后,膨胀率接近厚置备——省下来的空间又被"删了不释放"吃回去了。
提示:判断当前数据存储是不是 VMFS5,在 ESXi 上执行
vmfsFstypelist或直接在 vSphere 客户端看数据存储的 VMFS 版本,不要凭印象。VMFS6 的行为不一样,套用 VMFS5 的结论容易翻车。
2.3 停机清零与在线迁移:怎么选
两条路的本质区别在于"谁来发现空闲块"。清零法是让客户机自己把空闲块写成零,主机再用vmkfstools -K扫描出全零块并打洞释放,全程需要虚拟机关机;迁移法是让 Storage vMotion 重新写一遍磁盘,新写出来的目标磁盘只分配真实数据,源端旧磁盘随之删除,虚拟机可以不停机。
| 对比项 | sdelete + vmkfstools -K | Storage vMotion 借道厚置备 |
|---|---|---|
| 是否停机 | 需要关机,回收命令必须在关机状态执行 | 可在线,业务不中断 |
| 目标存储容量 | 不需要额外容量 | 需要能放下整块磁盘的配置大小 |
| 耗时构成 | 客户机清零 IO + 主机扫描打洞 | 两次全量迁移(去程厚置备、回程 Thin) |
| 对源盘的要求 | 必须是 Thin,且无快照 | 无特殊要求,但两次迁移都要排队 |
| 适用场景 | 有维护窗口、目标存储紧张 | 不能停机、有富余存储或临时挂载点 |
选型上我一般这样定:能停机、且存储本来就紧张,走清零法,一次搞定不占额外空间;不能停机、或者手头有临时存储可以借,走迁移法,代价是迁移期间源和目标各占一份,带宽也会被吃掉一大截。两种方法可以组合使用,比如先在线迁到厚置备把空间吐出来,等窗口期再迁回 Thin。
3. 方法一:sdelete 清零加 vmkfstools -K 的完整操作链
这条链路分成两段,前半段在客户机里做,目的是把"空闲"变成"全零";后半段在 ESXi 主机上做,目的是把这些全零块从 VMDK 里摘出去。两段之间必须关机,中间不能让客户机再跑起来写数据,否则前面清零的区域会被重新弄脏,白干一场。
3.1 sdelete -z 到底在客户机里做了什么
sdelete 是 Sysinternals 套件里的命令行工具,常被用来安全擦除文件,但它有个-z参数是专门为零化空闲空间设计的:遍历目标卷上所有未被占用的簇,逐块写入零,写完不留下任何文件。对文件系统来说,卷的已用空间没有变化;对下层存储来说,原先那些"内容不明的空闲块"现在内容确定了,全是零。
这一步的意义在于给主机一个可判定的信号。VMFS 无法理解 NTFS 的位图,但它能识别"这块数据全是零",vmkfstools -K就是靠扫描全零块来决定哪些块可以打洞释放。所以清零做得越彻底,后面能回收的空间越接近客户机里的空闲量。
代价是 IO。零化会向卷写入等于空闲空间大小的数据量,一块剩 40G 空闲的盘就要写 40G,机械盘阵列上跑几个小时不稀奇。我一般放在业务低峰做,并且提前确认客户机所在存储的写带宽够用,别把同一数据存储上的其他虚拟机一起拖慢。
3.2 客户机侧的命令与参数
把sdelete64.exe拷到客户机 C 盘根目录,用管理员权限打开命令提示符。第一次运行会弹出许可协议确认,交互式确认在无人值守场景里容易卡住,所以直接带-accepteula参数跳过。
:: 进入工具所在目录 cd /d C:\ :: -accepteula 免交互接受许可;-z 零化空闲空间;C: 为目标卷 sdelete64.exe -accepteula -z C:执行后会看到逐块推进的百分比。参数上没有太多可调的:-z就是零化模式,-p可以指定覆盖次数,默认一次就够,多覆几次只会成倍拉长耗时,对回收结果没有额外帮助;-q会关掉进度输出,适合写进批处理脚本但不利于观察。
关于卷的选择要特别小心。正文里的环境只有一个分区,所以一条命令就够了。多分区的情况必须逐个卷执行,-z D:、-z E:各来一遍,漏掉的卷上那部分空闲空间就回收不了。系统保留分区和恢复分区没有盘符,不用管,量也很小。
跑完之后不要重启客户机,也不要在上面继续装软件、拷文件。零化结果保存在磁盘块里,客户机继续运行会正常写入新数据,把刚清零的区域重新弄脏。正确做法是确认进度到 100%,立刻在 vSphere 里执行关机。
注意:Linux 客户机没有 sdelete,常见做法是用
dd if=/dev/zero of=/zerofill bs=1M把剩余空间写满再删掉文件,或者用zerofree处理只读挂载的 ext 文件系统。思路一致,都是把空闲块写成零。
3.3 关机之后:SSH 上主机执行 vmkfstools -K
虚拟机关机后,回收动作要在 ESXi 主机上做。先确认主机开启了 SSH 服务,然后用 root 登录。虚拟机的文件都在/vmfs/volumes/<数据存储>/<虚拟机目录>/下面,进目录之前先看清楚磁盘文件名,带-flat的那个是数据文件,vmkfstools要作用在描述符文件上,也就是不带-flat后缀的那个.vmdk。
# 登录 ESXi 主机(把地址换成你自己的主机) ssh root@192.0.2.10 # 进入虚拟机所在目录,目录名含空格时要加引号 cd "/vmfs/volumes/DS01/WinVM01" # 确认磁盘文件名和当前占用 ls -lh *.vmdk # 对目标磁盘执行打洞回收 vmkfstools -K "WinVM01.vmdk"-K的作用是扫描 VMDK 中的全零块,把它们标记为空闲并归还给数据存储,执行时会打印进度和最终释放的块数量。这一步只对 Thin 磁盘有意义,厚置备磁盘本身已经占满,打洞不会改变占用。执行前必须确保虚拟机关机,因为运行中的虚拟机持有磁盘锁,命令会直接报错退出。
执行时间跟磁盘容量和存储性能相关,几十 G 的盘通常在几分钟到十几分钟,上百 G 的可能要等更久。中途不要 Ctrl+C,中断不会损坏磁盘,但已经处理的部分要重新扫。如果目录里还有快照产生的 delta 文件,先别急着跑-K,快照链的存在会让回收作用在错误的层级上,具体处理方式在避坑那章展开。
3.4 回收效果怎么验证
命令跑完后不要只看它打印的成功提示,回到目录里看文件实际占用。
# 查看配置大小(第一列是逻辑大小) ls -lh "WinVM01.vmdk" "WinVM01-flat.vmdk" # 查看实际占用块数换算后的空间 du -h "WinVM01-flat.vmdk"ls -lh显示的是文件逻辑大小,对 Thin 盘来说这个值基本等于配置大小,不会因为回收而改变;du -h统计的是实际占用的块,回收生效后这个数字应该明显下降,理想情况下接近客户机内的真实用量。两个数字的差值,就是还挂在 VMDK 上没被释放的块。
验证完再开机,进客户机确认数据完整、卷可用。有些环境回收后第一次开机会触发文件系统检查,属于正常现象,等它跑完即可。如果du显示几乎没有变化,先别怀疑命令,回去查三件事:回收前客户机有没有真正跑完零化、虚拟机是不是彻底关机、磁盘上有没有快照。
4. 方法二:Storage vMotion 借道厚置备的在线回收
不能停机的时候,这条路是唯一选择。它的巧妙之处在于不直接跟 VMFS5 的回收缺陷较劲,而是绕过它:既然旧磁盘上的块标记删不掉,那就把数据搬到一块新磁盘上,让新磁盘在写入过程中只分配真实数据,搬完把旧磁盘整个删掉,空间自然就回来了。
4.1 迁移为什么能让空间掉下来
Storage vMotion 的本质是"读源盘、写目标盘、切换、删源盘"。写入目标盘时,虚拟化层只知道客户机文件系统里哪些块有数据,无数据或全零的区域不会被实际分配,所以目标盘落下来就是按真实用量分配的。源盘被删除后,它占用的那些块一次性归还给数据存储,膨胀了几年的大文件就此消失。
这里有个前提容易被忽略:迁移的目标格式选什么,决定了目标盘是"按需分配"还是"一次占满"。如果目标格式仍然选 Thin,那目标盘确实只占真实用量,但源端空间要到迁移完成、源盘删除那一刻才释放;如果选厚置备,目标盘会按配置大小一次性占满,源端同样在完成后释放。两种都能达到释放源端空间的目的,区别在于目标端占多少。
正文给的方案是去程选"厚置备延迟归零",回程再选 Thin。为什么要绕这一圈?因为在 VMFS5 上把一块已经膨胀的 Thin 盘直接迁成 Thin,目标端会按真实用量分配,源端释放,理论上一步到位。但在部分旧版本环境里,直接 Thin 到 Thin 的迁移对已膨胀磁盘的处理并不彻底,落下来仍可能带着历史占用,先落成厚置备再迁回 Thin,等于强制重写了两遍,结果最干净。这条经验在老环境里流传很广,属于稳妥优先的做法。
4.2 第一次迁移:改成厚置备延迟归零
操作入口在 vSphere 客户端:选中虚拟机,右键选择迁移,迁移类型选"仅更改存储",然后挑目标数据存储,在磁盘格式那一栏选"厚置备延迟归零"。
虚拟机右键 → 迁移 → 仅更改存储 → 选择目标数据存储(容量必须 ≥ 虚拟磁盘配置大小) → 虚拟磁盘格式:厚置备延迟归零(Lazy Zeroed Thick) → 确认并开始迁移"厚置备延迟归零"的含义是:创建时把全部空间分配出来,但块内容在首次写入前不预先清零。相比"厚置备置零",它省掉了迁移过程中写零的开销,迁移速度快很多;相比 Thin,它保证目标端有完整的物理空间,不会因为超分导致迁移中途失败。
这一趟迁完,源数据存储上的旧磁盘被删除,空间立即释放。此时虚拟机的磁盘格式已经变成厚置备延迟归零,占用的空间等于配置大小——如果只是想把空间要回来、不在乎格式,到这里就可以收工。
4.3 第二次迁移:迁回 Thin
还想继续享受 Thin 的容量优势,就再迁一次。同样是"仅更改存储",目标选回原来的数据存储,磁盘格式这次选"Thin"。
虚拟机右键 → 迁移 → 仅更改存储 → 选择原数据存储(此时它已有足够空闲) → 虚拟磁盘格式:Thin → 确认并开始迁移这一趟写入的目标盘是 Thin,只按真实数据分配,落下来就是我们要的结果。原来的厚置备磁盘在迁移完成后删除,它占的那份配置大小的空间一并归还。两次迁移之后,虚拟机的实际占用应该接近客户机内的真实用量。
顺序不要弄反。如果先去程选 Thin、回程选厚置备,最后留下的是一块占满配置大小的厚盘,空间没省下来。判断标准很简单:最后一次迁移选的格式,就是最终留在数据存储上的格式。
4.4 容量与时间估算
迁移法最容易翻车的地方是容量算错。去程目标端要放下整块磁盘的配置大小,不是客户机的已用空间。一块配置 500G、实际只用 80G 的 Thin 盘,去程目标存储必须有 500G 以上可用,否则迁移直接失败,而且失败前已经把一部分数据写过去,需要手工清理残留。
| 阶段 | 目标端需要容量 | 迁移后源端 | 迁移后目标端占用 |
|---|---|---|---|
| 去程(厚置备延迟归零) | ≥ 磁盘配置大小 | 释放 | = 配置大小 |
| 回程(Thin) | ≥ 客户机真实用量 | 释放 | ≈ 真实用量 |
时间上,迁移速度取决于存储带宽和磁盘数量。普通千兆环境下一块几百 G 的盘跑几个小时是常事,万兆或 FC 环境会快很多。迁移期间虚拟机的 IO 会走镜像模式,源和目标同时承担写入,如果目标存储本身压力大,业务侧能感受到抖动。
建议把迁移排在业务低峰,并且一次只迁一台,别批量排队。批量迁移会把存储带宽吃满,单台的完成时间成倍拉长,中途出问题时也更难定位是哪一台。迁完之后同样用du -h在主机上核对实际占用,别只信客户端显示的磁盘容量。
5. 避坑与排查:五条现场血泪记录
这两套流程单独看都不复杂,真正让人加班的是细节。下面五条都是我在实际环境里遇到或帮人排查过的情况,按现象、原因、解决三段写,遇到对得上的直接照方抓药。
5.1 清零阶段的三条记录
第一条:跑完vmkfstools -K,占用几乎没变。
现象是命令正常结束,打印了完成信息,但du -h看到的数字跟执行前差不多。原因通常是客户机侧零化没做彻底,或者做完零化之后客户机又运行了一段时间,把刚清零的区域重新写脏了,主机扫描时找不到足够的全零块。解决办法是重新开机跑一遍sdelete64 -accepteula -z C:,跑完立刻关机,中间不重启、不做任何写操作,然后重新执行-K。如果客户机有多个分区,确认每个卷都做过零化。
第二条:命令直接报错,提示磁盘被占用或无法锁定。
现象是vmkfstools -K一执行就退出,报文件被锁定。原因基本是虚拟机没真正关机——挂起不算关机,虚拟机处于休眠或挂起状态时磁盘仍然被持有。另一种可能是这台虚拟机在别的 ESXi 主机上注册着,或者有备份任务正在跑,磁盘上有快照挂载。解决办法是先确认虚拟机状态是"已关闭电源",检查有没有残留的快照,确认备份窗口已过;仍不行就在主机上执行vmfslunlock类的排查思路,先定位是谁持有锁,再决定是否重启管理代理。
第三条:磁盘上有快照,回收做完没效果。
现象是磁盘明明膨胀得厉害,回收后空间纹丝不动,或者只掉了一点点。原因是快照存在时,客户机的写入落在 delta 文件上,基础盘变成了只读层,-K作用在基础盘上能回收的只有快照创建前就已存在的零块,快照之后删掉的数据全在 delta 里。解决办法是先在快照管理器里删除全部快照并等待合并完成,合并过程本身会重写磁盘、可能顺带释放一部分空间,合并完再按流程走一遍清零和打洞。有快照的盘不要硬做回收,这是最容易白干的一步。
5.2 迁移阶段的两条记录
第四条:去程迁移到一半失败,目标存储被写满。
现象是迁移进度卡在某个百分比后报错,目标数据存储告警容量不足。原因是容量按客户机已用空间估算,忽略了厚置备延迟归零会按配置大小一次性分配。解决办法是迁移前用配置大小加 10% 余量核算目标容量,不够就先扩数据存储或者换一个更大的目标。失败后要去目标存储里删掉残留的虚拟机目录和注册信息,否则那部分空间会一直挂着不释放,下次迁移还会失败。
第五条:迁回 Thin 之后,占用还是很大。
现象是两趟迁移都成功了,格式也确认是 Thin,但du -h看到的占用远高于客户机里的真实用量。原因有两个方向:一是回程迁移时客户机的空闲块里还留着历史数据,虚拟化层无法判定这些块可丢弃,照原样写了过去;二是去程之前没做任何零化处理,脏块一路被搬来搬去。解决办法是在回程迁移前,先在客户机里跑一次零化(能停机就用 sdelete,不能停机就用系统自带的优化工具),再迁回 Thin,落下来的占用会明显更接近真实用量。这条的教训是:迁移只保证"不分配空块",不保证"识别脏块",两者不是一回事。
6. 进阶:把一次性回收做成能重复执行的例行操作
单次回收解决的是存量问题,真正省心的是让它别再堆积。我现在管理的数据存储里,Thin 盘的膨胀巡检是固定动作,每季度跑一次,差值超过阈值就排进维护窗口。下面两段是我常用的做法,拿去改成自己环境的版本即可。
6.1 用主机侧 UNMAP 做批量回收
如果环境里已经有不支持自动回收之外的其他版本能力,主机侧提供了一条手动触发 UNMAP 的命令,可以一次性对整个数据存储做块回收,不用逐台虚拟机上去敲vmkfstools。执行前先确认当前版本支持这条命令,在测试数据存储上验证过再用。
# 先看命令支持的参数 esxcli storage vmfs unmap --help # 对指定数据存储触发回收,reclaim-unit 控制每次提交的块数量 esxcli storage vmfs unmap -l DS01 -n 200-l指定数据存储标签,也可以用-u传 UUID;-n是每次提交的回收单元,数值越大单次处理越快,但对存储的瞬时压力也越大,业务时段建议调小或干脆避开。这条命令不会替代客户机侧的零化,它回收的是 VMFS 层面已知的空闲块,客户机里那些"删除但未通知"的块它照样看不见。所以正确顺序仍然是先零化、再 UNMAP,两步都做才有效果。
注意:UNMAP 会产生持续的存储 IO,在共享阵列上跑之前跟存储侧确认一下队列深度和限额,别把整片存储拖慢。
6.2 回收前的检查清单与巡检脚本
每次动手前我都会过一遍这几项:虚拟机是否已关机或已确认可在线迁移、有没有未合并的快照、目标存储容量是否够配置大小、备份任务是否在跑、客户机零化是否完成。这五项里任何一项没确认,我都会先停下来,因为返工的成本远高于多花十分钟检查。
巡检可以用一段短脚本在主机上跑,把配置大小和实际占用差值超过阈值的盘列出来,作为下一轮回收的输入。
#!/bin/sh # 扫描所有数据存储下的 thin 盘,差值超过 5GB 的输出 THRESHOLD=$((5 * 1024 * 1024 * 1024)) find /vmfs/volumes -name "*-flat.vmdk" -type f 2>/dev/null | while read -r f; do cfg=$(ls -ln "$f" | awk '{print $5}') # 逻辑大小约等于配置大小 real=$(du -k "$f" | awk '{print $1 * 1024}') # 实际占用 diff=$((cfg - real)) [ "$diff" -gt "$THRESHOLD" ] && echo "$f 配置=$cfg 实际=$real 差值=$diff" done脚本逻辑很直白:遍历每个数据存储下的-flat.vmdk,拿逻辑大小减实际占用,超过阈值就打印。阈值按自己的存储规模调,小环境设 5GB 合适,大盘多的环境可以提到 20GB 以上,避免输出刷屏。这段脚本只读不写,跑在业务时段也没有风险,缺点是遍历全量数据存储比较慢,量大时建议分数据存储分批跑。
早些年我在一个测试集群上直接对着一块有快照的盘敲了-K,命令跑完什么也没释放,白白占了一个停机窗口,从那以后每次回收前我都强制走一遍检查清单,快照、关机状态、容量余量三项确认完才动手。这套流程和命令我整理在那份文档里了,按自己的环境替换路径和盘符就能用。希望帮到你。
本文还有配套的精品资源,点击获取