FreeBSD 使用手册
上一页 | 第19章 　存储 | 下一页

# 19.18 高可用性存储 (HAST)

Contributed by Daniel Gerzo. With inputs from Freddie Cash、 Pawel Jakub Dawidek、 Michael W. Lucas 和 Viktor Petersson.

## 19.18.1 概述

高可用性是担负关键业务的应用的一项主要需求， 而高可用存储则是这类环境中的一项关键组件。 高可用存储 Highly Available STorage， 或 HAST， 是由 Pawel Jakub Dawidek `<pjd@FreeBSD.org>` 开发的一种用于提供在两台物理上隔离的系统之间以透明的方式， 通过 TCP/IP 网络传输数据的高可用性框架。 HAST 可以看作通过网络进行的 RAID1 (镜像)， 类似于 GNU/Linux® 平台上的 DRBD® 存储系统。 配合 FreeBSD 提供的其他高可用性基础设施， 如 CARP， HAST 可以用来构建可以抗御硬件故障的高可用存储集群。

读完这节， 您将了解：

- 何为 HAST， 它如何工作以及提供哪些功能。

- 如何在 FreeBSD 上配置和使用 HAST。

- 如何与 CARP 及 devd(8) 配合构建可靠的存储系统。

在阅读这节之前， 您应：

- 了解 UNIX® 和 FreeBSD 的基础知识 (第 4 章)。

- 知道如何配置网络接口以及其他核心 FreeBSD 子系统 (第 12 章)。

- 理解 FreeBSD 的网络功能 (第 IV 部分 于 FreeBSD 使用手册)。

- 使用 FreeBSD 8.1-RELEASE 或更新版本。

HAST 项目是由 FreeBSD 基金会资助完成的， 并得到了来自 OMCnet Internet Service GmbH 和 TransIP BV 的支持。

## 19.18.2 HAST 的功能

HAST 系统提供的功能主要包括：

- 可以掩盖本地硬盘的 I/O 错误。

- 文件系统无关， 因而可以配合 FreeBSD 支持的任何文件系统使用。

- 高效率的快速重新同步机制， 令系统只同步在另一节点停机时修改过的块。

- 可以在已经部署好的环境中添加冗余。

- 配合 CARP、 Heartbeat 或其他类似的工具， 可以实现健壮的可靠存储系统。

## 19.18.3 HAST 的运行机制

由于 HAST 本质上是在多个机器间同步地进行块级复制， 因此它需要至少两个节点 (物理的机器) ── 其一作为 `主` (也称作 `master`) 节点， 另一个作为 `从` (`slave`) 节点。 这两台机器会共同构成一个集群。

> 注意: 目前 HAST 只能使用最多两个集群节点。

由于 HAST 是配置成以主从节点的方式运行， 在任何时刻都只能有唯一的一个节点是主节点。 `主` 节点， 也称作 `活跃` 节点， 负责处理由 HAST 管理的设备的全部 I/O 请求。 而 `从` 节点则会自动从 `主` 节点同步数据的变更操作。

在 HAST 系统中的物理设备包括：

- 本地磁盘 (在主节点上)

- 远程磁盘 (在从节点上)

HAST 在块的级别上同步运行， 这使其对文件系统和应用程序透明。 HAST 在 `/dev/hast/` 目录中提供标准的 GEOM 设备供其他工具或应用程序使用， 因此， 在使用上， 对应用程序或文件系统而言， HAST 提供的设备与普通的裸盘或分区等没有任何区别。

发到本地磁盘的每次写、 删除或缓存刷写操作， 都会同时通过 TCP/IP 发到远程磁盘上。 读操作是由本地磁盘完成， 除非本地磁盘上的数据不是最新的， 或发生了 I/O 错误。 在这种情况下， 读操作会在从节点上完成。

### 19.18.3.1 同步及复制模式

HAST 希望提供快速的故障恢复能力。 基于这一考量， 减少在某个节点停机后需要的同步时间就十分重要。 为了提供快速的同步能力， HAST 会维护一份保存在磁盘上的脏区段位映射表 (bitmap of dirty extents)， 在普通的同步模式中， 它只同步这些部分的数据 (初始的同步除外)。

处理同步有多种不同的方式， HAST 计划实现以下几种同步方式：

- memsync： 当本地的写操作已经完成， 并且远程节点汇报已经收到数据时， 便认为数据的写操作已经完成， 而不是等待远程节点完成数据的写操作。 远程节点在发出回应之后， 会立即开始执行写操作。 这种模式的目标是减少响应时间， 但在同时仍然保持很好的可靠性。 目前 memsync 复制模式尚未实现。

- fullsync： 只有在本地写操作完成， 并且远程的写操作也已经完成的情况下， 才认为数据的写操作已经完成。 这种模式是最保险， 同时也是最慢的一种复制模式。 这是目前系统预设的复制模式。

- async： 在本地写操作完成时， 即认为数据已经写完。 这是最快， 同时也是风险最大的复制模式， 一般而言只有在另一节点的延迟较大时才应考虑使用。 目前 async 复制模式尚未实现。

> 警告: 目前， 只支持 fullsync 复制模式。

## 19.18.4 HAST 的配置

HAST 需要 `GEOM_GATE` 支持才能正常工作。 系统自带的预设 `GENERIC` 内核 并不 包含 `GEOM_GATE`， 但默认的 FreeBSD 安装包含了 `geom_gate.ko` 内核模块。 如果对系统进行了裁剪， 则应确认这个模块是否可用。 此外， `GEOM_GATE` 也可以静态联编进内核， 方法是在内核的编译配置中添加下面的设置：

    options	GEOM_GATE

从操作系统的角度， HAST 框架包含了下面这些部件：

- 负责进行数据同步的 hastd(8) 服务程序，

- 用于执行管理操作的 hastctl(8) 用户态管理工具，

- 配置文件 hast.conf(5)。

下面的例子将介绍使用 HAST 在两个节点之间以 `主`-`从` 模式复制数据的方法。 两个节点的名字分别是 ``hasta`` 其 IP， 地址为 `172.16.0.1`， 以及 ``hastb``， 其 IP 地址为 `172.16.0.2`。 这两台机器都使用尺寸相同的磁盘 `/dev/`ad6`` 来专用于 HAST 的运行。 HAST 存储池 (有时也称为资源， 例如位于 `/dev/hast/` 的设备文件) 将命名为 ``test``。

HAST 的配置文件是 `/etc/hast.conf`。 在两个节点上， 这个文件的内容应该是完全一样的。 最简配置如下：

    resource test {
    	on hasta {
    		local /dev/ad6
    		remote 172.16.0.2
    	}
    	on hastb {
    		local /dev/ad6
    		remote 172.16.0.1
    	}
    }

如果需要更高级的配置， 请参阅联机手册 hast.conf(5)。

> 提示: 在 `remote` 语句中也可以使用主机名。 这种情况下需要确保这些主机名是可以解析的， 例如在 `/etc/hosts` 文件中， 或在本地 DNS 中进行了定义。

现在在两个节点上都有同样的配置了， 接下来我们需要创建 HAST 存储池。 在两个节点上分别运行下面的命令来初始化本地此怕， 并启动 hastd(8) 服务：

    # hastctl create test
    # /etc/rc.d/hastd onestart

> 注意: 没有 办法使用已经包含文件系统的 GEOM 设备来创建存储池 (换言之， 已经存在的文件系统无法转换为 HAST 管理的存储池)， 这是因为创建存储池的过程需要保存一些元数据， 而已经写入文件系统的设备不再能提供保存这些元数据所需的空间。

HAST 并不负责选择节点的角色 (`主` 或 `从`)。 节点的角色是由管理员手工， 或由类似 Heartbeat 这样的软件通过 hastctl(8) 来完成配置的。 在希望成为主节点的系统 (``hasta``) 上运行下面的命令令其成为主节点：

    # hastctl role primary test

类似地， 用下面的命令来指明从节点 (``hastb``)：

    # hastctl role secondary test

> 小心: 有可能会出现两个节点之间无法正常通讯， 但又都配置为主节点这样的情况； 这种称作 `脑分裂` 的状态是十分危险的。 在 第 19.18.5.2 节 中介绍了如何从这种状态中恢复的方法。

接下来， 可以在两个节点上分别用 hastctl(8) 工具来验证节点身份是否正确：

    # hastctl status test

这其中比较重要的是 `status`(状态) 这行， 在两个节点上， 其输出均应为 `complete`(完好)。 如果系统给出的输出是 `degraded` (降级)， 则表示出现了问题。 正常情况下， 节点间的同步已经开始。 当 `hastctl status` 命令报告的 `dirty` 数据块数量为 0 字节时， 表示两个节点的数据已经完全同步。

最后一步是在 GEOM 设备 `/dev/hast/`test`` 上创建文件系统。 这项工作必须在 `主` 节点上进行 (因为 `/dev/hast/`test`` 只在 `主` 节点上出现)， 随硬盘尺寸的不同， 这可能需要花费数分钟的时间：

    # newfs -U /dev/hast/test
    # mkdir /hast/test
    # mount /dev/hast/test /hast/test

一旦完成了 HAST 框架的配置， 最后一步就是确保 HAST 在系统引导过程中会自动启动了。 为了达到这个目的， 应在 `/etc/rc.conf` 文件中添加这行配置：

    hastd_enable="YES"

### 19.18.4.1 故障转移配置

这个例子的目的在于建立一套健壮的存储系统， 令其能够抵御在任何一个节点上发生的故障。 这其中的关键任务是对集群中的 `主` 节点发生故障的情形进行及时的补救处理。 当发生这种情况时， `从` 节点可以无缝地接手主节点的工作， 对文件系统进行检查并挂接， 从而继续运行， 而不损失任何数据。

为了达成这一任务， 需要使用 FreeBSD 提供的另一项功能 ── CARP 所提供的 IP 层自动故障转移能力。 CARP 是共用地址冗余协议 Common Address Redundancy Protocol 的缩写， 它允许多个同网段的主机共享同一 IP 地址。 请根据 第 32.14 节 的介绍在两个节点上都配置 CARP。 完成这些配置之后， 两个节点都会有自己的 `carp0` 网络接口， 共用 IP 地址 `172.16.0.254`。 显然， 集群中的 HAST 主节点也必须是 CARP 主节点。

前面一节中创建的 HAST 存储池现在可以提供给网络上的其他主机使用了。 其上的文件系统可以通过 NFS、 Samba 等等， 以共用 IP 地址 `172.16.0.254` 来访问。 现在余下的唯一问题是自动化对主节点故障的处理。

当 CARP 网络接口的链路状态发生变化时， FreeBSD 操作系统会产生一个 devd(8) 消息， 这样就可以监视 CARP 网络接口的状态了。 CARP 接口的状态变化表示节点发生故障， 或重新回到了网络中。 这些情况下需要运行特定的脚本来完成对应的处理。

为了截获 CARP 网络接口的状态变化， 需要在两个节点的 `/etc/devd.conf` 文件中添加如下的设置：

    notify 30 {
    	match "system" "IFNET";
    	match "subsystem" "carp0";
    	match "type" "LINK_UP";
    	action "/usr/local/sbin/carp-hast-switch master";
    };

    notify 30 {
    	match "system" "IFNET";
    	match "subsystem" "carp0";
    	match "type" "LINK_DOWN";
    	action "/usr/local/sbin/carp-hast-switch slave";
    };

为使编辑的配置生效， 需要在两个节点上执行下面的命令：

    # /etc/rc.d/devd restart

当网络接口 `carp0` 的状态发生变化时， 系统会产生一个通知消息， 这允许 devd(8) 子系统运行管理员指定的任意脚本， 在这个例子中是 `/usr/local/sbin/carp-hast-switch`。 这个脚本的作用是自动化故障转移。 关于前面 devd(8) 配置的具体含义， 请参阅联机手册 devd.conf(5)。

下面是一个这种脚本的示例：

    #!/bin/sh

    # Original script by Freddie Cash <fjwcash@gmail.com>
    # Modified by Michael W. Lucas <mwlucas@BlackHelicopters.org>
    # and Viktor Petersson <vpetersson@wireload.net>

    # The names of the HAST resources, as listed in /etc/hast.conf
    resources="test"

    # delay in mounting HAST resource after becoming master
    # make your best guess
    delay=3

    # logging
    log="local0.debug"
    name="carp-hast"

    # end of user configurable stuff

    case "$1" in
    	master)
    		logger -p $log -t $name "Switching to primary provider for ${resources}."
    		sleep ${delay}

    		# Wait for any "hastd secondary" processes to stop
    		for disk in ${resources}; do
    			while $( pgrep -lf "hastd: ${disk} \(secondary\)" > /dev/null 2>&1 ); do
    				sleep 1
    			done

    			# Switch role for each disk
    			hastctl role primary ${disk}
    			if [ $? -ne 0 ]; then
    				logger -p $log -t $name "Unable to change role to primary for resource ${disk}."
    				exit 1
    			fi
    		done

    		# Wait for the /dev/hast/* devices to appear
    		for disk in ${resources}; do
    			for I in $( jot 60 ); do
    				[ -c "/dev/hast/${disk}" ] && break
    				sleep 0.5
    			done

    			if [ ! -c "/dev/hast/${disk}" ]; then
    				logger -p $log -t $name "GEOM provider /dev/hast/${disk} did not appear."
    				exit 1
    			fi
    		done

    		logger -p $log -t $name "Role for HAST resources ${resources} switched to primary."

    		logger -p $log -t $name "Mounting disks."
    		for disk in ${resources}; do
    			mkdir -p /hast/${disk}
    			fsck -p -y -t ufs /dev/hast/${disk}
    			mount /dev/hast/${disk} /hast/${disk}
    		done

    	;;

    	slave)
    		logger -p $log -t $name "Switching to secondary provider for ${resources}."

    		# Switch roles for the HAST resources
    		for disk in ${resources}; do
    			if ! mount | grep -q "^/dev/hast/${disk} on "
    			then
    			else
    				umount -f /hast/${disk}
    			fi
    			sleep $delay
    			hastctl role secondary ${disk} 2>&1
    			if [ $? -ne 0 ]; then
    				logger -p $log -t $name "Unable to switch role to secondary for resource ${disk}."
    				exit 1
    			fi
    			logger -p $log -t $name "Role switched to secondary for resource ${disk}."
    		done
    	;;
    esac

简而言之， 在节点成为网络的 `master` / `primary` 节点时， 脚本会进行下面的操作：

- 在本节点升格为 HAST 存储池的主节点。

- 检查 HAST 存储池上的文件系统。

- 挂接存储池中的文件系统到适当的位置。

当节点成为 `backup` / `secondary` 节点时：

- 卸下 HAST 存储池。

- 将本节点降格为 HAST 存储池的从节点。

> 小心: 务必注意， 上面的脚本只是概念性的介绍。 它并不能处理所有可能发生的情况， 因此应根据实际情况进行修改， 例如启动/停止必要的服务， 等等。

> 提示: 在前面的例子中， 出于示范的目的我们使用的是标准的 UFS 文件系统。 为了减少恢复所需的时间， 可以使用带日志的 UFS 文件系统， 或者使用 ZFS 文件系统。

更具体的信息和例子请参阅 HAST Wiki 页面。

## 19.18.5 故障排除

### 19.18.5.1 一般故障排除提示

HAST 通常都能够无故障地运行， 不过， 和任何其他软件产品一样， 有时它也可能会无法以希望的方式运转。 导致问题的可能性有很多， 但一般来说， 首先要确保集群中所有节点的时间是同步的。

当尝试排除 HAST 故障时， 应提高 hastd(8) 的调试级别。 这可以通过在启动 hastd(8) 服务时指定 `-d` 参数来实现。 需要说明的是， 可以多次指定这一参数来进一步提高调试级别。 此外， 还可以考虑使用 `-F` 参数来启动服务， 它会令 hastd(8) 服务在前台运行。

### 19.18.5.2 从脑分裂状态恢复

当集群中的两个节点之间无法相互通讯时， 两个节点都会认为自己是主节点， 从而导致 `脑分裂` 的状态。 这种情形十分危险， 因为两个节点会产生互相无法合并的数据。 这种情形需要系统管理员实施手工干预。

从这种状态中恢复时， 管理员必须决定哪一个节点包含最重要的数据变动 (或者手工合并这些改动) 并让 HAST 进行一次完整的同步操作， 覆盖有问题的那个节点的数据。 要完成这个工作，在有问题的节点上执行下面的命令：

    # hastctl role init <resource>
    # hastctl create <resource>
    # hastctl role secondary <resource>

上一页 | 起始页 | 下一页
对交换区进行加密 | 上一级 | GEOM： 模块化磁盘变换框架

本文档和其它文档可从这里下载：ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题，请先阅读文档，如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.