第 20 章 GEOM: 模块化磁盘变换框架

This translation may be out of date. To help with the translations please access the FreeBSD translations instance.

20.1. 概述

本章将介绍以 FreeBSD GEOM 框架来使用磁盘。 这包括了使用这一框架来配置的主要的 RAID 控制工具。 这一章不会深入讨论 GEOM 如何处理或控制 I/O、 其下层的子系统或代码。 您可以从 geom(4) 联机手册及其众多 SEE ALSO 参考文献中得到这些信息。 这一章也不是对 RAID 配置的权威介绍, 它只介绍由 支持GEOM 的 RAID 级别。

读完这章, 您将了解:

  • 通过 GEOM 支持的 RAID 类型。

  • 如何使用基本工具来配置和管理不同的 RAID 级别。

  • 如何通过 GEOM 使用镜像、 条带、 加密和挂接在远程的磁盘设备。

  • 如何排除挂接在 GEOM 框架上的磁盘设备的问题。

阅读这章之前, 您应:

20.2. GEOM 介绍

GEOM 允许访问和控制类 (classes) - 主引导记录、 BSD 标签 (label), 等等 - 通过使用 provider, 或在 /dev 中的特殊文件。 它支持许多软件 RAID 配置, GEOM 能够向操作系统, 以及在其上运行的工具提供透明的访问方式。

20.3. RAID0 - 条带

条带是一种将多个磁盘驱动器合并为一个卷的方法。 许多情况下, 这是通过硬件控制器来完成的。 GEOM 磁盘子系统提供了 RAID0 的软件支持, 它也成为磁盘条带。

在 RAID0 系统中, 数据被分为多个块, 这些块将分别写入阵列的所有磁盘。 与先前需要等待系统将 256k 数据写到一块磁盘上不同, RAID0 系统, 能够同时分别将打碎的 64k 写到四块磁盘上, 从而提供更好的 I/O 性能。 这一性能提升还能够通过使用多个磁盘控制器来进一步改进。

在 RAID0 条带中的每一个盘的尺寸必须一样, 因为 I/O 请求是分散到多个盘上的, 以便让这些盘上的读写并行完成。

磁盘条带图

Procedure: 在未格式化的 ATA 磁盘上建立条带

  1. 加载 geom_stripe.ko 模块:

    # kldload geom_stripe
  2. 确信存在合适的挂接点 (mount point)。 如果这个卷将成为根分区, 那么暂时把它挂接到其他位置i, 如 /mnt

    # mkdir /mnt
  3. 确定将被做成条带卷的磁盘的设备名, 并创建新的条带设备。 举例而言, 要将两个未用的、 尚未分区的 ATA 磁盘 /dev/ad2/dev/ad3 做成一个条带设备:

    # gstripe label -v st0 /dev/ad2 /dev/ad3
    Metadata value stored on /dev/ad2.
    Metadata value stored on /dev/ad3.
    Done.
  4. 接着需要写标准的 label, 也就是通常所说的分区表到新卷上, 并安装标准的引导代码:

    # bsdlabel -wB /dev/stripe/st0
  5. 上述过程将在 /dev/stripe 目录中的 st0 设备基础上建立两个新设备。 这包括 st0ast0c。 这时, 就可以在 st0a 设备上用下述 newfs 命令来建立文件系统了:

    # newfs -U /dev/stripe/st0a

    在屏幕上将滚过一些数字, 整个操作应该能在数秒内完成。 现在可以挂接刚刚做好的卷了。

要挂接刚创建的条带盘:

# mount /dev/stripe/st0a /mnt

要在启动过程中自动挂接这个条带上的文件系统, 需要把关于卷的信息放到 /etc/fstab 文件中。为达到此目的, 需要创建一个叫 stripe 的永久的挂载点:

# mkdir /stripe
# echo "/dev/stripe/st0a /stripe ufs rw 2 2" \
    >> /etc/fstab

此外, geom_stripe.ko 模块也必须通过在 /boot/loader.conf 中增加下述设置, 以便在系统初始化过程中自动加载:

# echo 'geom_stripe_load="YES"' >> /boot/loader.conf

20.4. RAID1 - 镜像

镜像是许多公司和家庭用户使用的一种无须中断的备份技术。 简单地说, 镜像的概念就是 磁盘B 是同步复制 (replicate) 的 磁盘A 的副本, 或者 磁盘C+D 是 diskA+B 的同步复制副本, 等等。 无论磁盘配置如何, 这种技术的共同特点都是一块磁盘或分区的内容会同步复制到另外的地方。 这样, 除了能够很容易地恢复信息之外, 还能够在无须中断服务或访问的情况下进行备份, 甚至直接将副本送到数据保安公司异地储存。

在开始做这件事之前, 首先请准备两个容量相同的磁盘驱动器, 下面的例子假定它们都是使用直接访问方式 (Direct Access, da(4)) 的 SCSI 磁盘。

20.4.1. 对主磁盘进行镜像

假定您现有系统中的 FreeBSD 安装到了第一个, 也就是 da0 盘上, 则应告诉 gmirror(8) 将主要数据保存在这里。

在开始构建镜像卷之前, 可以启用更多的调试信息, 并应开放对设备的完全访问。 这可以通过将 sysctl(8) 变量 kern.geom.debugflags 设置为下面的值来实现:

# sysctl kern.geom.debugflags=17

接下来需要创建镜像。 这个过程的第一步是在主磁盘上保存元数据信息, 也就是用下面的命令来创建 /dev/mirror/gm 设备:

在引导用的设备基础上新建镜像时, 有可能会导致保存在磁盘上最后一个扇区的数据丢失。 在新安装 FreeBSD 之后立即创建镜像可以减低此风险。 下面的操作与默认的 FreeBSD 9.X 安装过程不兼容, 因为它采用了新的 GPT 分区格式。 GEOM 会覆盖 GPT 元数据, 这会导致数据丢失, 并有可能导致系统无法引导。

# gmirror label -vb round-robin gm0 /dev/da0

系统应给出下面的回应:

Metadata value stored on /dev/da0.
Done.

初始化 GEOM, 这步操作会加载内核模块 /boot/kernel/geom_mirror.ko

# gmirror load

当这个命令运行完之后, 系统会在 /dev/mirror 目录中创建设备节点 gm0

配置在系统初始化过程中自动加载 geom_mirror.ko

# echo 'geom_mirror_load="YES"' >> /boot/loader.conf

编辑 /etc/fstab 文件, 将其中先前的 da0 改为新的镜像设备 gm0

如果 vi(1) 是你喜欢的编辑器, 以下则是完成此项任务的一个简便方法:

# vi /etc/fstab

vi(1) 中备份现有的 fstab 内容, 具体操作是 :w /etc/fstab.bak。 接着, 把所有旧的 da0 替换成 gm0, 也就是输入命令 :%s/da/mirror\/gm/g

修改完后的 fstab 文件应该是下面的样子。 磁盘驱动器是 SCSI 或 ATA 甚至 RAID 都没有关系, 最终的结果都是 gm

# Device		Mountpoint	FStype	Options		Dump	Pass#
/dev/mirror/gm0s1b	none		swap	sw		0	0
/dev/mirror/gm0s1a	/		ufs	rw		1	1
/dev/mirror/gm0s1d	/usr		ufs	rw		0	0
/dev/mirror/gm0s1f	/home		ufs	rw		2	2
#/dev/mirror/gm0s2d	/store		ufs	rw		2	2
/dev/mirror/gm0s1e	/var		ufs	rw		2       2
/dev/acd0		/cdrom		cd9660	ro,noauto	0	0

重启系统:

# shutdown -r now

在系统初始化过程中, 新建的 gm0 会代替 da0 设备工作。 系统完成初始化之后, 可以通过检查 mount 命令的输出来查看效果:

# mount
Filesystem         1K-blocks    Used    Avail Capacity  Mounted on
/dev/mirror/gm0s1a   1012974  224604   707334    24%    /
devfs                      1       1        0   100%    /dev
/dev/mirror/gm0s1f  45970182   28596 42263972     0%    /home
/dev/mirror/gm0s1d   6090094 1348356  4254532    24%    /usr
/dev/mirror/gm0s1e   3045006 2241420   559986    80%    /var
devfs                      1       1        0   100%    /var/named/dev

这个输出是正常的。 最后, 使用下面的命令将 da1 磁盘加到镜像卷中, 以开始同步过程:

# gmirror insert gm0 /dev/da1

在构建镜像卷的过程中, 可以用下面的命令查看状态:

# gmirror status

一旦镜像卷的构建操作完成, 这个命令的输出就会变成这样:

      Name    Status  Components
mirror/gm0  COMPLETE  da0
                      da1

如果有问题或者构建仍在进行, 输出中的 COMPLETE 就会是 DEGRADED

20.4.2. 故障排除

20.4.2.1. 系统拒绝引导

如果系统引导时出现类似下面的提示:

ffs_mountroot: can't find rootvp
Root mount failed: 6
mountroot>

这种情况应使用电源或复位按钮重启机器。 在引导菜单中, 选择第六 (6) 个选项。 这将让系统进入 loader(8) 提示符。 在此处手工加载内核模块:

OK? load geom_mirror
OK? boot

如果这样做能解决问题, 则说明由于某种原因模块没有被正确加载。 检查 /boot/loader.conf 中相关条目是否正确。 如果问题仍然存在,可以在内核配置文件中加入:

options	GEOM_MIRROR

然后重新编译和安装内核来解决这个问题。

20.4.3. 从磁盘故障中恢复

磁盘镜像的一大好处是在当其中一个磁盘出现故障时, 可以很容易地将其替换掉, 并且通常不会丢失数据。

考虑前面的 RAID1 配置, 假设 da1 出现了故障并需要替换, 要替换它, 首先确定哪个磁盘出现了故障, 并关闭系统。 此时, 可以用换上新的磁盘, 并重新启动系统。 这之后可以用下面的命令来完成磁盘的替换操作:

# gmirror forget gm0
# gmirror insert gm0 /dev/da1

在重建过程中可以用 gmirror status 命令来监看进度。 就是这样简单。

20.5. RAID3 - 使用专用校验设备的字节级条带

RAID3 是一种将多个磁盘组成一个卷的技术, 在这个配置中包含一个专用于校验的盘。 在 RAID3 系统中, 数据会以字节为单位拆分并写入除校验盘之外的全部驱动器中。 这意味着从 RAID3 中读取数据时将会访问所有的驱动器。 采用多个磁盘控制器可以进一步改善性能。 RAID3 阵列最多可以容忍其中的 1 个驱动器出现故障, 它可以提供全部驱动器总容量的 1 - 1/n, 此处 n 是阵列中的磁盘数量。 这类配置比较适合保存大容量的数据, 例如多媒体文件。

在建立 RAID3 阵列时, 至少需要 3 块磁盘。 所有的盘的尺寸必须一致, 因为 I/O 请求会并发分派到不同的盘上。 另外, 由于 RAID3 本身的设计, 盘的数量必须恰好是 3, 5, 9, 17, 等等 (2^n + 1)。

20.5.1. 建立专用的 RAID3 阵列

在 FreeBSD 中, RAID3 是通过 graid3(8) GEOM class 实现的。 在 FreeBSD 中建立专用的 RAID3 阵列需要下述步骤。

虽然理论上从 RAID3 阵列启动 FreeBSD 是可行的, 但这并不常见, 也不推荐您这样做。

  1. 首先, 在引导加载器中用下面的命令加载 geom_raid3.ko 内核模块:

    # graid3 load

    此外, 也可以通过命令行手工加载 geom_raid3.ko 模块:

    # kldload geom_raid3.ko
  2. 创建用于挂载卷的挂点目录:

    # mkdir /multimedia/
  3. 确定将要加入阵列的磁盘设备名, 并创建新的 RAID3 设备。 最终, 这个设备将代表整个阵列。 下面的例子使用三个未经分区的 ATA 磁盘: ada1ada2 保存数据, 而 ada3 用于校验。

    # graid3 label -v gr0 /dev/ada1 /dev/ada2 /dev/ada3
    Metadata value stored on /dev/ada1.
    Metadata value stored on /dev/ada2.
    Metadata value stored on /dev/ada3.
    Done.
  4. 为新建的 gr0 设备分区, 并在其上创建 UFS 文件系统:

    # gpart create -s GPT /dev/raid3/gr0
    # gpart add -t freebsd-ufs /dev/raid3/gr0
    # newfs -j /dev/raid3/gr0p1

    屏幕上会滚过许多数字, 这个过程需要一段时间才能完成。 此后, 您就完成了创建卷的全部操作, 可以挂载它了。

  5. 最后一步是挂载文件系统:

    # mount /dev/raid3/gr0p1 /multimedia/

    现在可以使用 RAID3 阵列了。

为了让上述配置在系统重启后继续可用, 还需要进行一些额外的配置操作。

  1. 在挂载卷之前必须首先加载 geom_raid3.ko 模块。 将下面的配置添加到 /boot/loader.conf 文件中, 可以让系统在引导过程中自动加载这个模块:

    geom_raid3_load="YES"
  2. 您需要在 /etc/fstab 文件中加入下列配置, 以便让系统引导时自动挂载阵列上的文件系统:

    /dev/raid3/gr0p1	/multimedia	ufs	rw	2	2

20.6. GEOM Gate 网络设备

通过 gate 工具, GEOM 支持以远程方式使用设备, 例如磁盘、 CD-ROM、 文件等等。 这和 NFS 类似。

在开始工作之前, 首先要创建一个导出文件。 这个文件的作用是指定谁可以访问导出的资源, 以及提供何种级别的访问授权。 例如, 要把第一块 SCSI 盘的第四个 slice 导出, 对应的 /etc/gg.exports 会是类似下面的样子:

192.168.1.0/24 RW /dev/da0s4d

这表示允许同属私有子网的所有机器访问 da0s4d 分区上的文件系统。

要导出这个设备, 首先请确认它没有被挂接, 然后是启动 ggated(8) 服务:

# ggated

现在我们将在客户机上 mount 该设备, 使用下面的命令:

# ggatec create -o rw 192.168.1.1 /dev/da0s4d
ggate0
# mount /dev/ggate0 /mnt

到此为止, 设备应该已经可以通过挂接点 /mnt 访问了。

请注意, 如果设备已经被服务器或网络上的任何其他机器挂接, 则前述操作将会失败。

如果不再需要使用这个设备, 就可以使用 umount(8) 命令来安全地将其卸下了, 这一点和其他磁盘设备类似。

20.7. 为磁盘设备添加卷标

在系统初始化的过程中, FreeBSD 内核会为检测到的设备创建设备节点。 这种检测方式存在一些问题, 例如, 在通过 USB 添加设备时应如何处理? 很可能有闪存盘设备最初被识别为 da0 而在这之后, 则由 da0 变成了 da1。 而这则会在挂接 /etc/fstab 中的文件系统时造成问题, 这些问题, 还可能在系统引导时导致无法正常启动。

解决这个问题的一个方法是以连接拓扑方式链式地进行 SCSI 设备命名, 这样, 当在 SCSI 卡上增加新设备时, 这些设备将使用一个未用的编号。 但如果 USB 设备取代了主 SCSI 磁盘的位置呢? 由于 USB 通常会在 SCSI 卡之前检测到, 因此很可能出现这种现象。 当然, 可以通过在系统引导之后再插入这些设备来绕过这个问题。 另一种绕过这个问题的方法, 则是只使用 ATA 驱动器, 并避免在 /etc/fstab 中列出 SCSI 设备。

还有一种更好的解决方法。 通过使用 glabel 工具, 管理员或用户可以为磁盘设备打上标签, 并在 /etc/fstab 中使用这些标签。 由于 glabel 会将标签保存在对应 provider 的最后一个扇区, 在系统重启之后, 它仍会持续存在。 因此, 通过将具体的设备替换为使用标签表示, 无论设备节点变成什么, 文件系统都能够顺利地完成挂接。

这并不是说标签一定是永久性的。 glabel 工具既可以创建永久性标签, 也可以创建临时性标签。 在重启时, 只有永久性标签会保持。 请参见联机手册 glabel(8) 以了解两者之间的差异。

20.7.1. 标签类型和使用示范

有两种类型的标签, 一种是普通标签, 另一种是文件系统标签。 标签可以是永久性的或暂时性的。永久性的标签可以通过 tunefs(8)newfs(8) 命令创键。根据文件系统的类型, 它们将在 /dev 下的一个子目录中被创建。例如, UFS2 文件系统的标签会创建到 /dev/ufs 目录中。永久性的标签还可以使用 glabel label 创建。它们不再是文件系统特定的,而是会在 /dev/label 目录中被创建。

暂时性的标签在系统下次重启时会消失, 这些标签会创建到 /dev/label 目录中, 很适合测试之用。可以使用 glabel create 创建暂时性的标签。请参阅 glabel(8) 手册页以获取更多详细信息。

要为一个 UFS2 文件系统创建永久性标签, 而不破坏其上的数据,可以使用下面的命令:

# tunefs -L home /dev/da3

如果文件系统满了, 这可能会导致数据损坏; 不过, 如果文件系统快满了, 此时应首先删除一些无用的文件, 而不是增加标签。

现在, 您应可以在 /dev/ufs 目录中看到标签, 并将其加入 /etc/fstab

/dev/ufs/home		/home            ufs     rw              2      2

当运行 tunefs 时, 应首先卸下文件系统。

现在可以像平时一样挂接文件系统了:

# mount /home

现在, 只要在系统引导时通过 /boot/loader.conf 配置加载了内核模块 geom_label.ko, 或在联编内核时指定了 GEOM_LABEL 选项, 设备节点由于增删设备而顺序发生变化时, 就不会影响文件系统的挂接了。

通过使用 newfs 命令的 -L 参数, 可以在创建文件系统时为其添加默认的标签。 请参见联机手册 newfs(8) 以了解进一步的详情。

下列命令可以清除标签:

# glabel destroy home

以下的例子展示了如何为一个启动磁盘打上标签。

例 1. 为启动磁盘打上标签

为启动磁盘打上永久性标签, 系统应该能够正常启动, 即使磁盘被移动到了另外一个控制器或者转移到了一个不同的系统上。 此例中我们假设使用了一个 ATA 磁盘, 当前这个设备被系统识别为 ad0。 还假设使用了标准的 FreeBSD 分区划分方案, /, /var, /usr/tmp 文件系统, 还有一个 swap 分区。

重启系统,在 loader(8) 提示符下键入 4 启动到单用户模式。 然后输入以下的命令:

# glabel label rootfs /dev/ad0s1a
GEOM_LABEL: Label for provider /dev/ad0s1a is label/rootfs
# glabel label var /dev/ad0s1d
GEOM_LABEL: Label for provider /dev/ad0s1d is label/var
# glabel label usr /dev/ad0s1f
GEOM_LABEL: Label for provider /dev/ad0s1f is label/usr
# glabel label tmp /dev/ad0s1e
GEOM_LABEL: Label for provider /dev/ad0s1e is label/tmp
# glabel label swap /dev/ad0s1b
GEOM_LABEL: Label for provider /dev/ad0s1b is label/swap
# exit

系统加继续启动进入多用户模式。 在启动完毕后, 编辑 /etc/fstab 用各自的标签替换下常规的设备名。 最终 /etc/fstab 看起来差不多是这样的:

# Device                Mountpoint      FStype  Options         Dump    Pass#
/dev/label/swap         none            swap    sw              0       0
/dev/label/rootfs       /               ufs     rw              1       1
/dev/label/tmp          /tmp            ufs     rw              2       2
/dev/label/usr          /usr            ufs     rw              2       2
/dev/label/var          /var            ufs     rw              2       2

现在可以重启系统了。 如果一切顺利的话, 系统可以正常启动并且 mount 命令显示:

# mount
/dev/label/rootfs on / (ufs, local)
devfs on /dev (devfs, local)
/dev/label/tmp on /tmp (ufs, local, soft-updates)
/dev/label/usr on /usr (ufs, local, soft-updates)
/dev/label/var on /var (ufs, local, soft-updates)

从 FreeBSD 7.2 开始, glabel(8) class 新增了一种用于 UFS 文件系统唯一标识符, ufsid 的标签支持。 这些标签可以在 /dev/ufsid 目录中找到, 它们会在系统引导时自动创建。 在 /etc/fstab 机制中, 也可以使用 ufsid 标签。 您可以使用 glabel status 命令来获得与文件系统对应的 ufsid 标签列表:

% glabel status
                  Name  Status  Components
ufsid/486b6fc38d330916     N/A  ad4s1d
ufsid/486b6fc16926168e     N/A  ad4s1f

在上面的例子中 ad4s1d 代表了 /var 文件系统, 而 ad4s1f 则代表了 /usr 文件系统。 您可以使用这些 ufsid 值来挂载它们, 在 /etc/fstab 中配置类似这样:

/dev/ufsid/486b6fc38d330916        /var        ufs        rw        2      2
/dev/ufsid/486b6fc16926168e        /usr        ufs        rw        2      2

所有包含了 ufsid 的标签都可以用这种方式挂载, 从而消除了需要手工创建永久性标签的麻烦, 而又能够提供提供与设备名无关的挂载方式的便利。

20.8. 通过 GEOM 实现 UFS 日志

随着 FreeBSD 7.0 的发布, 提供了长期为人们所期待的日志功能的实现。 这个实现采用了 GEOM 子系统, 可以很容易地使用 gjournal(8) 工具来进行配置。

日志是什么? 日志的作用是保存文件系统事务的记录, 换言之, 完成一次完整的磁盘写入操作所需的变动, 这些记录会在元数据以及文件数据写盘之前, 写入到磁盘中。 这种事务日志可以在随后用于重放并完成文件系统事务, 以避免文件系统出现不一致的问题。

这种方法是另一种阻止文件系统丢失数据并发生不一致的方法。 与 Soft Updates 追踪并确保元数据更新顺序这种方法不同, 它会实际地将日志保存到指定为此项任务保留的磁盘空间上, 在某些情况下可全部存放到另外一块磁盘上。

与其他文件系统的日志实现不同, gjournal 采用的是基于块, 而不是作为文件系统的一部分的方式 - 它只是作为一种 GEOM 扩展实现。

如果希望启用 gjournal, FreeBSD 内核需要下列选项 - 这是 FreeBSD 7.0 以及更高版本系统上的默认配置:

options	UFS_GJOURNAL

如果使用日志的卷需要在启动的时候被挂载, 还需加载 geom_journal.ko 内核模块, 将以下这行加入 /boot/loader.conf

geom_journal_load="YES"

这个功能也可被编译进一个定制的内核, 需在内核配置文件中加入以下这行:

options	GEOM_JOURNAL

现在, 可以为空闲的文件系统创建日志了。 对于新增的 SCSI 磁盘 da4, 具体的操作步骤为:

# gjournal load
# gjournal label /dev/da4

这样, 就会出现一个与 /dev/da4 设备节点对应的 /dev/da4.journal 设备节点。 接下来, 可以在这个设备上建立文件系统:

# newfs -O 2 -J /dev/da4.journal

这个命令将建立一个包含日志设备的 UFS2 文件系统。

然后就可以用 mount 命令来挂接设备了:

# mount /dev/da4.journal /mnt

当磁盘包含多个 slice 时, 每个 slice 上都会建立日志。 例如, 如果有 ad4s1ad4s2 这两个 slice, 则 gjournal 会建立 ad4s1.journalad4s2.journal

出于性能考虑, 可能会希望在其他磁盘上保存日志。 对于这类情形, 应该在启用日志的设备后面,给出日志提供者或存储设备。 在暨存的文件系统上, 可以用 tunefs 来启用日志; 不过, 在尝试修改文件系统之前, 您应对其进行备份。 多数情况下, 如果无法创建实际的日志, gjournal 就会失败, 并且不会防止由于不当使用 tunefs 而造成的数据丢失。

对于 FreeBSD 系统的启动磁盘使用日志也是可能的。 请参阅 Implementing UFS Journaling on a Desktop PC 以获得更多详细信息。


Last modified on: March 9, 2024 by Danilo G. Baio