dm-clone¶
简介¶
dm-clone 是一个设备映射器目标,它将现有的只读源设备一对一复制到可写目标设备:它呈现一个虚拟块设备,使所有数据立即显示,并相应地重定向读取和写入。
dm-clone 的主要用例是将潜在的远程、高延迟、只读、存档类型的块设备克隆到可写、快速的主类型设备中,以实现快速、低延迟的 I/O。克隆设备立即可见/可挂载,并且源设备到目标设备的复制在后台与用户 I/O 并行发生。
例如,可以将应用程序备份从只读副本(可通过网络存储协议(NBD、光纤通道、iSCSI、AoE 等)访问)恢复到本地 SSD 或 NVMe 设备,并立即开始使用该设备,而无需等待恢复完成。
当克隆完成时,可以完全删除 dm-clone 表,并替换为例如直接映射到目标设备的线性表。
dm-clone 目标重用了精简配置目标使用的元数据库。
词汇表¶
- 数据填充
使用来自源设备相同区域的数据填充目标设备区域的过程,即将区域从源设备复制到目标设备。
一旦某个区域被填充,我们将关于它的所有 I/O 重定向到目标设备。
设计¶
子设备¶
该目标通过将三个设备传递给它来构建(以及稍后详细介绍的其他参数)
源设备 - 被克隆的只读设备,也是数据填充的来源。
目标设备 - 数据填充的目标,它将成为源设备的克隆。
小型元数据设备 - 它记录目标设备中哪些区域已经有效,即哪些区域已经被填充,或者已通过用户 I/O 直接写入。
目标设备的大小必须至少等于源设备的大小。
区域¶
dm-clone 将源设备和目标设备划分为固定大小的区域。区域是数据填充的单元,即从源设备复制到目标设备的最小数据量。
首次创建 dm-clone 设备时,区域大小是可配置的。建议的区域大小与文件系统块大小相同,通常为 4KB。区域大小必须在 8 个扇区 (4KB) 和 2097152 个扇区 (1GB) 之间,并且是 2 的幂。
从/到已填充区域的读取和写入由目标设备提供服务。
对尚未填充区域的读取直接由源设备提供服务。
对尚未填充区域的写入将延迟到相应的区域被填充为止,并且立即开始填充该区域。
请注意,大小等于区域大小的写入请求将跳过从源设备复制相应区域的操作,并直接覆盖目标设备的区域。
丢弃¶
dm-clone 将对尚未填充的范围的丢弃请求解释为跳过请求覆盖的区域填充的提示,即它跳过将区域的数据从源设备复制到目标设备,仅更新其元数据。
如果目标设备支持丢弃,则默认情况下 dm-clone 会将丢弃请求传递给它。
后台数据填充¶
dm-clone 会持续从源设备复制到目标设备,直到复制完整个设备。
从源设备到目标设备复制数据会占用带宽。用户可以设置一个限制来防止在任何一个时间发生超过某个量的复制。此外,dm-clone 会考虑流向设备的用户 I/O 流量,并在有正在进行的 I/O 时暂停后台数据填充。
消息 hydration_threshold <#regions> 可用于设置正在复制的最大区域数,默认值为 1 个区域。
dm-clone 使用 dm-kcopyd 将源设备的各个部分复制到目标设备。默认情况下,我们发出大小等于区域大小的复制请求。消息 hydration_batch_size <#regions> 可用于调整这些复制请求的大小。增加数据填充批次大小会导致 dm-clone 尝试将连续的区域批量处理在一起,因此我们以该多个区域的批次复制数据。
当目标设备的数据填充完成时,将向用户空间发送一个 dm 事件。
更新磁盘上的元数据¶
每次写入 FLUSH 或 FUA bio 时都会提交磁盘上的元数据。如果没有发出此类请求,则每秒都会进行提交。这意味着 dm-clone 设备的行为类似于具有易失性写入缓存的物理磁盘。如果断电,可能会丢失一些最近的写入。元数据应始终保持一致,无论发生任何崩溃。
目标接口¶
构造函数¶
clone <metadata dev> <destination dev> <source dev> <region size> [<#feature args> [<feature arg>]* [<#core args> [<core arg>]*]]
元数据设备
保存持久元数据的快速设备
目标设备
源将被克隆到的目标设备
源设备
包含要克隆的数据的只读设备
区域大小
以扇区为单位的区域大小
#功能参数
传递的功能参数数量
功能参数
no_hydration 或 no_discard_passdown
#核心参数
传递给 dm-clone 的对应键/值对的偶数个参数
核心参数
传递给 dm-clone 的键/值对,例如 hydration_threshold 256
可选的功能参数为
no_hydration
创建禁用后台数据填充的 dm-clone 实例
no_discard_passdown
禁用将丢弃传递到目标设备
可选的核心参数为
hydration_threshold <#regions>
在后台数据填充期间,一次从源设备复制到目标设备的最大区域数。
hydration_batch_size <#regions>
在后台数据填充期间,尝试将连续的区域批量处理在一起,因此我们以该多个区域的批次从源设备复制数据到目标设备。
状态¶
<metadata block size> <#used metadata blocks>/<#total metadata blocks> <region size> <#hydrated regions>/<#total regions> <#hydrating regions> <#feature args> <feature args>* <#core args> <core args>* <clone metadata mode>
元数据块大小
每个元数据块的固定大小,以扇区为单位
#已使用的元数据块
已使用的元数据块数量
#总元数据块
元数据块总数
区域大小
设备的可配置区域大小,以扇区为单位
#已填充的区域
已完成填充的区域数量
#总区域
要填充的区域总数
#正在填充的区域
当前正在填充的区域数量
#功能参数
后续的功能参数数量
功能参数
功能参数,例如 no_hydration
#核心参数
后续的核心参数数量(必须为偶数)
核心参数
用于调整核心的键/值对,例如 hydration_threshold 256
克隆元数据模式
ro 表示只读,rw 表示读写
在即使只读模式也被认为不安全的情况下,将不允许进一步的 I/O 操作,并且状态将仅包含字符串 ‘Fail’。如果元数据模式发生更改,则会向用户空间发送一个 dm 事件。
消息¶
- disable_hydration
禁用目标设备的后台填充。
- enable_hydration
启用目标设备的后台填充。
- hydration_threshold <#regions>
设置后台填充阈值。
- hydration_batch_size <#regions>
设置后台填充批处理大小。
示例¶
克隆包含文件系统的设备¶
创建 dm-clone 设备。
dmsetup create clone --table "0 1048576000 clone $metadata_dev $dest_dev \ $source_dev 8 1 no_hydration"
挂载设备并整理文件系统。dm-clone 会解析文件系统发送的丢弃指令,并且不会填充未使用的空间。
mount /dev/mapper/clone /mnt/cloned-fs fstrim /mnt/cloned-fs
启用目标设备的后台填充。
dmsetup message clone 0 enable_hydration
当填充完成后,我们可以使用线性表替换 dm-clone 表。
dmsetup suspend clone dmsetup load clone --table "0 1048576000 linear $dest_dev 0" dmsetup resume clone
不再需要元数据设备,可以安全地丢弃或将其重复用于其他目的。
已知问题¶
我们将对尚未填充的区域的读取重定向到源设备。如果读取源设备的延迟很高,并且用户反复从同一区域读取,则此行为可能会降低性能。我们应该将这些读取用作提示,以便尽快填充相关区域。目前,我们依靠页面缓存来缓存这些区域,因此希望不会最终多次从源设备读取它们。
在填充完成后,释放内核资源,即跟踪哪些区域已填充的位图。
在后台填充期间,如果我们无法读取源设备或写入目标设备,我们会打印一条错误消息,但填充过程会无限期地继续,直到成功为止。我们应该在多次失败后停止后台填充,并向用户空间发出 dm 事件以引起注意。
为什么不...?¶
在实现 dm-clone 之前,我们探索了以下替代方案
使用 dm-cache,缓存大小等于源设备,并实现新的克隆策略
生成的缓存设备不是源设备的逐一镜像,因此我们无法在克隆完成后删除缓存设备。
dm-cache 会写入源设备,这违反了我们必须将源设备视为只读的要求。
缓存在语义上与克隆不同。
使用 dm-snapshot,COW 设备等于源设备
dm-snapshot 将其元数据存储在 COW 设备中,因此生成的设备不是源设备的逐一镜像。
没有后台复制机制。
dm-snapshot 需要在完成挂起的异常时提交其元数据,以确保快照一致性。在克隆的情况下,我们不需要如此严格,并且可以依赖于在每次写入 FLUSH 或 FUA bio 时,或者像 dm-thin 和 dm-cache 一样定期提交元数据。这显著提高了性能。
使用 dm-mirror:镜像目标具有后台复制/镜像机制,但它会写入所有镜像,从而违反了我们必须将源设备视为只读的要求。
使用 dm-thin 的外部快照功能。这种方法是所有替代方案中最有希望的,因为精简配置的卷是源设备的逐一镜像,并且以与 dm-clone 相同的方式处理对未配置/尚未克隆的区域的读取和写入。
仍然
没有后台复制机制,尽管可以实现一个。
最重要的是,我们希望支持任意块设备作为克隆过程的目标,而不是将自己限制在精简配置的卷上。精简配置具有固有的元数据开销,用于维护精简卷映射,这会显著降低性能。
此外,克隆设备不应强制使用精简配置。另一方面,如果我们希望使用精简配置,我们可以只使用一个精简 LV 作为 dm-clone 的目标设备。