20.15.高可用性存储(HAST) ========================== 高可用性是商业应用的主要要求之一,而高可用性存储是这种环境下的一个关键组成部分。在 FreeBSD 中,高可用性存储(HAST)框架允许在由 TCP/IP 网络连接的几台物理上分离的机器上透明地存储相同的数据。HAST 可以被理解为基于网络的 RAID1(镜像),并且类似于 GNU/Linux® 平台中使用的 DRBD® 存储系统。与 FreeBSD 的其他高可用性功能(如 CARP)相结合,HAST 使得建立一个高可用性的存储集群成为可能,它可以抵抗硬件故障的发生。 以下是 HAST 的主要特点: - 可以用来掩盖本地硬盘上的 I/O 错误。 - 文件系统无关,因为它可以与 FreeBSD 支持的任何文件系统一起工作。 - 高效和快速的重新同步机制,只有在一个节点停机期间被修改的块才会被同步。 - 可以在已经部署的环境中使用,以增加额外的冗余。 - 与 CARP 、 Heartbeat 或其他工具一起,它可以用来建立一个健壮而可靠的存储系统。 读完本节后,你会了解: - 什么是 HAST,它是如何工作的,以及它提供哪些功能。 - 如何在 FreeBSD 上设置和使用 HAST。 - 如何整合 CARP 和 `devd(8) `__ 来建立一个强大的存储系统。 在阅读本节之前,你应该: - 了解 UNIX® 和 FreeBSD 的基础知识 (`FreeBSD 基础 `__)。 - 知道如何配置网络接口和其他核心的 FreeBSD 子系统(\ `配置与优化 `__\ )。 - 对 FreeBSD 网络有一个很好的理解(\ `网络通信 `__\ ) HAST 项目由 FreeBSD 基金会赞助,并得到了 http://www.omc.net/ 和 http://www.transip.nl/ 的支持。 20.15.1. HAST 操作 ------------------ HAST 在两台物理机之间提供同步的块级复制:\ *主*\ (\ *primary*\ )节点和 *次*\ (\ *secondary*\ )节点。这两台机器一起被称为一个集群。 由于 HAST 在主—次配置中工作,它只允许集群节点中的一个在任何特定时间处于活动状态。主节点,也叫 *活动* 节点,是一个将处理所有 I/O 请求的 HAST 管理的设备。次节点是自动从主要节点同步的。 HAST 系统的物理组件是主节点上的本地磁盘,以及远程的次要节点上的磁盘。 HAST 在块级上同步运行,使它对文件系统和应用程序透明。HAST 在 **/dev/hast/** 中提供了标准的 GEOM 工具,供其他工具或应用程序使用。使用 HAST 提供的设备和普通磁盘或分区之间没有区别。 每个写入、删除或刷新操作都通过 TCP/IP 发送到本地磁盘和远程磁盘。每个读操作都是从本地磁盘提供的,除非本地磁盘不是最新的或发生 I/O 错误。在这种情况下,读取操作被发送到辅助节点。 HAST 试图提供快速的故障恢复。出于这个原因,在一个节点的故障后减少同步时间是很重要的。为了提供快速的同步,HAST 管理一个磁盘上的脏区映射表,并且只在常规的同步过程中同步这些表,但初始同步除外。 有许多方法来处理同步问题。HAST 实现了几种复制模式来处理不同的同步方法: - *memsync* :这种模式下,当本地写操作完成,远程节点确认数据到达时,但在实际存储数据之前,报告写操作完成。远程节点上的数据将在发送确认后直接存储。这种模式旨在减少延迟,但仍然提供良好的可靠性。这种模式是默认的。 - *fullsync* :当本地写和远程写都完成时,这种模式报告写操作已经完成。这是最安全和最慢的复制模式。 - *async* :这种模式在本地写操作完成后报告为完成。这是最快速和最危险的复制模式。它只应该在复制到远方节点时使用,因为其他模式的延迟太高。 20.15.2.配置 HAST ----------------- HAST 框架由几个部分组成: - `hastd(8) `__ 守护进程,提供数据同步。当这个守护进程被启动时,它将自动加载\ ``geom_gate.ko``\ 。 - 用户区管理工具——`hastctl(8) `__\ 。 - 配置文件 `hast.conf(5) `__\ 。这个文件在启动 HAST 之前必须存在。 如果用户希望在内核中静态地编译 ``GEOM_GATE`` 支持,则应在定制内核配置文件中加入这一行,然后按照\ `配置 FreeBSD 内核 `__ 的说明重建内核: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session options GEOM_GATE 下面的例子示范了如何在主-次操作中配置两个节点,使用 HAST 在两者之间复制数据。一个节点将被称为 ``hasta``\ ,其 IP 地址为 ``172.16.0.1``\ ;另一个是 ``hastb``\ ,其 IP 地址为 ``172.16.0.2``\ 。两个节点将有一个相同大小的专用硬盘 **/dev/ad6** 用于 HAST 操作。HAST 池,有时被称为资源或 **/dev/hast/** 中的 GEOM 设备,将被称为 ``test``\ 。 HAST 的配置是使用 **/etc/hast.conf** 完成的。这个文件在两个节点上应该是相同的。最简单的配置是: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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) `__\ 。 **技巧** 如果在 **/etc/hosts** 或本地 DNS 中定义的主机是可解析的,也可以在远程语句中使用主机名。 如果配置存在于两个节点上,就可以创建 HAST 池。在两个节点上运行这些命令,将初始元数据放到本地磁盘上并启动 `hastd(8) `__: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hastctl create test # service hastd onestart .. **注意** *不* 可能使用已经包含文件系统的 GEOM 设备来创建存储池,也不可能将现有的存储转换为 HAST 管理的池。这个程序需要在设备上存储一些元数据,而在现有的设备上没有足够的所需空间可用。 HAST 节点的\ ``主``\ 或\ ``次``\ 角色是由管理员或像 Heartbeat 这样的工具通过 `hastctl(8) `__ 来选择的。 在主节点 ``hasta`` 上执行这个命令: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hastctl role primary test 在次节点 ``hastb`` 上执行这个命令: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hastctl role secondary test 通过在每个节点上运行 ``hastctl`` 来验证结果: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hastctl status test 检查输出中的 ``status`` 行。如果它显示 ``degraded``\ ,说明配置文件出了问题。它应该在每个节点上显示 ``complete``\ ,即意味着节点之间的同步已经开始。当 ``hastctl status`` 报告说有 0 byte 的 ``脏区`` 映射时,同步就完成了。 下一步是在 GEOM 设备上创建一个文件系统并挂载它。这必须在主节点上完成。创建文件系统可能需要几分钟时间,这取决于硬盘的大小。这个例子在 **/dev/hast/test** 上创建了一个 UFS 文件系统: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # newfs -U /dev/hast/test # mkdir /hast/test # mount /dev/hast/test /hast/test 如果 HAST 框架被正确配置,最后一步是确保 HAST 在系统启动时自动启动。添加这一行到 **/etc/rc.conf** 即可: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session hastd_enable="YES" 20.15.2.1.故障转移配置 ~~~~~~~~~~~~~~~~~~~~~~ 这个例子的目标是建立一个强大的存储系统,它可以抵御任何特定节点的故障。如果主节点发生故障,副节点就会被无缝地接管,检查和挂载文件系统,并继续工作,不会丢失任何一点数据。 为了完成这项任务,共用地址冗余协议(CARP)被用来在 IP 层提供自动故障转移。CARP 允许同一网段的多个主机共享一个 IP 地址。根据\ `“共用地址冗余协议(CARP)” `__\ 中提供的文档,在集群的两个节点上设置 CARP。在这个例子中,每个节点将有自己的管理 IP 地址和 ``172.16.0.254`` 的共享 IP 地址。集群的主要 HAST 节点必须是主要的 CARP 节点。 在上一节中创建的 HAST 池现在已经准备好被导出到网络上的其他主机。这可以通过 NFS 或 Samba 导出,使用共享 IP 地址 ``172.16.0.254`` 来完成。唯一没有解决的问题是在主节点发生故障时的自动故障转移。 在 CARP 接口开启或关闭时,FreeBSD 操作系统会生成 `devd(8) `__ 事件,从而可以观察 CARP 接口的状态变化。CARP 接口上的状态变化表明其中一个节点失败或重新上线了。这些状态变化事件使运行一个脚本成为可能,该脚本将自动处理 HAST 故障切换。 为了捕捉 CARP 接口上的状态变化,在每个节点上的 **/etc/devd.conf** 中添加这个配置: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session notify 30 { match "system" "IFNET"; match "subsystem" "carp0"; match "type" "LINK_UP"; action "/usr/local/sbin/carp-hast-switch primary"; }; notify 30 { match "system" "IFNET"; match "subsystem" "carp0"; match "type" "LINK_DOWN"; action "/usr/local/sbin/carp-hast-switch secondary"; }; .. **注意** 如果系统运行的是 FreeBSD 10 及更高版本,把 CARP 配置的接口名称替换 **carp0**\ 。 在两个节点上重新启动 `devd(8) `__ 以使新配置生效: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # service devd restart 当指定的接口状态因开启或关闭而改变时,系统会产生一个通知,允许 `devd(8) `__ 子系统运行指定的自动故障切换脚本,\ **/usr/local/sbin/carp-hast-switch**\ 。关于这个配置的进一步说明,请参考 `devd.conf(5) `__\ 。 下面是一个自动故障转移脚本的例子: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session #!/bin/sh # Original script by Freddie Cash # Modified by Michael W. Lucas # and Viktor Petersson # The names of the HAST resources, as listed in /etc/hast.conf resources="test" # delay in mounting HAST resource after becoming primary # make your best guess delay=3 # logging log="local0.debug" name="carp-hast" # end of user configurable stuff case "$1" in primary) 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 ;; secondary) 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 简而言之,当一个节点成为主节点时,脚本会采取这些行动: - 将另一个节点上的 HAST 池提升为主池。 - 检查 HAST 池下的文件系统。 - 挂载该池。 当一个节点成为次节点时: - 解除 HAST 池的挂载。 - 将 HAST 池降级为次级。 .. **当心** 这只是一个作为概念证明的脚本例子。它并不能处理所有可能的情况,可以以任何方式对其进行扩展或改变,例如,启动或停止所需的服务。 **技巧** 在这个例子中,使用了基础的 UFS 文件系统。为了减少恢复所需的时间,可以使用带日志支持的 UFS 或 ZFS 文件系统来代替。 更详细的信息和更多的例子可以在以下网站找到: `http://wiki.FreeBSD.org/HAST `__\ 。 20.15.3.故障排除 ---------------- HAST 的工作一般应该没有问题。然而,就像任何其他软件产品一样,可能有的时候它不能像预期的那样工作。问题的来源可能是不同的,但经验法则是确保集群的节点之间的时间是同步的。 当排除 HAST 的故障时,应通过 ``hastd -d`` 来增加 `hastd(8) `__ 的调试级别。这个参数可以指定多次以进一步提高调试级别。也可以考虑使用 ``-F``\ ,它将在前台启动 ``hastd``\ 。 20.15.3.1.从“大脑分裂”中恢复过来 -------------------------------- 当集群的节点无法相互通信,而两个节点都被配置为主节点时,就会出现 *大脑分裂*\ 。这是一个危险的情况,因为它允许两个节点对数据进行不兼容的修改。必须由系统管理员手动纠正这个问题。 管理员必须决定哪个节点含有更重要的变化(或者手动执行合并改动)。然后,让 HAST 对有破损数据的节点执行完全同步。要做到这一点,在需要重新同步的节点上执行这些命令: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hastctl role init test # hastctl create test # hastctl role secondary test