第 21 章 GEOM :模块化磁盘转换框架

如果发现翻译错误,请直接 发起PR修改

21.1. 简介

在 FreeBSD 中, GEOM 框架通过使用提供者(或者说是位于 /dev 目录下的磁盘设备)允许访问和控制类别,例如主引导记录和 BSD 标签。通过支持各种软件 RAID 配置, GEOM 可以透明地提供对操作系统和操作系统工具的访问。

本章介绍了在 FreeBSD 中使用 GEOM 框架下的磁盘。这包括使用该框架进行配置的主要 RAID 控制工具。本章不是关于 RAID 配置的权威指南,只讨论了 GEOM 支持的 RAID 分类。

阅读完本章后,您将会了解:

  • GEOM 提供哪种类型的 RAID 支持?

  • 如何使用基本工具来配置、维护和操作各种 RAID 级别。

  • 如何通过 GEOM 镜像、条带化、加密和远程连接磁盘设备。

  • 如何排除与 GEOM 框架连接的磁盘问题。

在阅读本章之前,您应该:

  • 了解 FreeBSD 如何处理磁盘设备(参考: disks[disks ,存储] )。

  • 了解如何配置和安装新的内核(参考: kernelconfig[kernelconfig ,配置 FreeBSD 内核] )。

21.2. RAID0 - 条带化

条带化将多个磁盘驱动器合并为一个卷。可以通过使用硬件 RAID 控制器来执行条带化。 GEOM 磁盘子系统提供了对磁盘条带化的软件支持,也称为 RAID0 ,无需 RAID 磁盘控制器。

在 RAID0 中,数据被分割成块,并写入阵列中的所有驱动器。如下图所示, RAID0 可以同时将 64k 写入阵列中的四个驱动器,而不是等待系统将 256k 写入一个磁盘,从而提供更优异的 I/O 性能。通过使用多个磁盘控制器,可以进一步提升性能。

磁盘条带化示意图

RAID0 条带中的每个磁盘必须具有相同的大小,因为 I/O 请求会交错地并行读取或写入多个磁盘。

RAID0 不提供任何冗余功能。这意味着如果阵列中的一个磁盘故障,所有磁盘上的数据都会丢失。如果数据很重要,请实施定期将备份保存到远程系统或设备的备份策略。

使用普通磁盘在 FreeBSD 系统上创建基于 GEOM 的 RAID0 软件的过程如下。创建条带后,可以参考 gstripe(8) 了解如何控制现有的条带的更多信息。

  • 过程:创建一个未格式化的 ATA 磁盘条带 *

    1. 加载 geom_stripe.ko 模块:

      # kldload geom_stripe
    2. 确保存在一个合适的挂载点。如果该卷将成为根分区,则暂时使用另一个挂载点,例如 /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. 在新卷上编写一个标准标签,也称为分区表,并安装默认的引导代码:

      # bsdlabel -wB /dev/stripe/st0
    5. /dev/stripe 中,除了 st0 之外,此过程应该创建另外两个设备。它们分别是 st0ast0c 。此时,可以使用 newfsst0a 上创建一个 UFS 文件系统:

      # newfs -U /dev/stripe/st0a

      许多数字将在屏幕上滑动,几秒钟后,过程将完成。卷已创建并准备好挂载。

    6. 手动挂载创建的磁盘条带:

      # mount /dev/stripe/st0a /mnt
    7. 要在引导过程中自动挂载此条带文件系统,请将卷信息放置在 [/etc/fstab] 中。在此示例中,创建了一个永久挂载点,命名为 [stripe] 。

      # mkdir /stripe
      # echo "/dev/stripe/st0a /stripe ufs rw 2 2" \
      >> /etc/fstab
    8. 在系统初始化期间,还必须自动加载 geom_stripe.ko 模块,方法是在 /boot/loader.conf 文件中添加一行。

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

21.3. RAID1 - 镜像

RAID1 ,或者称为镜像,是一种将相同数据写入多个磁盘驱动器的技术。镜像通常用于防止由于驱动器故障而导致的数据丢失。镜像中的每个驱动器都包含数据的完全相同副本。当一个驱动器故障时,镜像仍然可以工作,从仍在运行的驱动器提供数据。计算机继续运行,管理员有时间替换故障的驱动器而不会中断用户。

这些示例中展示了两种常见情况。第一种情况是使用两个新的驱动器创建一个镜像,并将其作为现有单个驱动器的替代品。第二个示例是在一个新的驱动器上创建一个镜像,将旧驱动器的数据复制到其中,然后将旧驱动器插入到镜像中。虽然这个过程稍微复杂一些,但只需要一个新的驱动器。

传统上,镜像中的两个驱动器在型号和容量上是相同的,但是 gmirror(8) 并不要求如此。使用不同驱动器创建的镜像的容量将等于镜像中最小驱动器的容量。较大驱动器上的额外空间将不会被使用。后来插入镜像的驱动器必须至少具有与镜像中已有的最小驱动器相同的容量。

这里展示的镜像过程是非破坏性的,但是像任何重要的磁盘操作一样,请先进行完整备份。

在这些过程中,使用 dump(8) 来复制文件系统,但它无法在使用软更新日志的文件系统上工作。请参阅 tunefs(8) 以了解有关检测和禁用软更新日志的信息。

21.3.1. 元数据问题

许多磁盘系统将元数据存储在每个磁盘的末尾。在将磁盘重新用于镜像之前,应该擦除旧的元数据。大多数问题是由两种特定类型的残留元数据引起的: GPT 分区表和来自先前镜像的旧元数据。

可以使用 gpart(8) 命令来擦除 GPT 元数据。这个例子会从磁盘 ada8 上擦除主 GPT 分区表和备份 GPT 分区表。

# gpart destroy -F ada8

可以使用 gmirror(8) 命令一步完成从活动镜像中移除磁盘并擦除元数据。在这个例子中,磁盘 ada8 从活动镜像 gm4 中被移除。

# gmirror remove gm4 ada8

如果镜像没有运行,但旧的镜像元数据仍然存在于磁盘上,请使用 gmirror clear 命令将其删除:

# gmirror clear ada8

gmirror(8) 在磁盘的末尾存储一个块的元数据。由于 GPT 分区方案也在磁盘末尾存储元数据,因此不建议使用 gmirror(8) 来镜像整个 GPT 磁盘。这里使用 MBR 分区,因为它只在磁盘的开头存储分区表,不会与镜像的元数据冲突。

21.3.2. 使用两个新硬盘创建镜像

在这个例子中, FreeBSD 已经安装在一个单独的磁盘上, ada0 。系统已经连接了两个新的磁盘, ada1ada2 。将在这两个磁盘上创建一个新的镜像,并用它来替换旧的单个磁盘。

内核模块 geom_mirror.ko 必须在内核中构建或在启动或运行时加载。现在手动加载内核模块:

# gmirror load

使用两个新的硬盘创建镜像:

# gmirror label -v gm0 /dev/ada1 /dev/ada2

gm0 是用户选择的设备名称,用于指定新镜像。在镜像启动后,该设备名称将出现在 /dev/mirror/ 中。

现在可以使用 gpart(8) 在镜像上创建 MBR 和 bsdlabel 分区表。这个例子使用传统的文件系统布局,包括用于 [/] 、交换空间、 [/var] 、 [/tmp] 和 [/usr] 的分区。也可以只使用一个 [/] 和一个交换空间分区。

镜像上的分区不必与现有磁盘上的分区大小相同,但必须足够大以容纳已经存在于 ada0 上的所有数据。

# gpart create -s MBR mirror/gm0
# gpart add -t freebsd -a 4k mirror/gm0
# gpart show mirror/gm0
=>       63  156301423  mirror/gm0  MBR  (74G)
         63         63                    - free -  (31k)
        126  156301299                 1  freebsd  (74G)
  156301425         61                    - free -  (30k)
# gpart create -s BSD mirror/gm0s1
# gpart add -t freebsd-ufs  -a 4k -s 2g mirror/gm0s1
# gpart add -t freebsd-swap -a 4k -s 4g mirror/gm0s1
# gpart add -t freebsd-ufs  -a 4k -s 2g mirror/gm0s1
# gpart add -t freebsd-ufs  -a 4k -s 1g mirror/gm0s1
# gpart add -t freebsd-ufs  -a 4k mirror/gm0s1
# gpart show mirror/gm0s1
=>        0  156301299  mirror/gm0s1  BSD  (74G)
          0          2                      - free -  (1.0k)
          2    4194304                   1  freebsd-ufs  (2.0G)
    4194306    8388608                   2  freebsd-swap (4.0G)
   12582914    4194304                   4  freebsd-ufs  (2.0G)
   16777218    2097152                   5  freebsd-ufs  (1.0G)
   18874370  137426928                   6  freebsd-ufs  (65G)
  156301298          1                      - free -  (512B)

通过在 MBR 中安装引导代码和 bsdlabel ,并设置活动分区,使镜像可引导:

# gpart bootcode -b /boot/mbr mirror/gm0
# gpart set -a active -i 1 mirror/gm0
# gpart bootcode -b /boot/boot mirror/gm0s1

在新的镜像上格式化文件系统,并启用软更新。

# newfs -U /dev/mirror/gm0s1a
# newfs -U /dev/mirror/gm0s1d
# newfs -U /dev/mirror/gm0s1e
# newfs -U /dev/mirror/gm0s1f

现在可以使用 dump(8)restore(8) 将原始的 ada0 磁盘上的文件系统复制到镜像中。

# mount /dev/mirror/gm0s1a /mnt
# dump -C16 -b64 -0aL -f - / | (cd /mnt && restore -rf -)
# mount /dev/mirror/gm0s1d /mnt/var
# mount /dev/mirror/gm0s1e /mnt/tmp
# mount /dev/mirror/gm0s1f /mnt/usr
# dump -C16 -b64 -0aL -f - /var | (cd /mnt/var && restore -rf -)
# dump -C16 -b64 -0aL -f - /tmp | (cd /mnt/tmp && restore -rf -)
# dump -C16 -b64 -0aL -f - /usr | (cd /mnt/usr && restore -rf -)

编辑 /mnt/etc/fstab ,将其指向新的镜像文件系统:

# Device		Mountpoint	FStype	Options	Dump	Pass#
/dev/mirror/gm0s1a	/		ufs	rw	1	1
/dev/mirror/gm0s1b	none		swap	sw	0	0
/dev/mirror/gm0s1d	/var		ufs	rw	2	2
/dev/mirror/gm0s1e	/tmp		ufs	rw	2	2
/dev/mirror/gm0s1f	/usr		ufs	rw	2	2

如果内核模块 geom_mirror.ko 没有被编译到内核中,需要编辑 /mnt/boot/loader.conf 文件,在启动时加载该模块:

geom_mirror_load="YES"

重新启动系统以测试新的镜像,并验证所有数据是否已复制。 BIOS 将把镜像视为两个独立的驱动器,而不是一个镜像。由于驱动器是相同的,选择哪个来启动并不重要。

如果启动时出现问题,请参阅 故障排除 。关闭电源并断开原始的 ada0 磁盘,可以将其保留为离线备份。

在使用中,镜像将表现得就像原始的单个驱动器一样。

21.3.3. 使用现有驱动器创建镜像

在这个例子中, FreeBSD 已经安装在一个单独的磁盘上, ada0 。一个新的磁盘, ada1 ,已经连接到系统上。将在新磁盘上创建一个单磁盘镜像,将现有系统复制到其中,然后将旧磁盘插入到镜像中。这个稍微复杂的过程是必需的,因为 gmirror 需要在每个磁盘的末尾放置一个 512 字节的元数据块,而现有的 ada0 通常已经分配了所有的空间。

加载 geom_mirror.ko 内核模块:

# gmirror load

使用 diskinfo 命令检查原始磁盘的媒体大小:

# diskinfo -v ada0 | head -n3
/dev/ada0
        512             # sectorsize
        1000204821504   # mediasize in bytes (931G)

在新的磁盘上创建一个镜像。为了确保镜像容量不大于原始的 ada0 驱动器,使用 gnop(8) 创建一个与原始驱动器大小完全相同的虚拟驱动器。这个驱动器不存储任何数据,只用于限制镜像的大小。当 gmirror(8) 创建镜像时,它将限制容量为 gzero.nop 的大小,即使新的 ada1 驱动器有更多的空间。请注意,第二行中的 1000204821504 等于 ada0 的媒体大小,如上面的 diskinfo 所示。

# geom zero load
# gnop create -s 1000204821504 gzero
# gmirror label -v gm0 gzero.nop ada1
# gmirror forget gm0

由于 gzero.nop 不存储任何数据,因此镜像设备不会将其视为已连接。镜像设备被告知“忘记”未连接的组件,从而删除对 gzero.nop 的引用。结果是一个只包含一个磁盘 ada1 的镜像设备。

创建 gm0 后,查看 ada0 上的分区表。这个输出来自一个 1TB 的硬盘。如果驱动器末尾有一些未分配的空间,可以直接将内容从 ada0 复制到新的镜像中。

然而,如果输出显示磁盘上的所有空间都已分配,就像下面的列表中所示,那么在磁盘末尾没有可用于 512 字节镜像元数据的空间。

# gpart show ada0
=>        63  1953525105        ada0  MBR  (931G)
          63  1953525105           1  freebsd  [active]  (931G)

在这种情况下,必须编辑分区表,将 mirror/gm0 的容量减少一个扇区。具体操作将在后面解释。

无论哪种情况,都应首先使用 gpart backupgpart restore 命令来复制主硬盘上的分区表。

# gpart backup ada0 > table.ada0
# gpart backup ada0s1 > table.ada0s1

这些命令创建了两个文件, table.ada0table.ada0s1 。这个例子是来自一个 1 TB 的硬盘。

# cat table.ada0
MBR 4
1 freebsd         63 1953525105   [active]
# cat table.ada0s1
BSD 8
1  freebsd-ufs          0    4194304
2 freebsd-swap    4194304   33554432
4  freebsd-ufs   37748736   50331648
5  freebsd-ufs   88080384   41943040
6  freebsd-ufs  130023424  838860800
7  freebsd-ufs  968884224  984640881

如果磁盘末尾没有显示可用空间,则必须将分区和最后一个分区的大小都减小一个扇区。编辑这两个文件,将分区和最后一个分区的大小都减小一个。这些是每个列表中的最后一个数字。

# cat table.ada0
MBR 4
1 freebsd         63 1953525104   [active]
# cat table.ada0s1
BSD 8
1  freebsd-ufs          0    4194304
2 freebsd-swap    4194304   33554432
4  freebsd-ufs   37748736   50331648
5  freebsd-ufs   88080384   41943040
6  freebsd-ufs  130023424  838860800
7  freebsd-ufs  968884224  984640880

如果磁盘末尾至少有一个扇区未分配,这两个文件可以直接使用,无需修改。

现在将分区表恢复到 [mirror/gm0] 中:

# gpart restore mirror/gm0 < table.ada0
# gpart restore mirror/gm0s1 < table.ada0s1

使用 gpart show 命令检查分区表。此示例中,分区表如下: gm0s1a 用于 /gm0s1d 用于 /vargm0s1e 用于 /usrgm0s1f 用于 /data1 ,以及 gm0s1g 用于 /data2

# gpart show mirror/gm0
=>        63  1953525104  mirror/gm0  MBR  (931G)
          63  1953525042           1  freebsd  [active]  (931G)
  1953525105          62              - free -  (31k)

# gpart show mirror/gm0s1
=>         0  1953525042  mirror/gm0s1  BSD  (931G)
           0     2097152             1  freebsd-ufs  (1.0G)
     2097152    16777216             2  freebsd-swap  (8.0G)
    18874368    41943040             4  freebsd-ufs  (20G)
    60817408    20971520             5  freebsd-ufs  (10G)
    81788928   629145600             6  freebsd-ufs  (300G)
   710934528  1242590514             7  freebsd-ufs  (592G)
  1953525042          63                - free -  (31k)

切片和最后一个分区都必须在磁盘末尾至少有一个空闲块。

在这些新分区上创建文件系统。分区的数量将根据原始磁盘 ada0 而变化。

# newfs -U /dev/mirror/gm0s1a
# newfs -U /dev/mirror/gm0s1d
# newfs -U /dev/mirror/gm0s1e
# newfs -U /dev/mirror/gm0s1f
# newfs -U /dev/mirror/gm0s1g

通过在 MBR 中安装引导代码和 bsdlabel ,并设置活动分区,使镜像可引导:

# gpart bootcode -b /boot/mbr mirror/gm0
# gpart set -a active -i 1 mirror/gm0
# gpart bootcode -b /boot/boot mirror/gm0s1

/etc/fstab 调整为使用镜像上的新分区。首先通过将其复制到 /etc/fstab.orig 来备份此文件。

# cp /etc/fstab /etc/fstab.orig

编辑 [/etc/fstab] 文件,将 [/dev/ada0] 替换为 [mirror/gm0] 。

# Device		Mountpoint	FStype	Options	Dump	Pass#
/dev/mirror/gm0s1a	/		ufs	rw	1	1
/dev/mirror/gm0s1b	none		swap	sw	0	0
/dev/mirror/gm0s1d	/var		ufs	rw	2	2
/dev/mirror/gm0s1e	/usr		ufs	rw	2	2
/dev/mirror/gm0s1f	/data1		ufs	rw	2	2
/dev/mirror/gm0s1g	/data2		ufs	rw	2	2

如果内核中没有构建 geom_mirror.ko 内核模块,请编辑 /boot/loader.conf 文件,在启动时加载它:

geom_mirror_load="YES"

现在可以使用 dump(8)restore(8) 将原始磁盘上的文件系统复制到镜像上。使用 dump -L 转储的每个文件系统都会首先创建一个快照,这可能需要一些时间。

# mount /dev/mirror/gm0s1a /mnt
# dump -C16 -b64 -0aL -f - /    | (cd /mnt && restore -rf -)
# mount /dev/mirror/gm0s1d /mnt/var
# mount /dev/mirror/gm0s1e /mnt/usr
# mount /dev/mirror/gm0s1f /mnt/data1
# mount /dev/mirror/gm0s1g /mnt/data2
# dump -C16 -b64 -0aL -f - /usr | (cd /mnt/usr && restore -rf -)
# dump -C16 -b64 -0aL -f - /var | (cd /mnt/var && restore -rf -)
# dump -C16 -b64 -0aL -f - /data1 | (cd /mnt/data1 && restore -rf -)
# dump -C16 -b64 -0aL -f - /data2 | (cd /mnt/data2 && restore -rf -)

重新启动系统,从 ada1 引导。如果一切正常,系统将从 mirror/gm0 引导,该镜像现在包含与 ada0 之前相同的数据。如果启动时出现问题,请参考 故障排除

此时,镜像仍然只由单个 ada1 磁盘组成。

成功从 mirror/gm0 启动后,最后一步是将 ada0 插入到镜像中。

ada0 被插入到镜像中时,它的原始内容将被来自镜像的数据覆盖。在将 ada0 添加到镜像之前,请确保 mirror/gm0 的内容与 ada0 相同。如果之前使用 dump(8)restore(8) 复制的内容与 ada0 上的内容不一致,请将 /etc/fstab 恢复为挂载 ada0 上的文件系统,重新启动,并重新开始整个过程。

# gmirror insert gm0 ada0
GEOM_MIRROR: Device gm0: rebuilding provider ada0

两个磁盘之间的同步将立即开始。使用 gmirror status 命令查看进度。

# gmirror status
      Name    Status  Components
mirror/gm0  DEGRADED  ada1 (ACTIVE)
                      ada0 (SYNCHRONIZING, 64%)

一段时间后,同步将完成。

GEOM_MIRROR: Device gm0: rebuilding provider ada0 finished.
# gmirror status
      Name    Status  Components
mirror/gm0  COMPLETE  ada1 (ACTIVE)
                      ada0 (ACTIVE)

mirror/gm0 现在由两个磁盘 ada0ada1 组成,并且它们的内容会自动同步。在使用中, mirror/gm0 的行为与原始的单个驱动器相同。

21.3.4. 故障排除

如果系统无法启动,可能需要更改 BIOS 设置以从新镜像驱动器中启动。可以使用任何一个镜像驱动器进行引导,因为它们包含相同的数据。

如果启动停在这个消息上,说明镜像设备出现了问题:

Mounting from ufs:/dev/mirror/gm0s1a failed with error 19.

Loader variables:
  vfs.root.mountfrom=ufs:/dev/mirror/gm0s1a
  vfs.root.mountfrom.options=rw

Manual root filesystem specification:
  <fstype>:<device> [options]
      Mount <device> using filesystem <fstype>
      and with the specified (optional) option list.

    e.g. ufs:/dev/da0s1a
        zfs:tank
        cd9660:/dev/acd0 ro
          (which is equivalent to: mount -t cd9660 -o ro /dev/acd0 /)

  ?               List valid disk boot devices
  .               Yield 1 second (for background tasks)
  <empty line>    Abort manual input

mountroot>

忘记在 /boot/loader.conf 中加载 geom_mirror.ko 模块可能会导致这个问题。要修复它,从 FreeBSD 安装介质启动,并在第一个提示处选择 Shell 。然后加载镜像模块并挂载镜像设备:

# gmirror load
# mount /dev/mirror/gm0s1a /mnt

编辑 [/mnt/boot/loader.conf] 文件,在其中添加一行代码以加载镜像模块:

geom_mirror_load="YES"

保存文件并重新启动。

导致“错误 19 ”的其他问题需要更多的努力来修复。虽然系统应该从 ada0 启动,但如果 /etc/fstab 不正确,将会出现选择 shell 的另一个提示。在引导加载程序提示符处输入 ufs:/dev/ada0s1a ,然后按下 Enter 。撤消 /etc/fstab 中的编辑,然后将文件系统从原始磁盘 (ada0) 挂载,而不是从镜像挂载。重新启动系统,然后再次尝试该过程。

Enter full pathname of shell or RETURN for /bin/sh:
# cp /etc/fstab.orig /etc/fstab
# reboot

21.3.5. 从磁盘故障中恢复

磁盘镜像的好处是,即使一个磁盘发生故障,镜像也不会丢失任何数据。在上面的例子中,如果 ada0 失效,镜像将继续工作,并从剩余的工作驱动器 ada1 提供数据。

要替换故障的驱动器,请关闭系统并将故障的驱动器物理上替换为容量相等或更大的新驱动器。制造商在以千兆字节为单位评估驱动器时使用了一些任意的值,唯一真正确定的方法是比较 diskinfo -v 显示的扇区总数。比镜像容量更大的驱动器也可以工作,尽管新驱动器上的额外空间将不会被使用。

在计算机重新启动后,镜像将以“降级”模式运行,只有一个驱动器。镜像被告知忘记当前未连接的驱动器。

# gmirror forget gm0

使用 元数据问题 中的说明清除替换磁盘上的任何旧元数据。然后将替换磁盘(此示例中为 ada4 )插入到镜像中。

# gmirror insert gm0 /dev/ada4

当新驱动器插入到镜像中时,重新同步过程开始。将镜像数据复制到新驱动器可能需要一段时间。在复制过程中,镜像的性能将大大降低,因此最好在计算机需求较低时插入新驱动器。

可以使用 gmirror status 来监视进度,它会显示正在同步的驱动器以及完成的百分比。在重新同步期间,状态将显示为 DEGRADED ,当过程完成时,状态将变为 COMPLETE

21.4. RAID3 - 以字节为单位的条带化和专用奇偶校验

RAID3 是一种将多个磁盘驱动器组合成一个具有专用奇偶校验磁盘的卷的方法。在 RAID3 系统中,数据被分割成多个字节,这些字节被写入阵列中的所有磁盘,除了一个充当专用奇偶校验磁盘的磁盘。这意味着从 RAID3 实现中读取磁盘时,会访问阵列中的所有磁盘。通过使用多个磁盘控制器,可以提高性能。 RAID3 阵列提供了 1 个驱动器的容错能力,同时提供了总容量为所有磁盘的总容量的 1-1/n 倍的容量,其中 n 是阵列中的硬盘数量。这种配置主要适用于存储较大的数据,如多媒体文件。

构建 RAID3 阵列至少需要 3 个物理硬盘。每个硬盘的大小必须相同,因为 I/O 请求会交错地并行读取或写入多个硬盘。此外,由于 RAID3 的特性,驱动器的数量必须等于 3 、 5 、 9 、 17 等,即 2 ^ n + 1 。

本节演示了如何在 FreeBSD 系统上创建一个软件 RAID3 。

虽然理论上可以在 FreeBSD 上从 RAID3 阵列引导,但这种配置并不常见,也不建议使用。

21.4.1. 创建一个专用的 RAID3 阵列

在 FreeBSD 中,对 RAID3 的支持是通过 graid3(8) GEOM 类实现的。在 FreeBSD 上创建一个专用的 RAID3 阵列需要以下步骤。

  1. 首先,通过执行以下命令之一加载内核模块 geom_raid3.ko

    # graid3 load

    或者:

    # kldload geom_raid3
  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

    许多数字将在屏幕上滑动,经过一段时间后,过程将完成。卷已创建并准备好挂载:

    # 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

21.5. 软件 RAID 设备

一些主板和扩展卡会添加一些简单的硬件,通常只是一个 ROM ,使得计算机可以从 RAID 阵列启动。启动后,对 RAID 阵列的访问由运行在计算机主处理器上的软件处理。这种“硬件辅助软件 RAID ”提供了不依赖于任何特定操作系统的 RAID 阵列,并且在加载操作系统之前就可以正常工作。

根据所使用的硬件,支持多个级别的 RAID 。有关完整列表,请参阅 graid(8)

graid(8) 需要 geom_raid.ko 内核模块,该模块从 FreeBSD 9.1 开始包含在 GENERIC 内核中。如果需要,可以使用 graid load 命令手动加载该模块。

21.5.1. 创建一个数组

软件 RAID 设备通常在计算机启动时按下特殊键进入菜单。该菜单可用于创建和删除 RAID 阵列。 graid(8) 也可以直接从命令行创建阵列。

graid label 用于创建一个新的阵列。本例中使用的主板具有 Intel 软件 RAID 芯片组,因此指定了 Intel 元数据格式。新的阵列被赋予了一个标签 gm0 ,它是一个镜像( RAID1 ),并使用驱动器 ada0ada1

当将驱动器制作成新的阵列时,一些空间将被覆盖。请先备份现有数据!

# graid label Intel gm0 RAID1 ada0 ada1
GEOM_RAID: Intel-a29ea104: Array Intel-a29ea104 created.
GEOM_RAID: Intel-a29ea104: Disk ada0 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-a29ea104: Subdisk gm0:0-ada0 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-a29ea104: Disk ada1 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-a29ea104: Subdisk gm0:1-ada1 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-a29ea104: Array started.
GEOM_RAID: Intel-a29ea104: Volume gm0 state changed from STARTING to OPTIMAL.
Intel-a29ea104 created
GEOM_RAID: Intel-a29ea104: Provider raid/r0 for volume gm0 created.

状态检查显示新的镜像已经准备就绪,可以使用了。

# graid status
   Name   Status  Components
raid/r0  OPTIMAL  ada0 (ACTIVE (ACTIVE))
                  ada1 (ACTIVE (ACTIVE))

数组设备出现在 [/dev/raid/]# 目录中。第一个数组被称为 [r0]# 。如果存在其他数组,它们将被称为 [r1]# 、 [r2]# 等等。

某些设备上的 BIOS 菜单可以创建带有特殊字符的数组名称。为了避免这些特殊字符带来的问题,数组被赋予简单的编号名称,例如 r0 。要显示实际的标签,例如上面的示例中的 gm0 ,请使用 sysctl(8) 命令。

# sysctl kern.geom.raid.name_format=1

21.5.2. 多个卷

一些软件 RAID 设备支持在阵列上创建多个卷。卷的工作方式类似于分区,允许将物理驱动器上的空间分割并以不同的方式使用。例如, Intel 软件 RAID 设备支持两个卷。此示例创建一个 40G 的镜像卷,用于安全存储操作系统,然后是一个 20G 的 RAID0 (条带)卷,用于快速临时存储。

# graid label -S 40G Intel gm0 RAID1 ada0 ada1
# graid add -S 20G gm0 RAID0

卷在 /dev/raid/ 目录下以额外的 .filenamerX 条目显示。一个包含两个卷的阵列将显示 .filenamer0.filenamer1

请参阅 graid(8) ,了解不同软件 RAID 设备支持的卷数。

21.5.3. 将单个驱动器转换为镜像

在特定条件下,可以将现有的单个驱动器转换为 graid(8) 阵列而无需重新格式化。为了在转换过程中避免数据丢失,现有的驱动器必须满足以下最低要求:

  • 驱动器必须使用 MBR 分区方案进行分区。 GPT 或其他在驱动器末尾具有元数据的分区方案将被 graid(8) 元数据覆盖和损坏。

  • 驱动器末尾必须有足够的未分区和未使用空间来容纳 graid(8) 元数据。这些元数据的大小各不相同,但最大的占用 64 M ,因此建议至少有这么多的可用空间。

如果驱动器符合这些要求,请先进行完整备份。然后使用该驱动器创建一个单驱动器镜像:

# graid label Intel gm0 RAID1 ada0 NONE

graid(8) 元数据被写入未使用空间的驱动器末尾。现在可以将第二个驱动器插入到镜像中:

# graid insert raid/r0 ada1

原始驱动器的数据将立即开始复制到第二个驱动器。在复制完成之前,镜像将以降级状态运行。

21.5.4. 将新驱动器插入阵列中

可以将驱动器插入到阵列中,作为替换已经故障或丢失的驱动器。如果没有故障或丢失的驱动器,新的驱动器将成为备用驱动器。例如,将新的驱动器插入到工作中的两个驱动器的镜像中,将得到一个带有一个备用驱动器的两个驱动器的镜像,而不是一个三个驱动器的镜像。

在镜像阵列的示例中,数据立即开始复制到新插入的驱动器上。新驱动器上的任何现有信息都将被覆盖。

# graid insert raid/r0 ada1
GEOM_RAID: Intel-a29ea104: Disk ada1 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-a29ea104: Subdisk gm0:1-ada1 state changed from NONE to NEW.
GEOM_RAID: Intel-a29ea104: Subdisk gm0:1-ada1 state changed from NEW to REBUILD.
GEOM_RAID: Intel-a29ea104: Subdisk gm0:1-ada1 rebuild start at 0.

21.5.5. 从阵列中移除驱动器

可以永久从阵列中移除单个驱动器,并擦除其元数据:

# graid remove raid/r0 ada1
GEOM_RAID: Intel-a29ea104: Disk ada1 state changed from ACTIVE to OFFLINE.
GEOM_RAID: Intel-a29ea104: Subdisk gm0:1-[unknown] state changed from ACTIVE to NONE.
GEOM_RAID: Intel-a29ea104: Volume gm0 state changed from OPTIMAL to DEGRADED.

21.5.6. 停止数组

可以在不删除驱动器上的元数据的情况下停止数组。当系统启动时,数组将重新启动。

# graid stop raid/r0

21.5.7. 检查数组状态

数组状态可以随时检查。在上面的示例中,当一个驱动器被添加到镜像中后,数据将从原始驱动器复制到新驱动器。

# graid status
   Name    Status  Components
raid/r0  DEGRADED  ada0 (ACTIVE (ACTIVE))
                   ada1 (ACTIVE (REBUILD 28%))

某些类型的阵列,例如 RAID0CONCAT ,如果磁盘故障,可能不会在状态报告中显示。要查看这些部分故障的阵列,请添加 -ga

# graid status -ga
          Name  Status  Components
Intel-e2d07d9a  BROKEN  ada6 (ACTIVE (ACTIVE))

21.5.8. 删除数组

通过删除数组中的所有卷,可以销毁数组。当删除最后一个存在的卷时,数组将停止,并且从驱动器中删除元数据。

# graid delete raid/r0

21.5.9. 删除意外的数组

驱动器可能意外地包含 graid(8) 元数据,这可能是由于先前的使用或制造商的测试。 graid(8) 将检测到这些驱动器并创建一个阵列,从而干扰对单个驱动器的访问。要删除不需要的元数据:

  1. 启动系统。在启动菜单中,选择 2 进入加载器提示符。输入:

    OK set kern.geom.raid.enable=0
    OK boot

    系统将禁用 graid(8) 启动。

  2. 备份受影响驱动器上的所有数据。

  3. 作为一种解决方法,可以通过添加来禁用 graid(8) 阵列检测。

    kern.geom.raid.enable=0

    /boot/loader.conf

    要永久删除受影响驱动器上的 graid(8) 元数据,请启动 FreeBSD 安装 CD-ROM 或内存棒,并选择 Shell 。使用 status 命令找到阵列的名称,通常为 raid/r0

    # graid status
       Name   Status  Components
    raid/r0  OPTIMAL  ada0 (ACTIVE (ACTIVE))
                      ada1 (ACTIVE (ACTIVE))

    按名称删除卷:

    # graid delete raid/r0

    如果显示了多个卷,请为每个卷重复此过程。在删除最后一个阵列后,卷将被销毁。

    重新启动并验证数据,如果需要的话,从备份中恢复。在元数据被删除后,还可以删除 /boot/loader.conf 中的 kern.geom.raid.enable = 0 条目。

21.6. GEOM Gate Network 是一种计算机网络技术。

GEOM 提供了一种简单的机制,通过使用 GEOM Gate 网络守护进程 ggated ,可以远程访问诸如磁盘、 CD 和文件系统等设备。拥有设备的系统运行服务器守护进程,处理客户端使用 ggatec 发出的请求。设备不应包含任何敏感数据,因为客户端和服务器之间的连接没有加密。

与 NFS 类似, ggated 也使用 exports 文件进行配置。该文件指定了哪些系统被允许访问导出的资源以及它们所提供的访问级别。例如,要给客户端 192.168.1.5 对第一个 SCSI 磁盘的第四个分区进行读写访问权限,可以在 /etc/gg.exports 文件中添加以下行:

192.168.1.5 RW /dev/da0s4d

在导出设备之前,请确保它当前没有挂载。然后,启动 ggated :

# ggated

有几个选项可用于指定备用的监听端口或更改 exports 文件的默认位置。有关详细信息,请参阅 ggated(8)

要在客户端机器上访问导出的设备,首先使用 ggatec 指定服务器的 IP 地址和导出设备的设备名称。如果成功,该命令将显示一个要挂载的 ggate 设备名称。将指定的设备名称挂载到一个空闲的挂载点上。此示例连接到 192.168.1.1 上的 /dev/da0s4d 分区,然后将 /dev/ggate0 挂载到 /mnt 上:

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

现在可以通过客户端上的 /mnt 访问服务器上的设备。有关 ggatec 的更多详细信息和一些使用示例,请参阅 ggatec(8)

如果设备当前已在服务器或网络上的任何其他客户端上挂载,则挂载将失败。如果需要同时访问网络资源,请使用 NFS 。

当设备不再需要时,使用 umount 命令卸载它,以便资源可以供其他客户端使用。

21.7. 标记磁盘设备

在系统初始化过程中, FreeBSD 内核会在发现设备时创建设备节点。这种探测设备的方法会引发一些问题。例如,如果通过 USB 添加了一个新的磁盘设备,那么很可能会将一个闪存设备分配为 da0 的设备名称,而原来的 da0 则会变成 da1 。这将导致挂载文件系统时出现问题,如果这些文件系统在 /etc/fstab 中列出,可能还会阻止系统启动。

一种解决方案是按顺序连接 SCSI 设备,这样添加到 SCSI 卡的新设备将被分配未使用的设备号。但是对于可能替换主要 SCSI 磁盘的 USB 设备怎么办?这是因为 USB 设备通常在 SCSI 卡之前进行探测。一种解决方案是在系统启动后再插入这些设备。另一种方法是只使用单个 ATA 驱动器,并且在 /etc/fstab 中不列出 SCSI 设备。

更好的解决方案是使用 glabel 为磁盘设备添加标签,并在 /etc/fstab 中使用这些标签。由于 glabel 将标签存储在给定提供者的最后一个扇区中,因此标签将在重新启动后保持持久。通过将此标签作为设备,文件系统可以始终被挂载,无论通过哪个设备节点进行访问。

glabel 可以创建临时标签和永久标签。只有永久标签在重新启动后保持一致。有关标签之间差异的更多信息,请参阅 glabel(8)

21.7.1. 标签类型和示例

永久标签可以是通用标签或文件系统标签。可以使用 tunefs(8)newfs(8) 创建永久文件系统标签。这些类型的标签将在 /dev 的子目录中创建,并根据文件系统类型进行命名。例如, UFS2 文件系统标签将在 /dev/ufs 中创建。可以使用 glabel label 创建通用永久标签。这些标签不特定于文件系统,并将在 /dev/label 中创建。

临时标签在下次重启时被销毁。这些标签是在 [/dev/label] 中创建的,适用于实验。可以使用 glabel create 命令创建临时标签。

要为 UFS2 文件系统创建一个永久标签而不破坏任何数据,请执行以下命令:

# tunefs -L home /dev/da3

现在应该在 /dev/ufs 中存在一个标签,可以将其添加到 /etc/fstab 中:

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

在尝试运行 tunefs 时,文件系统不能被挂载。

现在可以挂载文件系统了:

# mount /home

从这一点开始,只要在启动时加载了 geom_label.ko 内核模块,并且在 /boot/loader.conf 中或者存在 GEOM_LABEL 内核选项,设备节点可以在不对系统产生任何不良影响的情况下发生变化。

使用 newfs 命令和 -L 选项可以创建带有默认标签的文件系统。有关更多信息,请参阅 newfs(8)

可以使用以下命令来销毁标签:

# glabel destroy home

下面的示例展示了如何为引导磁盘的分区添加标签。

例 1. 在引导磁盘上标记分区

通过对引导磁盘上的分区进行永久标记,即使将磁盘移动到另一个控制器或转移到不同的系统,系统也应能够继续正常引导。对于本示例,假设使用单个 ATA 磁盘,系统当前将其识别为 ad0 。还假设使用标准的 FreeBSD 分区方案,包括 //var/usr/tmp ,以及一个交换分区。

重新启动系统,在 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)

glabel(8) 类支持基于唯一文件系统 id ufsid 的 UFS 文件系统的标签类型。这些标签可以在 /dev/ufsid 中找到,并在系统启动时自动创建。可以使用 ufsid 标签来使用 /etc/fstab 挂载分区。使用 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 标签的分区都可以通过这种方式挂载,无需手动创建永久标签,同时仍然可以享受设备名称无关挂载的好处。

21.8. 通过 GEOM 实现的 UFS 日志记录

在 FreeBSD 上,支持 UFS 文件系统的日志功能。该实现通过 GEOM 子系统提供,并使用 gjournal 进行配置。与其他文件系统日志实现不同, gjournal 方法是基于块的,而不是作为文件系统的一部分实现的。它是一个 GEOM 扩展。

日志记录存储了文件系统事务的日志,例如在元数据和文件写入到磁盘之前构成完整磁盘写入操作的更改。可以通过重新播放事务日志来重做文件系统事务,以防止文件系统不一致性。

这种方法提供了另一种机制来防止文件系统的数据丢失和不一致性。与跟踪和强制执行元数据更新的软更新以及创建文件系统镜像的快照不同,日志是专门用于此任务的磁盘空间中存储的。为了提高性能,日志可以存储在另一个磁盘上。在这种配置中,应该在要启用日志记录的设备之后列出日志提供者或存储设备。

GENERIC 内核提供对 gjournal 的支持。要在启动时自动加载 geom_journal.ko 内核模块,请将以下行添加到 /boot/loader.conf 文件中:

geom_journal_load="YES"

如果使用自定义内核,请确保以下行在内核配置文件中:

options	GEOM_JOURNAL

一旦模块加载完成,可以按照以下步骤在新的文件系统上创建一个日志。在这个例子中, da4 是一个新的 SCSI 磁盘:

# gjournal load
# gjournal label /dev/da4

这将加载模块并在 /dev/da4 上创建一个设备节点 /dev/da4.journal 。

现在可以在日志设备上创建 UFS 文件系统,然后将其挂载到现有的挂载点上:

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

在多个切片的情况下,将为每个单独的切片创建一个日志。例如,如果 ad4s1ad4s2 都是切片,那么 gjournal 将创建 ad4s1.journalad4s2.journal

可以使用 tunefs 在当前文件系统上启用日志记录。然而,在尝试更改现有文件系统之前, 一定要 先备份数据。在大多数情况下,如果无法创建日志, gjournal 将失败,但这并不能保护免受错误使用 tunefs 导致的数据丢失。有关这些命令的更多信息,请参阅 gjournal(8)tunefs(8)

可以对 FreeBSD 系统的引导磁盘进行日志记录。有关详细说明,请参阅文章《在桌面 PC 上实现 UFS 日志记录》( extref:https://docs.freebsd.org/en/articles/gjournal-desktop/ )。


上次修改时间: September 18, 2024 by fiercex