使用 Btrfs 作为虚拟磁盘文件的底层存储

(2022-05-12 更新)

将虚拟磁盘文件存储在 Btrfs 文件系统中具有几个优点:

  • Btrfs 的数据透明压缩功能可以节省硬盘空间;
  • Btrfs 的 reflink(共享数据块) 和 subvolume snapshot(子卷快照)可用于给虚拟磁盘创建快照;
  • Btrfs 的 Copy-on-Write (CoW) transaction 机制和可保证虚拟磁盘文件的一致性,在大量随机写入时可以免除频繁的硬盘 flush 操作。

Btrfs 透明压缩 compress

挂载 Btrfs 文件系统时使用 compress=zstdcompress-force=zstd(使用 zstd 压缩算法)。

compress: 自动判断是否压缩数据(写入数据时,尝试压缩最开头的数据,如果不能压缩,放弃压缩后续数据)

compress-force: 始终压缩数据(尝试压缩每个数据块)

Btrfs 目前支持三种压缩算法:lzo, zlib, zstd

  • 压缩速度:lzo > zstd >> zlib
  • 解压速度:lzo > zstd >>> zlib
  • 压缩率:lzo << zstd < zlib

这里使用 zstd 压缩算法,它具有与 lzo 相似的速度但压缩率更高。

创建虚拟磁盘文件

以使用 libvirt/QEMU 为例,可通过下列方法之一创建虚拟磁盘文件:

  • 使用 virt-manager 图形界面,在对应的 storage pool 中创建格式为 raw 的虚拟磁盘文件,注意不要勾选 "Allocate entire volume now"(立刻分配所有空间)。
  • 或者使用命令行 truncate -s 20G disk.img 直接创建指定大小的稀疏文件。

使用稀疏的 raw 文件,将 "thin provisioning"、透明压缩以及一致性保持的工作交给 Btrfs 处理。

QEMU 磁盘缓冲策略 cache=unsafe

添加虚拟机的磁盘设备后,可以将虚拟磁盘设备的 cache 属性设置为 unsafe 以获得最佳的写入性能。

注意:需要为 btrfs 添加 flushoncommit 挂载选项(见 man btrfs)。

在 unsafe 设置下,QEMU 将会忽略来自 guest OS 的所有 flush 请求,所有数据将在 Btrfs 文件系统进行提交/检查点(commit/checkpoint)操作时保存,若 guest OS 产生大量随机写入操作,由于 Btrfs 的 CoW 机制,可以被转化为几次连续写入,以此提高写入性能。

但要注意的是,虽然 Btrfs 保证整个文件系统的一致性,但在使用 cache=unsafe 时,如果 guest OS 是某个跨机器系统的一部分,那么这个系统一致性是不受保证的。比如下列场景:

  1. guest OS 接收了来自远端的信息
  2. guest OS 将信息 M 保存到硬盘(保存过程中通过几次 flush 安全地完成数据库事务)
  3. 保存完成之后,guest OS 通知远端,使远端将信息副本删除

这段时间中,任何时刻的断电都不会导致 guest OS 中文件系统或数据库的损坏(假设它们都正确地实现了事务),机器重启后数据库的状态为这两者之一:不存在信息 M 或 存在信息 M。但是整个系统可能会存在这个状态:guest OS 不存在那条消息,同时远端的信息已删除。

最终效果

在该虚拟磁盘上安装了 Windows Server 2019 (with desktop experience) 并添加了几个服务(甚至通过嵌套虚拟化技术,在虚拟机中启用了 Hyper-V,又安装了一个 Windows 7 在内)。

可以通过 compsize 工具查看文件/目录的压缩情况:

$ sudo compsize win2k19.img
Processed 1 file, 182915 regular extents (192542 refs), 0 inline.
Type       Perc     Disk Usage   Uncompressed Referenced  
TOTAL       51%       11G          21G          20G       
none       100%      4.4G         4.4G         4.0G       
zstd        38%      6.7G          17G          16G   

如上,该虚拟磁盘文件原始数据大小为 20G,其中 16G 被 zstd 算法压缩到 6.7G,最终文件占用的物理大小为 11G,整体压缩率为 51%,即节省了一半的空间。