Linux虚拟机清理后宿主机磁盘空间没减少,核心原因是虚拟磁盘文件(qcow2、vmdk)在文件系统层删除数据时并不会自动返还底层块,你需要手动执行trim命令后用镜像工具重写文件,才能真正把空间还给宿主机。接下来这套流程我会按“为什么没释放怎么清guest内空间怎么压qcow2怎么验证常见疑问”的顺序讲完,全部是能直接照做的命令。
为什么Linux虚拟机删了文件,磁盘空间却不释放?
先理解根因,否则你会在错误的方向上反复折腾。
qcow2这类动态虚拟磁盘采用稀疏文件结构,guest里往盘上写数据时,宿主机的qcow2文件才逐渐变大;而guest里删除文件时,只是把文件系统里的索引标记清掉了,底层那些数据块依然留在qcow2文件里,也就是说,虚拟磁盘只会膨胀,不会自动收缩,你在guest里执行rm -rf,宿主机上的qcow2文件尺寸并不会因此减小。
另外一个常见误判是把ls -lh输出当成实际占用。ls -lh显示的是文件的逻辑大小(virtual size),而宿主机真正占用的磁盘空间要看du -h,例如一个虚拟磁盘配置了200G,ls -lh显示200G,但这并不代表它占用了宿主机200G,实际占用应以du和qemu-img info里的disk size为准。
已删除文件仍被进程占用,空间根本无法释放
还有一种和虚拟磁盘无关的情况:进程打开了某个文件并持续写入,而文件已被rm删除,此时df -h看到的空间占用始终不下降,因为文件句柄还活着,用lsof +L1可以查出这类被删除但未释放的文件:
lsof +L1 | grep deleted
有结果的话,重启对应服务或进程,空间就会立刻回来,这一步应该在所有清理动作之前做,因为后面讲到的trim指令对这类占用也无效。
Linux虚拟机磁盘空间不释放怎么办?三步根治法
在guest内部做三件事:发trim指令回收空闲块、清掉日志和缓存垃圾、杀掉占用已删文件的进程,这套操作不依赖宿主机工具,任何KVM、VirtualBox、VMware虚拟机都适用。
执行fstrim,让文件系统主动告知底层哪些块已空闲
ext4、xfs、btrfs都支持手动trim,命令是:
sudo fstrim -av
-a表示对所有已挂载文件系统执行,-v输出详细结果,执行后,文件系统会向块设备层发送discard指令,qcow2镜像内部未使用的块就会被标记为空洞,这为后续压缩打好了基础。
先检查自己的文件系统是否支持trim:
lsblk -D
输出中DISC-GRAN和DISC-MAX两列不是0,说明设备支持discard,如果命令报错或提示不支持,说明后端磁盘类型(比如某些旧版virtio驱动)不支持在线trim,可以尝试更新虚拟机内核和驱动后再做,swap分区不支持fstrim,这个下面单独讲。
清掉日志、包缓存和容器镜像,避免垃圾数据继续占位
trim回收的是“已经释放但未归还”的块,如果垃圾文件本身还存在,trim并不能帮你减肥,先压缩这些常见占用源:
- 系统日志:
sudo journalctl --vacuum-size=200M,把journal日志控制在200M以内,也可以用--vacuum-time=7d只保留最近7天。 - 包缓存:Debian系执行
sudo apt clean,RedHat系执行sudo yum clean all。 - 容器环境:如果你的虚拟机承担Docker职责,检查
/var/lib/docker目录,多数情况下docker system prune -af --volumes能回收相当一部分镜像层和悬空卷。 - 临时目录:
/tmp、/var/tmp下的大文件按需手动清理。
清理完成后再次执行sudo fstrim -av,这一步常在删除大量文件之后被遗漏。
处理占用已删除文件的进程
如果lsof +L1有输出,用kill重启对应进程,常见场景是日志服务持有一个被logrotate删除的文件句柄,重启rsyslog或journald后空间立即下降,不要用rm -rf硬删这种文件,正确做法是让服务自己轮转,或者直接重启服务。
qcow2镜像文件如何压缩?host端命令行实操
guest内的fstrim做完了,qcow2文件内部的空洞已经标记好,但宿主机上的qcow2文件体积仍是原样,因为trim只改元数据,不改变文件尺寸,真正的压缩要在宿主机上重写镜像。
转换前必须检查快照、挂载状态与容量
在宿主机上执行:
qemu-img snapshot -l /path/to/your.qcow2
如果列出了快照,convert过程会把快照扁平化,旧快照内的数据会合入新镜像,体积可能不减反增,有业务保留价值的快照建议先删除或单独备份,当前有虚拟机挂载该镜像时不能直接覆盖转换,最好关机后用宿主机维护窗口操作。
再确认宿主机剩余空间,转换会生成一个新文件,容量至少需要原镜像实际占用大小的1.2倍,压缩效果越好,需要空间越大,用
qemu-img measure预估:
qemu-img measure -O qcow2 /path/to/your.qcow2
输出的required大小就是新镜像最少需要的空间,预留不足时先清理宿主机上的临时文件,或者把新镜像写到另一块磁盘上。
执行qemu-img convert,把稀疏空洞真正写出来
qemu-img convert -p -O qcow2 /path/to/your.qcow2 /path/to/your-new.qcow2
-p显示进度条,-O指定输出格式,执行完毕后,用qemu-img info看新镜像:
qemu-img info /path/to/your-new.qcow2
重点看disk size这一项,它应该明显小于原镜像,确认无误后,把旧镜像改名留作备份,将新镜像放到原路径,启动虚拟机做一次完整读写测试,虚拟机的轻量备份场景里,qcow2镜像文件如何压缩是一个高频需求,上面这套流程就是标准答案。
和virt-sparsify对比,谁更适合你?
libguestfs工具包里的virt-sparsify也能做类似工作,且对raw和部分分区结构处理更细,两者的差异如下:
| 对比项 | qemu-img convert | virt-sparsify |
|---|---|---|
| 安装依赖 | 仅qemu-utils,极轻量 | 需安装libguestfs,依赖较重 |
| 支持的格式 | qcow2、raw、vmdk等主流格式 | raw、qcow2,vmdk支持有限 |
| 内部快照处理 | 扁平化合并,快照结构丢失 | 不支持带快照镜像,直接报错 |
| 零块识别 | 自动识别稀疏空洞 | 能识别更多类型的零块,压缩率更好 |
| 操作复杂度 | 简单直接 | 参数略多,但也能一条命令完成 |
我的建议是日常qcow2场景用convert足够,遇到raw镜像或者空洞识别不彻底时再换virt-sparsify,行业共识认为,越简单的工具越不容易在维护窗口出岔子。
隐藏的占用也别忘了:swap分区与btrfs快照
fstrim对swap分区无效,而swap在虚拟机里经常占据可观空间,清理方法如下:
sudo swapoff /dev/vg/swap-lv sudo dd if=/dev/zero of=/dev/vg/swap-lv bs=1M status=progress sudo mkswap /dev/vg/swap-lv sudo swapon /dev/vg/swap-lv
用dd把整个swap设备填零,qcow2的稀疏结构才能把这块“全零区域”折叠掉,填零完成后,新镜像会比原来小不少。
对于btrfs文件系统的虚拟机,快照也占用实际存储,先看快照列表:
btrfs subvolume list / btrfs subvolume delete /path/to/@snapshot_name
删除快照后同样执行fstrim -av,让释放的块回归镜像空洞。
怎么确认空间真的释放了?四个指标对照
压缩完成后别急着收工,用四个数字对照判断:
- guest内
df -h:看文件系统已用空间是否下降。 - 宿主机
ls -lh:看qcow2逻辑大小,这个值基本不变。 - 宿主机
du -h:看qcow2实际占用,压缩后这个值应明显下降。 qemu-img info:看virtual size与disk size的差值,差值越大说明稀疏化收益越明显。
这套指标组合的通吃场景很多,比如在珠海某单位的KVM机房排查空间告警时,就是靠du和qemu-img info的对比定位到大镜像文件的,少数情况下,fstrim执行成功但convert后体积无变化,多是由于虚拟机内做过大量覆盖写而非删除操作,覆盖写不会产生空洞,压缩空间自然有限,顺便提一句,市面上第三方运维对虚拟机空间瘦身的报价通常按回收容量计费,但自己掌握这套流程成本更低。
常见问题:Linux虚拟机释放磁盘空间的典型疑问
fstrim执行完毕后,qcow2文件大小为什么没变?
正常现象,因为fstrim只在guest内标记空闲块,并未修改qcow2文件本身,必须在宿主机上运行qemu-img convert或virt-sparsify重写镜像,宿主机上的文件才能真正收缩,另外注意检查fstrim运行结果中是否有报错,部分文件系统挂载参数未开启discard也不会影响手动fstrim,只有在设备根本不支持时才会失败。
qemu-img convert和virt-sparsify压缩原理一样吗?
不完全一样,convert是整体重写文件,保留稀疏空洞;virt-sparsify会额外扫描文件系统中的零块并忽略它们,压缩率通常更高,convert适合中小镜像,virt-sparsify适合大镜像或raw转qcow2场景,但virt-sparsify依赖libguestfs环境且不支持快照镜像,cine场景还是老老实实先用convert。
虚拟机运行中可以直接执行压缩吗?
fstrim可以在线执行,但qemu-img convert必须离线,在线状态下压缩qcow2需要对后端块设备做blockcommit或blockdev-mirror,操作复杂度高且容易损坏数据,我建议把压缩放进虚拟机维护窗口,如果无法停机,可以先对磁盘做在线快照,然后在另一个存储池上基于快照做convert,完成后在维护窗口切换磁盘文件。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/633074.html





