1. 一场磁盘告警引发的思考:qcow2 与 raw 到底差在哪
1.1 从一次“文件比磁盘还小”的诡异现象说起
先说个真实经历。某天凌晨,一套 KVM 环境的宿主机报警说/var/lib/libvirt/images分区使用率到了 91%,我登录上去一看,里面躺着十几个虚拟机的磁盘镜像文件。其中有一台数据库虚拟机的镜像文件大小只有 20G 出头,但虚拟机的df -h显示根分区已经用了 78G。合着这个虚拟机的数据量已经远超镜像文件本身的大小了?当时第一反应是监控脚本写错了,后来才反应过来——这台虚拟机的磁盘格式是 qcow2,属于稀疏文件,镜像文件的大小不表示虚拟机实际占用的物理空间,也不表示它给客户机暴露的逻辑磁盘大小。从那次之后,我就把 KVM 环境里两种主流磁盘格式 qcow2 和 raw 的基本特性、适用场景、性能差异彻底梳理了一遍。
很多刚接触虚拟化的朋友会把“虚拟磁盘格式”和“磁盘镜像文件大小”搞混。简单说,qcow2 是 QEMU 的写时分配格式,它只占实际写入数据需要的空间;raw 则是一个裸的块设备镜像,或者说它就是直接把一块磁盘的所有扇区按顺序复制成一个文件。raw 格式因为缺少元数据层,读写路径更短,但它的文件大小通常等于虚拟磁盘容量,除非你用特殊方式创建稀疏文件。这两种格式在 KVM 环境下是最常被拿来对比的,没有绝对的好与坏,只有适不适合你的业务场景。
1.2 两个词说清分配机制:按需取用 vs 一口吃下
要理解 qcow2 和 raw 的本质区别,先记住两个关键概念:按需分配和预分配。
qcow2 默认是“按需分配”,也就是 thin provisioning。虚拟机只写了一个 1G 的文件,qcow2 镜像文件在宿主机上就只占 1G 左右(还要算上一点元数据开销)。虚拟机不断写数据,镜像文件才不断变大。这种机制可以极大节省宿主机存储空间,尤其适合那种“创建了很多虚拟机但实际写入量都不大”的环境。它的内部结构分了三层,有 header、L1 表、L2 表,数据块是按需分配的,写数据时要先查映射表找到对应位置,如果位置不存在就分配新块再写入。
raw 格式就简单直接得多。它本身没有快照、没有元数据、没有压缩,就是一块裸盘。如果你用qemu-img create -f raw disk.raw 100G创建镜像,文件会立刻占满 100G(取决于宿主文件系统),因为 raw 格式默认是预分配。但这里有个容易忽略的细节:你完全可以用truncate -s 100G disk.raw或qemu-img create -f raw -o preallocation=off disk.raw 100G创建一个所谓的“稀疏 raw 文件”,文件看起来有 100G,实际占用只有几K,后面用到哪块才分配哪块。
所以从存储分配的角度看,qcow2 天生是稀疏的,raw 则可疏可密。但如果你把 raw 做成稀疏文件,它的“稀疏”是宿主文件系统(比如 ext4、xfs)帮你实现的,不是 raw 格式自己的特性。换句话说,qcow2 的分配管理是自带的,raw 的分配管理是借别人的。
| 对比维度 | qcow2 | raw(预分配) | raw(稀疏文件) |
|---|---|---|---|
| 创建时占用空间 | 极小(约 200K 起) | 等于虚拟磁盘大小 | 极小 |
| 写入时空间增长 | 自动按需增长 | 不增长,已全部分配 | 由文件系统按需分配 |
| 元数据开销 | 有(L1/L2 表) | 无 | 无 |
| 该格式是否自带稀疏能力 | 自带 | 不依赖文件系统 | 依赖文件系统支持 |
qcow2 的按需分配虽然省空间,但也带来一个天然问题:写入路径变长了。物理服务器上跑数据库、跑高频随机 IO 的场景,如果空间不紧张,很多人宁愿用 raw 省掉那层映射开销。而如果你的环境是开发测试、云计算平台的多租户虚拟化,qcow2 的节省空间能力就非常值钱。这就要看性能和便利性怎么权衡了。
2. 性能对比实测:同样跑业务,差距藏在元数据和快照里
2.1 顺序读写与随机 IO 的表现差异
很多人一上来就问我:“qcow2 性能是不是比 raw 差很多?”我的回答是:看场景,看负载,看后端存储。纯顺序读写的场景,比如虚拟机里做磁盘镜像拷贝、批量文件传输,raw 确实有优势,但没网上说的那么夸张。qcow2 在读路径上需要先读 L1/L2 表找到数据块位置,再读实际数据,正常情况下这张映射数据不大,可以被缓存命中,所以顺序读的损耗可能只有几个百分点。但在写路径上,qcow2 遇到新分配的块要额外执行一次元数据更新,顺序写的性能差距会明显一点。
随机 IO 的场景差距才会拉开。数据库类负载,比如 MySQL、PostgreSQL,会产生大量小文件随机读写的 IO。raw 格式下,宿主机把请求直接翻译成对镜像文件的偏移量读写,路径非常短。qcow2 格式下,每次 IO 都要经历“虚拟机发出请求 -> 找到对应的 L2 表项 -> 确认数据块位置 -> 读写实际数据”的过程。如果 L2 表不在缓存里,就会多一次甚至几次磁盘寻道,随机读写性能就会掉下来。
我用fio在同样一台宿主机上做过一轮简单测试,后端都是同一块企业级 SSD,虚拟机里都跑 CentOS,一个用 qcow2,一个用 raw。结果大致是这样的(不同硬件会有偏差,只作参考):
| 测试项 | qcow2 | raw | 差距 | 说明 |
|---|---|---|---|---|
| 顺序读(128K 队列深度 16) | 约 1200 MB/s | 约 1500 MB/s | raw 高约 25% | 元数据查询拖了后腿 |
| 顺序写(128K 队列深度 16) | 约 850 MB/s | 约 1100 MB/s | raw 高约 30% | 新块分配开销明显 |
| 随机读(4K 队列深度 32) | 约 120K IOPS | 约 140K IOPS | raw 高约 17% | 缓存命中后差距缩小 |
| 随机写(4K 队列深度 32) | 约 45K IOPS | 约 80K IOPS | raw 高将近一倍 | qcow2 写入放大严重 |
随机写差距大,是因为 qcow2 本身存在“写时分配”的额外消耗。每当写入一个数据块,如果该块之前是未分配的,先要分配一个新块,再把数据写进去,同时更新 L2 表。如果频繁覆盖已有块,还要处理块引用计数。这些元数据操作叠加在用户 IO 之上,就形成了写入放大。而 raw 格式没这些步骤,数据写到哪里就是哪里。
2.2 快照机制如何悄悄拖慢写入
qcow2 的一大卖点就是支持快照,但快照也是它性能下降的常见原因。默认情况下,你在 KVM 里用virsh snapshot-create-as给 qcow2 虚拟机拍快照,QEMU 会把新数据写入一个新的镜像分支,原来的数据块变成只读。这样设计的好处是快照点之前的镜像文件保持原样,可以随时回退。
问题在于,如果你在快照之上持续运行业务,一旦有写操作落在那些“快照前已经分配并被读入缓存”的块上,虚拟机就需要读取旧数据(copy-on-write 的 read-modify-write),再写入新数据。这一读一写,IO 延迟就上去了。我见过一个开发环境,qcow2 虚拟机挂着三个快照长期不合并,每次提交代码触发一次全量刷新,那个虚拟机的磁盘响应时间能从 5 毫秒飙到 300 多毫秒。后来我把快照合并完,性能立刻恢复了。
raw 格式本身不支持快照,但你可以借助后端方案实现类似能力。最常用的思路是底层使用 LVM 卷,给每个虚拟机分配一个 LV,然后用lvcreate -s做 LVM 快照。LVM 快照和 qcow2 快照不一样,它的 COW 机制由内核的 Device Mapper 实现,性能损耗比 qcow2 自带的快照小,而且不光是磁盘格式层面的功能,整个文件系统都能被一致地快照。缺点是要预先规划 VG 空间,快照卷也要预留容量。
如果你需要频繁快照、回退、保存某个时刻的磁盘状态,qcow2 的整合程度最高。但如果你更在意长时间稳定性能,那就要谨慎使用快照。要么不用,要么用后及时合并,别留下一堆“性能债务”。
2.3 缓存策略(cache 模式)与格式的配合
既然说到性能,就绕不开 KVM 的缓存策略。很多人把缓存策略和磁盘格式混在一起对比,其实这俩是不同层面的东西。cache=writeback、cache=none这些是 QEMU 层面控制 “宿主机页缓存如何处理磁盘 IO” 的参数,不会改变 qcow2 或 raw 的元数据结构,但会影响最终性能表现。
我的经验是:在 Linux 宿主机上,如果你用的是 raw 格式的镜像,且把它放在 SSD 上,那cache=none加上io=native是比较稳的组合。这样 QEMU 会绕过自身的页缓存,直接使用 AIO 原生接口发 IO,对数据库这类有自己缓存机制的应用最友好,避免了双缓存导致的资源浪费。qcow2 格式则稍微保守一点,建议用cache=writeback,因为 qcow2 的元数据更新比较频繁,让宿主机页缓存帮忙兜着能降低一些 IO 波动。
| 缓存模式 | raw 下表现 | qcow2 下表现 | 适用场景 |
|---|---|---|---|
| writeback | 性能好,但断电有丢数据风险 | 推荐,元数据更新更平滑 | 开发测试、可接受掉电不完全一致的场景 |
| none + native | 推荐,路径最短,性能透明 | 可用,但 qcow2 映射表 miss 时会放大延迟 | 生产数据库、高随机 IO |
| unsafe | 性能最强,数据安全最差 | 性能提升明显但不建议 | 临时跑批,可随时重置 |
我并不建议你把缓存参数照抄到一个生产环境里,因为宿主机的 RAID 卡缓存、BBU、存储网络带宽都会干涉最终效果。调优的正确姿势是:固定磁盘格式,分别测不同 cache 模式,记录 P95 延迟和 IOPS,再根据业务类型做决定。
3. 选型决策表:开发机、生产库、临时测试到底该用谁
3.1 选型维度拆解:空间、性能、运维复杂度
抛开纸面参数,选格式前先问自己三个问题:宿主机存储够不够?虚拟机 IO 压力有多大?团队对快照、备份等运维能力要求多高?
第一个是空间问题。如果你在物理机上开 20 台虚拟机,每台逻辑盘都开了 100G,但实际平均只用了 30G,那 qcow2 明显划算,总占用可能只需要 600G 左右;如果全用预分配的 raw,直接就是 2000G 没了。很多虚拟化集群愿意上 qcow2,就是因为它能显著提高存储利用率。第二个是性能问题。数据库、重负载应用,尤其是有大量随机写的小虚拟机,raw 的后端写放大更小,长期运行更稳。第三个是运维复杂度。qcow2 的快照、克隆、压缩、加密都是命令行内建能力,raw 想有类似功能得借助 LVM、zfs、btrfs 这些宿主文件系统,对团队的技术栈要求更高。
还有一点常常被忽略:虚拟磁盘格式和宿主机文件系统的耦合度。raw 格式存放在任意文件系统上,性能很大程度取决于文件系统的分配策略;qcow2 则多了一层自己的块管理。如果宿主机用 XFS、ext4,两种格式都能跑。如果底层是 ZFS,那 raw 配合 ZFS 的块引用快照反而能获得比 qcow2 更好的数据管理体验,这时不一定非要选 qcow2。
3.2 给不同场景的推荐组合
基于这些维度,我一般给不同场景开不同的“处方”。
开发测试环境:首选 qcow2。空间利用率高,快照回退方便,配合qemu-img create -b基础镜像还能实现秒级创建开发机。一个 20G 的基础镜像,后面挂 8 台开发机,每台只占几百 M 的差异数据量。测试人员随便改系统、随便折腾,出了问题恢复快照就行,成本极低。
生产数据库虚拟机:我通常推荐 raw 预分配,同时配合 LVM。数据库需要稳定的 IOPS 和低延迟,raw 格式少了一层元数据转换,更符合“稳定压倒一切”的原则。用 LVM 做底层卷管理,可以在线扩展虚拟机磁盘,还支持 LVM 快照,是生产环境里比较扎实的组合。不过 raw 注意保持一定的宿主机存储余量,因为文件创建后立即占满。
CI 机器人 / 临时测试机:如果你有一个 KVM 集群在跑 CI,比如每次代码提交都拉起几台虚拟机跑集成测试,跑完就销毁,那 qcow2 还是最优解。你可以准备一个“干净系统”的基础镜像,多个临时虚拟机用同一个 base 镜像创建,销毁时只删除差异盘,宿主机空间压力很小。如果再配一个zstd压缩的 qcow2 镜像作为镜像仓库,创建虚拟机的速度会更快。
网盘、对象存储网关这类 IO 慢但空间大的应用:这种应用一般是“大带宽顺序读写但延迟不敏感”,raw 和 qcow2 都能胜任。这里更该关注的是底层存储介质和网络,而不是格式。我觉得别为了追求性能盲目上 raw,毕竟 qcow2 的压缩特性在这种场景下能省不少空间。
下面这张表总结一下我的推荐:
| 场景 | 推荐格式 | 理由 |
|---|---|---|
| 开发测试机 | qcow2 | 省空间,快照方便,克隆快 |
| 生产数据库 | raw + LVM | 低延迟,无元数据开销,快照够用 |
| CI 临时实例 | qcow2 + backing file | 秒级创建,空间占用极低 |
| 长期归档镜像 | qcow2 压缩 | 减小镜像体积,完整保留虚拟盘状态 |
| 大容量文件服务器 | raw 或 qcow2 均可 | 瓶颈在存储介质,格式影响小 |
4. qcow2 独有的高级特性:快照、压缩、加密与后备盘
4.1 内置快照与外部快照的区别
既然聊 qcow2,就必须把它最拿得出手的几项高级能力说清楚。前面提到的快照,细分下来有两种玩法:内置快照(internal snapshot)和外部快照(external snapshot)。
内置快照是把快照状态直接保存在同一个 qcow2 文件里,用virsh snapshot-create-as --disk-only或者qemu-img snapshot -c创建。它的好处是管理简单,一个镜像文件自带多个历史状态,回退时用virsh snapshot-revert一条命令搞定。缺点是文件会变得越来越大,因为旧数据块不能被回收,快照多了之后虚拟机的写性能明显下降。
外部快照就灵活多了。创建时 QEMU 会生成一个新的 qcow2 文件作为当前活跃层,原来的 qcow2 变成只读的基础层。你可以一层一层叠,形成一个快照链。这种设计非常契合“想随时回到某个历史状态、又不想让单一文件膨胀失控”的场景。运维上要用virsh blockcommit把上面层的数据合并回底层,或者用virsh blockpull把底层合并到顶层,操作链条不复杂但概念容易绕,新手经常分不清 commit 和 pull 的方向。
内部快照链不会污染镜像文件之外的东西,但生产环境我更倾向外部快照,因为合并失败还能用底层文件单独恢复到某个时点。我这套习惯是在一次“内部快照把文件撑到 300G,qemu-img check 还报了多处不一致”的教训之后形成的。
4.2 压缩与加密对性能的实际影响
qcow2 支持-o compression=on,可以用qemu-img convert -c把现有镜像重新转换成压缩格式。压缩带来的空间收益非常可观,尤其是安装好系统、清理完缓存后的基准镜像,体积可能减少一半。代价是 CPU 需要在读写时进行压缩/解压操作,所以压缩镜像的 IO 延迟会比不压缩的略高,CPU 占用也更高。我在一台只有 4 核的宿主机上做过测试,5 台压缩 qcow2 虚拟机同时跑编译任务,整体耗时比不压缩多出约 15%-20%,但空间节省了 40% 以上。空间紧张、CPU 繁忙程度不高的环境,压缩是收益大于代价的。
加密这块,qcow2 支持 AES 加密,创建时指定-o encrypt.format=luks就能给磁盘镜像加上一层保护。它的原理是给虚拟磁盘的数据块加密,没有密钥就无法读取镜像内容。这对移动介质上的虚拟机、外包运维场景很有用。但加密的代价不只是性能,还有密钥管理的复杂度。如果你忘了密码,那整块虚拟盘的数据基本等于没了,系统层面的文件恢复工具再好都救不回来。所以我不建议在性能敏感环境里开加密,如果确实有合规需求,不如走系统层面加密,或者用宿主机文件系统的加密能力。
4.3 backing file 的继承链玩法
这是 qcow2 比 raw 灵活得多的一块。用qemu-img create -f qcow2 -b base.qcow2 -F qcow2 child.qcow2创建带后备文件(backing file)的镜像,子镜像只记录与基础镜像的差异数据。这种设计经常被用来做“黄金镜像 + 差异磁盘”的分层体系。
实际管理上,我建议遵循两个原则:第一,基础镜像必须是完全干净且相对固定的版本,不打补丁、不装多余软件。第二,子镜像生命周期结束后要及时清理,避免底层基础镜像改了,几百个子镜像的引用关系变得一团乱麻。KVM 不会强制校验子镜像和 base 镜像的匹配关系,但如果你误删了 base 文件,所有依赖它的子镜像就全废了。
在libvirt里,带 backing 的 qcow2 通常用于快速生成多台同配置虚拟机,比如考试环境里一次给 20 个学员开同样配置的 Linux 实验机。创建时只是创建一个很小的差异文件,几秒钟就能把虚拟机定义好,等学员开始操作才慢慢分配空间。这正是 qcow2 对比 raw 最大的商业模式级优势——省的不只是容量,还有创建时间。
5. 转换与迁移实操:从 qcow2 到 raw 的完整流程复盘
5.1 qemu-img convert 的核心参数与目标大小预估
没人能保证一种格式用一辈子。很多时候,开发环境里用 qcow2 写满了一堆数据,要搬到生产环境做数据库,就得把它转成 raw。这时候核心工具就是qemu-img convert。
转格式前先做两个准备:第一,虚拟机最好关机,或者至少确保文件系统是静态一致状态。我在运行中转过一次,结果客户机文件系统里出现了少量不一致,虽然fsck能修,但没必要冒这个险。第二,用qemu-img info source.qcow2看当前磁盘的虚拟大小和实际占用,这决定了目标 raw 文件要多少空间。比如虚拟磁盘是 100G,但 qcow2 实际只占了 30G,转成 raw 后,目标文件在预分配模式下就是 100G;如果宿主机空间不够 100G,你就得先用truncate建一个稀疏 raw,转换完成后看du -h确认实际占用。
标准的转换命令是这样:
# 查看源镜像信息 qemu-img info source.qcow2 # 转换为 raw,-p 显示进度,-f 指源格式,-O 指目标格式 qemu-img convert -p -f qcow2 -O raw source.qcow2 dest.raw转换过程本质上是按“虚拟磁盘布局”逐块复制数据,目标文件大小等于虚拟磁盘的容量,而不是源镜像的实际占用。所以如果你空间紧张,最好先把源镜像里的空闲空间清理掉。Linux 虚拟机可以用fstrim或virt-sparsify工具瘦身;Windows 虚拟机需要在系统里执行“优化驱动器”里的 TRIM 操作。瘦身后再转换,目标 raw 的实际占用会小很多。
另外,qemu-img convert也支持直接加压缩选项,比如:
# 把 raw 转成 qcow2 并压缩 qemu-img convert -p -f raw -O qcow2 -c source.raw dest.qcow2如果你转换的目的是为了归档备份,压缩选项就很值。如果是为了继续跑生产负载,我一般不加-c,避免无谓的 CPU 开销。
5.2 转换过程中最容易踩的三个坑
第一个坑是“目标文件被占用了却不知道”。我在一次在线迁移时,目标虚拟机还没关机,又顺手执行了向目标路径写入的 convert 命令,结果目标文件被 QEMU 进程锁定,convert 直接报错。正确流程是先virsh shutdown关闭虚拟机,确保镜像文件不再被进程占用,再开始转换。
第二个坑是“引导方式不匹配”。qcow2 和 raw 本身不涉及引导差异,但有些老系统在安装时生成的/boot/grub2/grub.cfg里,root 设备用的是设备 ID 或/dev/vda,转换完镜像挂到一个不同的 virtio 设备编号上,就可能出现 kernel panic。遇到这种情况,不要急着怀疑转换失败,先用virt-rescue或手动挂载目标镜像,检查/etc/fstab和 grub 配置,修正设备路径后再启动。现在的发行版大多用 UUID 定位根分区,这个问题已经少多了,但老镜像还是能碰上。
第三个坑是“空间判错,转换到一半磁盘写满”。比如虚拟磁盘 200G,宿主机空闲只剩 180G,你硬转 raw,跑到 90% 就报No space left on device,目标文件直接损坏。我的习惯是先qemu-img convert到一个同分区的临时路径,等完成并校验后再mv到最终位置。虽然多一次拷贝,但至少不会破坏原有镜像。同时转换前用df -h确认目标分区至少得有“虚拟磁盘容量 + 10%”的余量。
5.3 就地转换的思路:临时盘加重启方案
如果你不想把虚拟机关太久,可以采用“临时盘就地转换”的方式。大概流程是这样:在宿主机上新分配一块容量足够的存储空间,把 qcow2 转换成 raw 写到临时路径;然后直接修改虚拟机的 libvirt XML 定义,把磁盘路径指向新的 raw 文件;最后启动虚拟机验证业务。验证没问题再删除旧镜像。
这套方案的关键点是,新 raw 文件必须与原虚拟机的磁盘总线类型、设备名保持一致,否则客户机里的/dev/sda可能变成/dev/sdb。如果用的是 SATA 或 virtio-blk,通常不改也没事,但我还是建议在 XML 里保持原样,避免节外生枝。
另一个就地转换的例外技巧:如果你只是想从 qcow2 换成 raw 以获得更低延迟,但虚拟机已经承载了核心业务,不好中断,那就先做一次文件系统层面的快照(比如 LVM 快照),然后把快照转成 raw,再把业务切过去。这种方式停机时间可以控制在几分钟内,前提是网络、存储规划得当。
5.4 反向迁移:什么时候该从 raw 转回 qcow2
反向转换的需求一点都不少。我遇到最多的是这两个理由:一是想把生产环境的一台 raw 虚拟机变成开发测试机,需要精简单机并引来快照能力;二是宿主机规划存储空间后发现 raw 太占地方,全部转成 qcow2 能做到空间减半。
命令几乎没有差别,只是把源格式和目标格式反过来:
qemu-img convert -p -f raw -O qcow2 source.raw dest.qcow2从 raw 转 qcow2,最大的好处是转完之后文件体积会立刻变小,因为 qcow2 会按实际占用分配块。但有个点要注意:qcow2 默认不做空洞识别,如果你在 raw 里删了很多大文件,转换时这些空闲块也会被当成普通数据写进目标文件。先对客户机执行fstrim,再转换,效果会好很多。还有,转成 qcow2 之后,如果你计划长期依赖快照,务必定期qemu-img check检查一致性,别让元数据错误悄悄积累。
6. 长期运行后的几个隐蔽坑:碎片、TRIM 与备份方式
6.1 qcow2 碎片化问题与整理思路
很多人以为虚拟磁盘碎片只存在于客户机内部,这是误区。KVM 镜像文件在宿主机文件系统上同样会产生碎片,qcow2 尤其明显。因为它按需分配数据块,虚拟机长时间频繁写入、删除、再写入,qcow2 内部的数据块就会散落在文件各处,L2 表里记录的映射变得零碎。结果是顺序读性能明显下降,随机写延迟进一步放大。
判断 qcow2 碎片化程度,可以看qemu-img map输出的映射记录条数。如果一块 100G 的虚拟盘映射记录超过几万条,说明碎片情况已经比较严重。整理最直接的办法是离线转换:把 qcow2 转换成一个新的 qcow2,转换过程中新文件会重新整理数据块的布局。有人觉得这招太笨,但确实有效。也可以考虑把镜像迁移到支持更高效分配的文件系统上,比如 XFS,它的延迟分配策略对 qcow2 这类大文件的碎片控制比 ext4 好。
raw 格式在宿主机文件系统上同样会碎片化,但它的逻辑地址和文件偏移一般是线性对应的,碎片主要来自文件系统的磁盘块分配,没有 qcow2 那层映射表的额外放大效果。所以 raw 的性能退化通常比 qcow2 慢,但这不代表你可以完全不管。如果宿主机怀疑镜像碎片影响虚拟机 IO,可以用e4defrag或 xfs 的在线整理工具去整理镜像文件对应的大文件,不过我更建议直接冷备份恢复一次,效果来得更彻底。
6.2 TRIM/丢弃操作在两种格式下的表现
现代操作系统卸载文件时给底层设备发 TRIM 命令,告诉 SSD “这块地址的数据可以清了”。在 KVM 里,这个命令是否能传递到镜像文件,取决于虚拟机磁盘是否开启了 discard 选项。virsh edit里对应的是<driver name='qemu' type='qcow2' discard='unmap'/>。
对 qcow2 来说,开启 discard 后,虚拟机删除文件释放的空间会被宿主机感知,qemu 就会把这些块在 L2 表里标记为未分配,镜像文件体积随之缩小。这是 qcow2 长期运行后控制磁盘占用的关键机制。很多运维发现 qcow2 镜像越跑越大,删了虚拟机内部几十 G 文件体积纹丝不动,就是因为没开启 discard,或者虚拟机里没做定期fstrim。
raw 格式开不开 discard 情况不一样。如果你的 raw 文件是预分配的,TRIM 只会把块标记为“可用”,文件体积不会缩小,因为它本来就已经固定大小。如果你用的是稀疏 raw 文件,TRIM 可以让文件系统释放空洞空间,体积实际缩小。不过这里有个隐藏条件:宿主机文件系统必须真的支持 punch hole 并把空间归还给文件系统,否则 discard 只是个摆设。我的经验是,XFS 在这块支持得比 ext4 更干脆,用稀疏 raw 配合定期 discard 是可以实现的,但千万别以为 raw 预分配也能瘦身。
6.3 备份镜像时的误区:直接拷贝活体镜像有多危险
必须先说一个最常见的错误操作:虚拟机还在运行,就直接用cp把 qcow2 或 raw 文件复制走当作备份。KVM 虚拟机运行时,客户机文件系统不是静止状态,直接复制出来的镜像内部很可能存在文件系统不一致,系统日志里会有大量 block error,严重点甚至导致虚拟机无法启动。rcp、scp、rsync 都一样,不解决“一致性”这个核心问题。
安全的备份路径有三条。一是先virsh snapshot-create-as snapshot-name --disk-only,做一个外部磁盘快照,然后备份这个一致性的快照文件。二是用virsh blockbackup配合底层存储系统的快照能力做在线备份,这个方案接近零中断但配置复杂一点。三是直接把虚拟机关机,再复制镜像文件,最保守但最可靠。我个人的习惯是:核心虚拟机使用底层存储快照(LVM 快照)备份,普通虚拟机用外部 qcow2 快照备份。
还有个小细节,备份 qcow2 时如果你想归档,建议顺手跑一遍qemu-img check,如果提示有泄漏簇或非零引用,先用qemu-img check -r leaks修一遍再归档。raw 备份则建议直接按位复制,也就是用dd if=/dev/... of=... bs=4M或等价工具,尽量避免用 cp,因为 cp 对稀疏大文件的处理有时并不理想。
6.4 监控与体检:日常该盯哪些指标
镜像格式选完之后,运维不是一劳永逸的。我建议每台宿主机都建一个定时任务,定期记录每个虚拟机的镜像文件大小、实际占用(du)、IO 延迟指标,并监控宿主机分区的剩余空间。qcow2 环境里尤其要注意“文件大小剧烈增长”的告警,这往往意味着虚拟机的 fstrim 失效了,或者有大范围写入放大。
对 qcow2 做定期体检的命令是qemu-img check,它会校验镜像内部的数据块引用和 L2 表结构。发现错误先分清是leaks还是corruptions。leaks属于“有块没被引用”,可以用-r leaks修复;corruptions属于“内部结构被破坏”,建议先从备份恢复,别轻易尝试自动修复。
raw 格式的体检更多依赖文件系统工具。fsck在宿主机文件系统层面确认镜像文件所在分区没有错误,filefrag可以查看镜像文件的碎片情况。如果底层用 LVM,还要定期检查 LV 的剩余空间,因为 raw 预分配后空间是固定占用的,LV 满了并不会自动扩容。
最后再说一句
两种格式较量了好多年,至今没有绝对胜者。qcow2 用一点性能换来了空间效率和多快照能力,raw 用空间换来了简单和低延迟。我在实际运维中的默认策略是:空间紧张、测试频繁、需要弹性管理的环境,一律 qcow2,快照和克隆带来的效率提升远大于那点性能损耗;生产环境的核心数据库、高 IO 负载,则老老实实用 raw 配合 LVM,别为了省那几十 G 空间去和延迟较劲。无论选哪种,记得把 fstrim、qemu-img check、定期备份这三件事纳入日常巡检,格式本身不会让运维省心,好的习惯才会。