精简配置

简介

本文档描述了一系列设备映射器目标,它们共同实现了精简配置和快照功能。

与之前的快照实现相比,此实现的主要亮点在于它允许在同一个数据卷上存储多个虚拟设备。这简化了管理,并允许在卷之间共享数据,从而减少了磁盘使用量。

另一个重要特性是支持任意深度的递归快照(快照的快照的快照...)。之前的快照实现通过链式查找表来完成此操作,因此性能是 O(深度)。这种新实现使用单一数据结构来避免随着深度增加而出现的性能下降。然而,在某些情况下,碎片化可能仍然是一个问题。

元数据与数据存储在单独的设备上,这为管理员提供了一些自由,例如可以:

  • 通过将元数据存储在镜像卷上,而将数据存储在非镜像卷上,从而提高元数据弹性。

  • 通过将元数据存储在 SSD 上来提高性能。

状态

这些目标被认为可以安全用于生产环境。但不同的使用场景将具有不同的性能特征,例如由于数据卷的碎片化。

如果您发现此软件性能不符合预期,请将详细信息发送邮件至 dm-devel@redhat.com,我们将尽力为您改进。

用于检查和修复元数据的用户空间工具已完全开发,并可作为“thin_check”和“thin_repair”使用。提供这些实用程序的软件包名称因发行版而异(在 Red Hat 发行版上,它名为“device-mapper-persistent-data”)。

操作指南

本节描述了一些使用精简配置的快速方法。它们使用 dmsetup 程序直接控制设备映射器驱动。一旦添加支持,建议最终用户使用更高级别的卷管理器,例如 LVM2。

池设备

池设备将元数据卷和数据卷连接在一起。它将 I/O 线性映射到数据卷,并通过两种机制更新元数据:

  • 来自精简目标的函数调用

  • 来自用户空间的设备映射器“消息”,用于控制新虚拟设备的创建等。

设置一个新的池设备

设置池设备需要一个有效的元数据设备和一个数据设备。如果您没有现有的元数据设备,可以通过将前 4KB 归零来创建一个,以表示空的元数据。

dd if=/dev/zero of=$metadata_dev bs=4096 count=1

您需要的元数据量将根据精简设备之间共享的块数(即通过快照)而异。如果共享量低于平均水平,您将需要一个比平均大小更大的元数据设备。

作为指导,我们建议您将元数据设备中使用的字节数计算为 48 * $data_dev_size / $data_block_size,但如果结果较小,则向上取整到 2MB。如果您正在创建大量记录大量更改的快照,您可能会发现需要增加此值。

支持的最大大小为 16GB:如果设备更大,将发出警告,并且多余的空间将不被使用。

重新加载池表

您可以重新加载池的表,事实上,当池空间不足时,就是通过这种方式调整池大小的。(注意:虽然目前不禁止在重新加载时指定不同的元数据设备,但如果它没有将 I/O 路由到与之前完全相同的磁盘位置,则会出现问题。)

使用现有池设备

dmsetup create pool \
    --table "0 20971520 thin-pool $metadata_dev $data_dev \
             $data_block_size $low_water_mark"

$data_block_size 表示每次可分配的最小磁盘空间单位,以 512 字节扇区为单位。$data_block_size 必须介于 128 (64KB) 和 2097152 (1GB) 之间,并且是 128 (64KB) 的倍数。薄池创建后,$data_block_size 无法更改。主要对精简配置感兴趣的用户可能希望使用 1024 (512KB) 之类的值。进行大量快照的用户可能希望使用 128 (64KB) 之类的值。如果您不将新分配的数据归零,则建议使用 256000 (128MB) 左右的较大 $data_block_size。

$low_water_mark 以 $data_block_size 大小的块表示。如果数据设备上的可用空间低于此水平,将触发一个 dm 事件,用户空间守护程序应捕获该事件,从而允许它扩展池设备。只会发送一个此类事件。

如果刚恢复的设备的可用空间低于低水位标记,则不会触发特殊事件。但是,恢复设备总是会触发一个事件;用户空间守护程序在处理此事件时应验证可用空间是否超过低水位标记。

元数据设备的低水位标记在内核中维护,如果元数据设备上的可用空间低于该标记,将触发 dm 事件。

更新磁盘元数据

每次写入 FLUSH 或 FUA bio 时,都会提交磁盘上的元数据。如果没有此类请求,则每秒提交一次。这意味着精简配置目标的行为类似于具有易失性写入缓存的物理磁盘。如果断电,您可能会丢失最近的一些写入。尽管发生任何崩溃,元数据应始终保持一致。

如果数据空间耗尽,池将根据配置(参见:error_if_no_space)报错或排队 I/O。如果元数据空间耗尽或元数据操作失败:池将报错 I/O,直到池离线并执行修复,以 1) 修复任何潜在的不一致性,以及 2) 清除强制修复的标志。一旦池的元数据设备修复,就可以调整大小,这将允许池恢复正常运行。请注意,如果池被标记为需要修复,则在执行修复之前,池的数据和元数据设备无法调整大小。还应注意的是,当池的元数据空间耗尽时,当前元数据事务将被中止。鉴于池将缓存其完成可能已向上层 I/O 层(例如文件系统)确认的 I/O,因此强烈建议在需要修复池时对这些层执行一致性检查(例如 fsck)。

精简配置

  1. 创建新的精简配置卷。

要创建一个新的精简配置卷,您必须向一个活跃的池设备发送消息,例如本例中的 /dev/mapper/pool。

dmsetup message /dev/mapper/pool 0 "create_thin 0"

这里的“0”是卷的标识符,一个 24 位数字。由调用者负责分配和管理这些标识符。如果标识符已被使用,消息将因 -EEXIST 错误而失败。

  1. 使用精简配置卷。

精简配置卷使用“thin”目标激活。

dmsetup create thin --table "0 2097152 thin /dev/mapper/pool 0"

最后一个参数是 thinp 设备的标识符。

内部快照

  1. 创建内部快照。

快照通过向池发送另一条消息来创建。

注意:如果您希望创建快照的源设备处于活动状态,则必须在创建快照之前暂停它,以避免数据损坏。目前不强制执行此操作,因此请务必小心!

dmsetup suspend /dev/mapper/thin
dmsetup message /dev/mapper/pool 0 "create_snap 1 0"
dmsetup resume /dev/mapper/thin

这里的“1”是卷的标识符,一个 24 位数字。“0”是源设备的标识符。

  1. 使用内部快照。

一旦创建,用户无需担心源和快照之间的任何连接。事实上,快照与任何其他精简配置设备没有区别,并且可以通过相同的方法对其自身进行快照。仅激活其中一个完全合法,并且对同时激活或删除它们没有顺序要求。(这与传统的设备映射器快照不同。)

以与任何其他精简配置卷完全相同的方式激活它。

dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 1"

外部快照

您可以使用外部**只读**设备作为精简配置卷的源。对精简设备未分配区域的任何读取都将传递到源设备。写入像往常一样触发新块的分配。

一个用例是希望在精简配置卷上运行虚拟机的宿主机,但其基础镜像位于另一个设备上(可能在多个虚拟机之间共享)。

如果使用此技术,您绝不能写入源设备!当然,您可以写入精简设备并对精简卷进行内部快照。

  1. 创建外部设备的快照

这与创建精简设备相同。在此阶段您无需提及源设备。

dmsetup message /dev/mapper/pool 0 "create_thin 0"
  1. 使用外部设备的快照。

将一个额外参数附加到 thin 目标,指定源设备。

dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 0 /dev/image"

注意:此快照的所有子代(内部快照)都需要相同的额外源参数。

停用

所有使用池的设备必须先停用,然后池本身才能停用。

dmsetup remove thin
dmsetup remove snap
dmsetup remove pool

参考

“thin-pool”目标

  1. 构造函数

    thin-pool <metadata dev> <data dev> <data block size (sectors)> \
              <low water mark (blocks)> [<number of feature args> [<arg>]*]
    

    可选特性参数

    skip_block_zeroing

    跳过新配置块的归零操作。

    ignore_discard

    禁用丢弃支持。

    no_discard_passdown

    不将丢弃操作传递给底层数据设备,而只删除映射。

    read_only

    不允许对池元数据进行任何更改。此模式仅在薄池以完整的读/写模式创建并首次使用后可用。它不能在初始薄池创建时指定。

    error_if_no_space

    如果空间不足,则报错 I/O,而不是排队。

    数据块大小必须介于 64KB (128 扇区) 和 1GB (2097152 扇区) 之间(含)。

  2. 状态

    <transaction id> <used metadata blocks>/<total metadata blocks>
    <used data blocks>/<total data blocks> <held metadata root>
    ro|rw|out_of_data_space [no_]discard_passdown [error|queue]_if_no_space
    needs_check|- metadata_low_watermark
    
    事务 ID

    用户空间使用的 64 位数字,用于帮助与卷管理器中的元数据同步。

    已用数据块 / 总数据块

    如果可用块的数量低于池的低水位标记,将向用户空间发送一个 dm 事件。此事件是边缘触发的,并且在每次恢复后只会发生一次,因此卷管理器编写者应该注册此事件,然后检查目标的状态。

    保留的元数据根

    为用户空间读取访问而“保留”的元数据根的位置(以块为单位)。“-”表示没有保留的根。

    启用丢弃传递|禁用丢弃传递

    丢弃操作是否实际传递给底层设备。当加载表时启用此功能时,如果底层设备不支持,它可能会被禁用。

    只读|读写|数据空间不足

    如果池遇到某些类型的设备故障,它将进入只读元数据模式,在该模式下不允许对池元数据进行任何更改(例如分配新块)。

    在即使只读模式也被认为不安全的严重情况下,将不允许进一步的 I/O,并且状态将只包含字符串“Fail”。此时应使用用户空间恢复工具。

    空间不足时报错|空间不足时排队

    如果池的数据或元数据空间耗尽,池将对目标数据设备的 I/O 进行排队或报错。默认是排队 I/O,直到添加更多空间或“no_space_timeout”过期。可以使用“no_space_timeout”dm-thin-pool 模块参数来更改此超时——它默认为 60 秒,但可以使用值 0 禁用。

    需要检查

    元数据操作失败,导致元数据超级块中设置了 needs_check 标志。在薄池完全恢复运行之前,必须停用元数据设备并进行检查/修复。“-”表示未设置 needs_check。

    元数据低水位标记

    元数据低水位标记的值(以块为单位)。内核在内部设置此值,但用户空间需要知道此值以确定事件是否因超过此阈值而引起。

  3. 消息

创建精简设备 <dev id>

创建一个新的精简配置设备。<dev id> 是调用者选择的任意唯一的 24 位标识符。

创建快照 <dev id> <origin id>

创建另一个精简配置设备的新快照。<dev id> 是调用者选择的任意唯一的 24 位标识符。<origin id> 是新设备将作为其快照的精简配置设备的标识符。

删除 <dev id>

删除一个精简设备。不可逆。

设置事务 ID <current id> <new id>

LVM 等用户空间卷管理器需要一种方法来同步其外部元数据与池目标的内部元数据。“thin-pool”目标提供存储任意 64 位事务 ID 并在目标的状态行上返回。为避免竞争条件,当您使用此比较并交换消息更改事务 ID 时,必须提供您认为的当前事务 ID。

保留元数据快照

保留数据映射 B 树的副本供用户空间使用。这允许用户空间检查在执行此消息时的映射。使用池的状态命令获取与元数据快照关联的根块。

释放元数据快照

释放先前保留的数据映射 B 树副本。

“thin”目标

  1. 构造函数

    thin <pool dev> <dev id> [<external origin dev>]
    
    池设备

    薄池设备,例如 /dev/mapper/my_pool 或 253:0

    设备 ID

    要激活的设备的内部设备标识符。

    外部源设备

    池外的一个可选块设备,将其视为只读快照源:对精简目标未分配区域的读取将映射到此设备。

池不存储精简设备的任何大小信息。如果您加载的精简目标小于您之前使用的目标,那么您将无法访问超出末尾映射的块。如果您加载的目标大于之前,则会根据需要分配额外的块。

  1. 状态

    <已映射扇区数> <最高映射扇区>

    如果池遇到设备错误并失败,状态将只包含字符串“Fail”。此时应使用用户空间恢复工具。

    在 <已映射扇区数> 为 0 的情况下,没有最高映射扇区,并且 <最高映射扇区> 的值未指定。