# 21.3.RAID1——镜像

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

以下是两个常见的情况。第一个示例创建一个由两块新磁盘组成的镜像，并用它替代现有的单个磁盘。第二个示例在单个新磁盘上创建一个镜像，将旧磁盘的数据复制到其中，然后将旧磁盘插入镜像中。尽管此过程稍微复杂一些，但只需要一个新磁盘。

传统上，镜像中的两个磁盘通常在型号和容量上完全相同，但 [gmirror(8)](https://man.freebsd.org/cgi/man.cgi?query=gmirror\&sektion=8\&format=html) 并不要求如此。使用不同的磁盘创建的镜像，其容量将等于镜像中最小磁盘的容量。较大磁盘上的额外空间将无法使用。稍后插入镜像的磁盘必须至少与镜像中已存在的最小磁盘容量相同。

> **警告**
>
> 这里展示的镜像过程是无损的，但和任何重大磁盘操作一样，首先请进行完整的备份。

> **警告**
>
> 尽管在这些操作中使用了 [dump(8)](https://man.freebsd.org/cgi/man.cgi?query=dump\&sektion=8\&format=html) 来复制文件系统，但它不能在启用了软更新日志的文件系统上工作。请参阅 [tunefs(8)](https://man.freebsd.org/cgi/man.cgi?query=tunefs\&sektion=8\&format=html) 获取有关检测和禁用软更新日志的更多信息。

## 21.3.1. 元数据问题

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

可以使用 [gpart(8)](https://man.freebsd.org/cgi/man.cgi?query=gpart\&sektion=8\&format=html) 擦除 GPT 元数据。此示例擦除磁盘 **ada8** 的主 GPT 和备份 GPT 分区表：

```sh
# gpart destroy -F ada8
```

可以使用 [gmirror(8)](https://man.freebsd.org/cgi/man.cgi?query=gmirror\&sektion=8\&format=html) 将磁盘从活动镜像中移除，并一步擦除元数据。此示例中，磁盘 **ada8** 被从活动镜像 **gm4** 中移除：

```sh
# gmirror remove gm4 ada8
```

如果镜像未运行，但磁盘上仍有旧的镜像元数据，可以使用 `gmirror clear` 来移除它：

```sh
# gmirror clear ada8
```

[gmirror(8)](https://man.freebsd.org/cgi/man.cgi?query=gmirror\&sektion=8\&format=html) 在磁盘末尾存储一块元数据。由于 GPT 分区方案也在磁盘末尾存储元数据，因此不建议使用 [gmirror(8)](https://man.freebsd.org/cgi/man.cgi?query=gmirror\&sektion=8\&format=html) 镜像整个 GPT 磁盘。这里使用的是 MBR 分区，因为它仅在磁盘开始处存储分区表，不与镜像元数据冲突。

## 21.3.2. 使用两块新磁盘创建镜像

在这个示例中，FreeBSD 已经安装在单个磁盘 **ada0** 上。两块新磁盘 **ada1** 和 **ada2** 已连接到系统。将创建一个新的镜像，将这两块磁盘用于替换旧的单个磁盘。

**geom\_mirror.ko** 内核模块必须内置到内核中，或在启动时或运行时加载。现在手动加载内核模块：

```sh
# gmirror load
```

使用这两块新磁盘创建镜像：

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

**gm0** 是用户选择的设备名称，分配给新创建的镜像。镜像启动后，设备名称会出现在 **/dev/mirror/** 中。

现在可以使用 [gpart(8)](https://man.freebsd.org/cgi/man.cgi?query=gpart\&sektion=8\&format=html) 在镜像上创建 MBR 和 bsdlabel 分区表。此示例使用传统的文件系统布局，包括 **/**、swap、**/var**、**/tmp** 和 **/usr** 分区。一个 **/** 和一个 swap 分区也可以工作。

镜像上的分区不需要与现有磁盘上的分区大小相同，但必须足够大，以容纳 **ada0** 上已有的所有数据。

```sh
# 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)
```

```sh
# 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 中安装引导代码并设置活动分区，使镜像可引导：

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

格式化新镜像上的文件系统，启用软更新：

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

现在，可以使用 [dump(8)](https://man.freebsd.org/cgi/man.cgi?query=dump\&sektion=8\&format=html) 和 [restore(8)](https://man.freebsd.org/cgi/man.cgi?query=restore\&sektion=8\&format=html) 将原始 **ada0** 磁盘上的文件系统复制到镜像上。

```sh
# 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**，指向新的镜像文件系统：

```sh
# 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**，以便在启动时加载该模块：

```sh
geom_mirror_load="YES"
```

重新启动系统以测试新镜像，并验证所有数据是否已复制。BIOS 会将镜像视为两个独立的磁盘，而不是一个镜像。由于磁盘是相同的，选择哪一个启动都无关紧要。

如果遇到问题，请参见 [故障排除](https://docs.freebsd.org/en/books/handbook/geom/#gmirror-troubleshooting)。关闭电源并断开原始的 **ada0** 磁盘后，可以将其保留为离线备份。

在使用中，镜像将像原来的单个磁盘一样运行。

## 21.3.3. 使用现有磁盘创建镜像

在此示例中，FreeBSD 已经安装在单个磁盘 **ada0** 上，并且已经连接了新磁盘 **ada1**。将会在新磁盘上创建一个单磁盘镜像，复制现有系统到该磁盘上，然后将旧磁盘插入到镜像中。由于 `gmirror` 需要在每个磁盘的末尾放置一个 512 字节的元数据块，而现有的 **ada0** 通常已经将所有空间分配完，因此此过程较为复杂。

加载 **geom\_mirror.ko** 内核模块：

```sh
# gmirror load
```

使用 `diskinfo` 检查原磁盘的媒体大小：

```sh
# diskinfo -v ada0 | head -n3
/dev/ada0
        512             # 扇区大小
        1000204821504   # 介质大小（以字节为单位） (931G)
```

在新磁盘上创建镜像。为了确保镜像的容量不大于原 **ada0** 驱动器的容量，使用 [gnop(8)](https://man.freebsd.org/cgi/man.cgi?query=gnop\&sektion=8\&format=html) 创建一个虚拟驱动器，大小与 **ada0** 相同。此驱动器不存储任何数据，仅用于限制镜像的大小。当 [gmirror(8)](https://man.freebsd.org/cgi/man.cgi?query=gmirror\&sektion=8\&format=html) 创建镜像时，它会将容量限制为 **gzero.nop** 的大小，即使新磁盘 **ada1** 有更多的空间。请注意，第二行中的 *1000204821504* 与 `diskinfo` 中显示的 **ada0** 的媒体大小相等。

```sh
# 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** 上的分区表。以下输出来自一个 1 TB 的驱动器。如果驱动器末尾有一些未分配的空间，内容可以直接从 **ada0** 复制到新的镜像。

然而，如果输出显示磁盘上的所有空间都已分配，如下所示，则无法为磁盘末尾的 512 字节镜像元数据留出空间。

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

在这种情况下，必须编辑分区表，通过减少 **mirror/gm0** 的容量一个扇区来为镜像元数据腾出空间。此过程将在后面说明。

无论哪种情况，都应首先使用 `gpart backup` 和 `gpart restore` 备份主磁盘的分区表。

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

这些命令会创建两个文件 **table.ada0** 和 **table.ada0s1**。这是来自一个 1 TB 驱动器的示例：

```sh
# cat table.ada0
MBR 4
1 freebsd         63 1953525105   [active]
```

```sh
# 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
```

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

```sh
# cat table.ada0
MBR 4
1 freebsd         63 1953525104   [active]
```

```sh
# 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**：

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

使用 `gpart show` 检查分区表。此示例中，**gm0s1a** 对应 **/**，**gm0s1d** 对应 **/var**，**gm0s1e** 对应 **/usr**，**gm0s1f** 对应 **/data1**，**gm0s1g** 对应 **/data2**。

```sh
# 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** 进行调整。

```sh
# 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 中安装引导代码并设置活动分区，使镜像可引导：

```sh
# 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** 来备份此文件。

```sh
# cp /etc/fstab /etc/fstab.orig
```

编辑 **/etc/fstab**，将 **/dev/ada0** 替换为 **mirror/gm0**。

```sh
# 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** 以在启动时加载它：

```sh
geom_mirror_load="YES"
```

现在可以使用 [dump(8)](https://man.freebsd.org/cgi/man.cgi?query=dump\&sektion=8\&format=html) 和 [restore(8)](https://man.freebsd.org/cgi/man.cgi?query=restore\&sektion=8\&format=html) 将原磁盘上的文件系统复制到镜像中。每个使用 `dump -L` 导出的文件系统将首先创建一个快照，这可能需要一些时间。

```sh
# 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** 相同的数据。若有问题启动，请参阅 [故障排除](https://docs.freebsd.org/en/books/handbook/geom/#gmirror-troubleshooting)。

此时，镜像仍然只由一个磁盘 **ada1** 组成。

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

> **重要**
>
> 当 **ada0** 被插入镜像时，它的原始内容将被镜像中的数据覆盖。确保在将 **ada0** 添加到镜像之前，**mirror/gm0** 的内容与 **ada0** 完全相同。如果使用 [dump(8)](https://man.freebsd.org/cgi/man.cgi?query=dump\&sektion=8\&format=html) 和 [restore(8)](https://man.freebsd.org/cgi/man.cgi?query=restore\&sektion=8\&format=html) 复制的内容与 **ada0** 上的内容不一致，请恢复 **/etc/fstab**，将文件系统挂载到 **ada0**，然后重新启动并重新开始整个过程。

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

同步将在两磁盘之间立即开始。使用 `gmirror status` 查看进度。

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

稍后，同步将完成。

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

**mirror/gm0** 现在由两个磁盘 **ada0** 和 **ada1** 组成，并且内容会在它们之间自动同步。在使用过程中，**mirror/gm0** 的表现将与原始单个驱动器相同。

## 21.3.4. 故障排除

如果系统无法启动，可能需要更改 BIOS 设置，以便从新镜像驱动器之一启动。两个镜像驱动器都可以用于启动，因为它们包含相同的数据。

如果启动时出现以下消息，说明镜像设备出现了问题：

```sh
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`。然后加载镜像模块并挂载镜像设备：

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

编辑 **/mnt/boot/loader.conf**，添加一行以加载镜像模块：

```sh
geom_mirror_load="YES"
```

保存文件并重启。

其他导致 `error 19` 的问题需要更多的努力来修复。尽管系统应从 **ada0** 启动，如果 **/etc/fstab** 不正确，仍会出现另一个提示符以选择一个 shell。在启动加载程序提示符下输入 `ufs:/dev/ada0s1a`，然后按 Enter 键。撤销 **/etc/fstab** 中的编辑，然后挂载原始磁盘 (**ada0**) 上的文件系统，而不是镜像。重新启动系统并再次尝试该过程。

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

## 21.3.5. 从磁盘故障中恢复

磁盘镜像的好处是，哪怕单个磁盘发生故障，镜像也不会丢失任何数据。在上述示例中，如果 **ada0** 故障，镜像将继续工作，从剩余的工作驱动器 **ada1** 提供数据。

要更换故障的驱动器，关闭系统并物理更换故障的驱动器，使用相同或更大容量的新驱动器。制造商在为驱动器评定容量时使用的值有些是任意的，唯一确保容量正确的方法是通过 `diskinfo -v` 比较总扇区数。容量大于镜像的驱动器可以使用，尽管新驱动器的额外空间不会被使用。

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

```sh
# gmirror forget gm0
```

应该清除新驱动器上任何旧的元数据，方法请参见 [元数据问题](https://docs.freebsd.org/en/books/handbook/geom/#geom-mirror-metadata)。然后，将新驱动器（例如 **ada4**）插入镜像中：

```sh
# gmirror insert gm0 /dev/ada4
```

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

可以通过 `gmirror status` 来监控进度，查看正在同步的驱动器及其完成百分比。在重同步过程中，状态将为 `DEGRADED`，当过程完成时，将变为 `COMPLETE`。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://handbook.bsdcn.org/di-21-zhang-geom-mo-kuai-hua-ci-pan-zhuan-huan-kuang-jia/21.3.-raid1-jing-xiang.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
