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 开发的一种用于提供在两台物理上隔离的系统之间以透明的方式, 通过 TCP/IP 网络传输数据的高可用性框架。 HAST 可以看作通过网络进行的 RAID1 (镜像), 类似于 GNU/Linux® 平台上的 DRBD® 存储系统。 配合 FreeBSD 提供的其他高可用性基础设施, 如 CARPHAST 可以用来构建可以抗御硬件故障的高可用存储集群。

  读完这节, 您将了解:

  在阅读这节之前, 您应:

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

19.18.2 HAST 的功能

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

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 框架包含了下面这些部件:

  下面的例子将介绍使用 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 存储池现在可以提供给网络上的其他主机使用了。 其上的文件系统可以通过 NFSSamba 等等, 以共用 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>

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

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