第九章:GEOM_GATE和HAST

除了通过网络导出文件系统外,FreeBSD还允许你导出设备节点。

设备节点非常特定于操作系统——很少有理由直接在FreeBSD主机上寻址Linux设备节点,反之亦然。

但也许你需要从机器B访问机器A中的CD刻录机,或者你需要在没有足够空间存储该映像的主机上获取文件系统映像,或者你只是需要一些临时存储空间。

FreeBSD包括geom_gate内核模块,或者称作ggate,可以在网络上传输设备节点(磁盘和分区、内存设备、光驱)。

通过网络导出设备节点使各种事情称为可能。最明显的是在两个主机之间镜像一个磁盘分区,这样主机就可以不断同步这些分区上的数据。

FreeBSD的HAST,即高可用存储(Highly Available Storage),允许你在网络上同步磁盘。

这些此怕设备导出既不加密也不经过身份验证。任何能够拦截流量的人都可以查看数据。很少有网络人员能够识别磁盘设备事务,因此你可以通过隐藏获得少量的安全性,但永远不要依赖攻击者的无知。

如果你不信任网络层,请在VPN内或通过SSH隧道传输此流量。

默认情况下,这些导出使用TCP端口3080。如果在公共网络上部署geom_gate或HAST,请使用防火墙或数据包过滤器保护TCP/3080。更好的是,使用网线或VLAN直接连接到镜像主机。

geom_gate 缺点

geom_gate协议没法将用户空间文件系统的缺点与iSCSI使用的网络磁盘的优点结合起来。

与FUSE一样,geom_gate和HAST都通过用户空间程序传递存储。用户端程序偶尔会崩溃并死亡。虽然这些程序通过了非常彻底的测试并得到了广泛的信任,但部署用于存储的用户空间程序意味着,当出现问题时,你非常需要主动监控和自动流程恢复。

同样,通过网络访问存储设备会增加网络负载。偶尔使用geom_gate以低优先级访问设备节点应该不是问题。如果你使用HAST在网络上镜像块设备,或者期望geom_gate具有良好的性能,请将存储流量与常规网络流量分开——不是在主网络接口上使用VLAN,而是使用专用直连电缆将它们连接起来。

很多时候,文件系统不是具有冗余的正确位置。集群数据库最好在数据库服务器中完成,而不是文件系统中。不过,如果你真的需要文件系统层的冗余,FreeBSD可以做到。

导出存储设备: geom_gate

geom_gate GEOM类只能导出光驱、内存磁盘、磁盘和分区。

无法导出磁带驱动器、终端、/dev/random或其他此类设备;

无法导出不能挂载的设备;

无法导出已挂载的设备。

geom_gate 服务器设置

导出设备节点需要设置导出文件并启动ggate守护进程。

文件/etc/gg.exports列出已导出的设备,每个设备一行。每行有三个部分:

host可以是IP地址、IP段或主机名。

导出的设备可以有三种权限:读写(rw)、只读(ro)、只写(wo)。

最后一个是被导出的确切设备节点。

下面示例中,在整个网络中以读写方式导出磁盘设备/dev/da0:

设置好导出文件后,启动ggated。这个守护进程没有启动脚本,也不需要其他参数:

如果修改了/etc/gg.exports文件,需要重新启动ggated。

geom_gate 客户端设置

geom_gate客户端执行ggatec程序中的所有操作。没有配置文件。

要连接到导出的设备,需要给 ggatec create命令两个参数:ggated服务器的主机名和设备节点:

我们示例中的ggated服务器导出设备节点/dev/da0,服务器名称为storm。于是命令如下:

ggatec命令返回了本地设备节点(/dev/ggate0),这个设备节点是映射到远程设备节点/dev/da0上的。现在你可以像使用本地设备一样使用它:

像使用本地磁盘一样,创建分区和文件系统:

这在短期内效果很好,但更复杂的设置可能需要更大的灵活性。

要断开此设备的连接,可使用ggatec destroy命令,使用-u选项指定geom_gate设备的编号。对于我们刚才创建的/dev/ggate0,编号就是0。

要查看连接到系统的所有设备,可以运行ggatec list命令。添加-v标志可以查看各种设备的详细信息。

ggatec(8) 选项

使用ggate,你可以控制连接设备的权限、设备号等。虽然手册列出了每个选项,但以下是最常用的选项。

仅仅因为服务器提供对设备节点的完全访问并不意味着你一定想要这种访问。使用-o选项设置连接设备的权限。使用ro表示只读、wo表示只写、rw表示读写:

如果你长期使用多个geom_gate设备,你可能希望为每个设备分配特定的设备编号。虽然你应该始终使用标签管理磁盘,但并非所有存储设备都可以有标签。你可以标记光盘,但不能标记它们所在的驱动器。例如,/dev/ggate0始终是CD驱动器,/dev/ggate1始终是DVD驱动器,这很好。使用-u指定特定的设备编号:

可以在启动脚本中使用-u,当你计划长期使用geom_gate设备的话。

geom_gate 失败

将网络添加到存储中会为你的数据增加一层令人愉快的不确定性。你可以确信服务器的SATA电缆不会决定停止传输比特。网络不太稳定。使用geom_gate会将用户空间程序添加到组合中。

如果geom_gate组件死亡,存储请求将备份到客户端。使用ggatec rescue命令恢复断开的连接,-u选项指定设备号。以下示例为/dev/ggate3启动了一个新的ggate进程:

当你拯救该进程时,任何缓冲存储请求都会被发送到服务器。

如果你特别想要长期镜像存储,请查看下一节:高可用存储。

高可用存储

可以使用原始iSCSI或geom_gate构建自己的网络镜像磁盘,但FreeBSD包含了专门为此设计的高可用存储(Highly Available Storage——HAST)系统。

HAST工作在GEOM层。它位于存储提供者(如磁盘、ZFS卷或映像)之上。每个HAST实例都与另一个系统上的HAST提供者配对。

HAST支持守护进程hastd使用两个HAST设备保持同步。对提供者的任何更改都会反映在合作伙伴提供者上。

系统管理员为其中一个HAST服务器分配主要角色,为另一个分配次要角色。HAST在/dev/hast中为每对镜像存储设备提供了一个设备节点。像使用任何其他节点一样使用该设备节点——对其进行分区,在其上放置文件系统,将其用作ZFS池的一部分,等等。

当主主机发生故障时,辅助主机可以使自己成为主主机,挂载文件系统并恢复提供服务。这种故障转移不是自动的,但通过通用地址冗余协议(Common Address Redundancy Protocol)和devd很容易实现自动化。

在配置HAST之前,请仔细考虑是否需要磁盘级冗余。你的应用程序真的需要在两台机器上同步文件系统吗?或者HAST只是比实时MySQL复制更容易配置吗?跨网络镜像存储会引入许多可能的故障点。虽然数据库软件的复制方法可能会让你发疯,但恢复一个不稳定的数据库比恢复一个混乱的的文件系统要容易得多。

HAST最适合的场景是,你有两个物理服务器,并希望在它们之间镜像一块存储。

如果你的基础架构比两台主机更大,请考虑在两台服务器上使用两个iSCSI驱动器作为数据存储,并让多台主机将其配置为ZFS或GEOM镜像。如果你需要与其他操作系统的互操作性,应该用iSCSI。

配置HAST需要分配设备参与镜像、配置和初始化镜像、创建文件系统以及配置故障转移。

预配置HAST

灾难性失败时会发生什么?假设你的一个HAST节点着火,需要完全更换。替换HAST辅助节点必须初始化器HAST数据副本。

与ZFS不同,HAST不了解在其上运行的文件系统。

假设你有一个4TB的驱动器需要高可用存储,备份必须通过网络传输所有4TB。即使在跑满千兆网的前提下,重新初始化镜像需要将近9个小时。

使你的HAST提供者不超过实现其目的所需的规模。也许你真的需要4TB的硬盘。

在大多数情况下,我建议使用ZFS卷作为HAST存储的后端。当出现问题时,你可以轻松地克隆和快照zvols。如果你使用UFS,请使用磁盘映像文件。对于这些示例,我的两个HAST主机都使用1GB的ZFS卷zroot/hast1。此应用程序仅存储此卷上最新或最重要的数据。这将恢复丢失的HAST节点缩短到几秒钟。

设计HAST配置的一个目标应该是尽量减少需要同步的元数据量。你可以使用四个zvol作为HAST的后备存储,然后在它们之上构建一个条带镜像,但之后你必须在主机之间来回传递一堆ZFS元数据。网络磁盘已经很慢了,为啥还要让它更慢?将单个冗余设备置于HAST之下,并在本地处理任何文件系统冗余,会更高效、更快。但是,如果你需要多个HAST设备作为单独的实体,请继续使用它们。只是不要在客户端进行条带和镜像。

你可以在较大的文件系统上使用HAST,但这需要你仔细注意文件系统的完整性和系统管理实践。支持数TB HAST的文件系统可以直接放在磁盘分区上,但需要配置尽可能多的安全检查。如果恢复冗余存储需要9个小时,应尽可能少地执行该恢复。

配置HAST

使用hastctl和/etc/hast.conf配置HAST。

两台主机上的配置文件应相同。

强烈建议在配置管理工具(比如Ansible或Puppet)中维护该文件,以确保一致性。

HAST将块设备的每个镜像称为资源。最小的hast.conf描述了一个资源,没有任何特殊选项。镜像中的每个主机在资源描述中都有一个子部分。以下是一个用于最低限度复制的hast.conf:

这将创建一个名为mirror1的HAST设备,包含两个主机:www1和www2。

你必须使用每个主机的实际主机名。每个主机都有两个强制设置,用于查找镜像的本地和远程部分。对于这个镜像,我在每个主机上创建了ZFS卷hast1。我还提供了找到远程镜像的IP地址。本例中,这两天主机由一条专用网线连接。

某些HAST配置仅在资源条目内发生。其他配置可以在任何资源之外,并适用于所有资源。例如,稍后讨论的完整性选项可以在全局级别设置,以使它们适用于所有资源。

资源条目中的选项会覆盖全局设置。

使用hastctl初始化两个节点上的HAST资源。然后在两台主机上启用并启动hastd:

使用hastctl告诉主节点它是hast1资源的主节点:

在第二节点上,使用hastctl设置它为后备:

首次创建设备并分配角色时,HAST会快速初始化存储设备。

HAST 状态

要检查HAST设备的状态,可使用hastctl status。如果添加HAST资源的名称,则仅显示指定的资源:

这显示了单个资源hast1。

Status列的值为complete表示两台主机已同步。

Role列表示本机在HAST中的角色,这里是primary。

要查看主机的HAST资源的更完整统计信息,可使用hastctl list命令,如果加上资源名称,则仅显示指定资源。

这个命令会给出从脏块数量到本地写入错误数量的所有信息。

HAST 文件系统

每个HAST资源在/dev/hast中都有一个设备节点,以资源命名。特定资源的设备节点仅显示在该资源的主主机上。我们的示例资源hast1在主节点上以/dev/hast/hast1的形式提供。

在这里我在HAST资源上创建了一个ZFS池:

如果UFS更可取,你可以使用newfs。我们强烈建议在UFS上使用软更新日志(newfs -j)。

对于UFS或ZFS,设置noatime有助于减少HAST流量,并使你的主机更容易同步HAST设备。

当主主机和辅助主机切换角色时,HAST资源的设备节点将显示在辅助节点上。

HAST 故障转移

将HAST主机从次要角色切换到主要角色需要降级主要节点并升级次要节点。

降级主节点的不礼貌方式是剥夺权力。不过,要实现更体面的迁移,首先要使用HAST资源停用所有内容。关闭写入或读取资源的程序,卸载文件系统或导出zpool。

然后,你可以在主节点上使用hastctl来告诉它现在是辅助节点:

主机现在正在等待有人认领此资源的主服务器。两个节点的状态都优点像这样:

两台主机上的状态都有破折号,表示此资源出于空闲状态。

在备份节点上,声明主角色并导入池:

如果你使用的是UFS,请在挂载文件系统之前运行fsck。虽然手动切换角色后文件系统应该是干净的,但在挂载文件系统之前进行fsck是最佳做法。

在小型文件系统上,fsck不会花费很长时间,而在大型文件系统上你真的需要使用软更新日志记录。

同步和完整性选项

虽然HAST的默认行为在大多数情况下都是可以接受的,但可以微调主机之间的同步,以最适合实际的环境和要求。你可以调整复制模式、校验和的使用、压缩等。

这些选项可以按资源或全局设置。在任何资源以外全局设置选项意味着它适用于配置文件中的每个HAST资源。以下示例,我们全局设置了复制选项:

某些资源的性能或完整性要求可能与其他资源不同。你可以根据每个资源设置这些选项。

以下为资源hast1设置了复制选项:

根据需要设置选项。

复制模式

HAST复制模式控制操作系统何时确认将数据写入磁盘。

正如文件系统何以在同步模式、异步模式或介于两者之间的模式下工作一样,HAST运行你决定内核何时应该告诉应用程序写入已完成。复制模式严重影响存储系统的表现速度。许多写入磁盘的程序在存储系统确认收到数据之前不会继续下一步。老鸟称之为“磁盘阻塞”。

默认模式为memsync,在数据位于主主机的磁盘和辅助主机的内存中时确认写入。数据不需要在辅助主机的磁盘上,只要在内存中。作为正常操作的一部分,辅助主机将立即存储数据,但程序可以在不等待磁盘的情况下继续进行。

使用HAST最安全的方法是fullsync模式。HAST仅在主节点和辅助节点上的磁盘上安全写入时才确认收到写入。这保证了数据能够达到磁盘。写入必须离开主节点,穿过网络,同故宫辅助节点,并到达辅助节点的磁盘,然后应用程序才能继续运行。如果你的数据很关键,你可能需要快速磁盘硬件和完全同步模式。

memsync模式比fullsync更快,但会增加风险。如果辅助节点在错误的时刻发生故障,则数据将无法到达磁盘。如果主节点在辅助节点重新启动时发生故障,则两个节点上数据可能会完全丢失。由于HAST存在于文件系统之下,丢失的数据可能会损坏文件系统。如果真的需要多台机器上的冗余块设备存储时,应考虑全同步模式。如果应用程序不能承受小的性能损失,应重新考虑整个设计。

HAST还具有asnyc模式,一旦数据写入主节点的磁盘,它就会立即确认数据。数据将被发送到辅助节点,并写入那里的磁盘,但程序不会等待。异步模式极大地增加了数据丢失的风险,因为主节点可能会在辅助节点甚至接收到数据之前发生故障。

强烈不建议使用异步模式。你唯一应该考虑异步模式的时候是,如果辅助节点离主节点太远,光速会导致不可接受的延迟。即便如此,我们仍然鼓励重建你的应用程序,而不是使用异步模式。

使用replication关键字和所需值设置复制模式:

不过,复制模式只是管理完整性的一种方式。

校验和

HAST可以计算每个数据块的校验和,并将该校验和与数据一起传输。辅助主机使用该校验和来验证它写入磁盘的数据是否与主主机发送的数据相同。

HAST校验和不是为了防止恶意攻击者。校验和(checksum)和数据(data)都以明文形式在同一条线上传输。可以更改该数据的入侵者也可以更改校验和以进行匹配。然而,校验和对于检测传输错误非常有用。HAST支持CRC32和SHA265校验和。

使用checksum关键字设置校验和:

在生产机上应该值中使用校验和。大多数现代系统的处理能力远远超过其磁盘或网络吞吐量。计算SHA256校验和需要比计算CRC32校验和更多的处理能力。CRC32校验和完全足以捕捉你可能会遇到的常见比特错误。条件允许的情况下,应尽量使用SHA256。

默认情况下,HAST不使用任何校验和。如果你想用,就必须启用它。

压缩

压缩以处理器时间换取网络带宽。

如果你有很多磁盘写入,压缩它们可以减少HAST所需的带宽量,使其真正适合你的网络。

使用compression关键字设置压缩方法。

HAST默认压缩方法是hole,这意味着只有全零组成的块才会被压缩。由于全新的磁盘设备只包含零,因此hole压缩大大加速了资源初始化。然而,它对日常表现没有多大帮助。

如果你想常规压缩数,HAST支持lzf压缩算法。LZF在许多地方都有使用,包括在ZFS中。压缩不会造成伤害,但它并不总是像你希望的那样有效。当你在HAST设备上的ZFS文件系统上进行LZF压缩时,在HAST中启用LZF就没有意义了。不过,即使压缩没有帮助,也不会造成伤害。

你也可以使用none选项完全禁用压缩。

元数据刷新

硬盘写缓存会使硬件将数据无序地写入磁盘。HAST可能会告诉硬盘驱动器先写入块A,然后再写入块B,但写入缓存可以出于中间,并决定先写入块B,但是块B需要块A,并且系统在写入两个块之间失败,则硬件刚刚损坏了文件系统。这在文件系统元数据中最为常见。

metaflush选项告诉hastd在每次元数据更新后自动刷新写缓存。HAST通常在更新元数据后刷新写缓存。但是,如果硬件不支持写缓存刷新,HAST将停止尝试刷新它。

建议购买支持写缓存刷新的硬盘。

HAST 网络

你可以控制HAST监听网络的方式、发送连接的位置,以及为传出连接绑定的源地址。

listen关键字告诉hastd为传入连接绑定到哪个地址。这是辅助主机监听更新和主主机监听确认的IP地址和端口。HAST默认监听所有可用IP地址和接口上TCP端口8457。

你可以全局使用linsten选项,也可以在hast.conf的特殊每服务器部分中使用。以下将hastd锁定到每个主机上的一个IP地址:

当然,你也可以使用IPv6地址。你可以通过用冒号和端口号指定新端口来更改端口:

如果全局使用listen关键字,则表示所有主机都在指定的IP地址上监听网络。IP地址的全部意义在于它们对机器是唯一的。全局listen关键字仅用于更改主机hastd连接到所有可用IP地址时hastd使用的TCP端口。

我们鼓励你创建每个主机的条目,并限制需要监听的地址数量。

在每个资源的每个节点条目中,你可以控制远程对等端的地址和用于向该对等端发送的本地地址。我们已经看到了remote关键字,这是任何HAST配置的必要部分。source关键字位于remote旁边,允许你指定与该对等端的传出连接的源端口。

如果你的主机在该子网上有多个IP地址,并且远程对等端使用数据包过滤将连接限制为hastd,则需要使用source。

失效、启动,和分裂大脑(split brain)

只有一个工作节点的HAST对(pair),将进入degraded(降级)状态。如果一个HAST节点重启,另一个节点会认为HAST设备已降级。

如果主节点发生故障,可以将辅助节点切换为主节点,知道坏掉的节点恢复正常。

这种不确定性决定了HAST行为的一个关键方面。虽然你可以在启动时启动hastd,但HAST不会对等待状态做出任何假设。在系统启动时,HAST设备进入初始化状态。一旦你告诉它们该如何配置以达到你的目的,它们就准备好采取行动了。

你必须在系统启动后使用hastctl来告诉每个HAST设备其角色。

在正常操作中,你可能会认为新启动的HAST服务器将扮演次要角色,以使主服务器可以发送使新启动的设备保持最新所需要的所有更新。这是可行的——除非两台机器都是新启动的。

当两台机器同时启动时,它们都会等待对方声明主要角色,并向/var/log/messages发出抱怨。你需要一种方法,让一个节点声明主要角色。如果你有一个日志监视程序,当相关日志消息出现时,你可以让它升级一个首选节点。你可以登录并手动将其中一个节点升级为主节点。FreeBSD可以通过CARP和devd自动为你推广一个,如第10章所述。

HAST行为谨慎,因为可能有多个宿主同时声称主要角色,导致分裂大脑的情况。大脑分裂是不好的。每个主机都会按照自己的想法修改HAST设备,并向HAST对等端发送更新。同伴不听,因为它认为自己是主人。设备的每个副本上的数据都不同。你的工作是找出如何解决这些分歧。

如果软件可以很容易地检查两个HAST设备上的数据——比如,如果ls显示了你需要知道的内容——你就可以通过shell脚本以编程方式解决差异。如果它们是更复杂的记录,如数据库或日志,则需要分析数据本身已捕获和协调更改。如何做到这一点完全取决于数据。

分裂大脑的问题是为什么我不建议使用HAST作为数据库存储后端。向PostgreSQL,甚至MySQL或MariaDB这样的集群数据库都有内置的例程、易于理解的过程或公认的破解方法来解决大脑分裂问题。文件系统没有。

在许多情况下,系统管理员在数据协调方面遇到了困难,放弃了,并通过销毁HAST设备的一个副本、重新初始化它并让HAST重新同步来解决大脑分裂问题。它们以恢复服务的名义接受少量数据丢失。

一旦你调和了由大脑分裂引起的数据差异,重新同步两个HAST提供者。你不能告诉HAST来回洗牌bits,直到这两个设备都互相赶上。相反,你必须清除并重新初始化辅助主机上的HAST资源,强制备份主机完全重新同步。将资源置于init模式,然后重新运行create命令并为其分配次要角色。在这里,我重新创建了备份HAST资源hast1:

主服务器向辅助服务器提供设备上的所有内容,恢复完整性。

但最好是精心管理HAST,而不是一开始就分裂大脑。

HAST事件命令

使用exec关键字告诉HAST在事件上运行命令。当资源角色发生变化、主机之间的初始hastd连接、hastd对等端断开连接、辅助资源开始与主资源同步、资源完成同步、同步中断以及hastd检测到大脑分裂时,HAST会运行此脚本。每当发生任何事件时,都会运行以下脚本:

你不能有每个事件的脚本,一个脚本必须处理所有这些事件。HAST根据事件为脚本提供不同的参数。

脚本在事件发生后立即运行。你不能在交换机中放置为事件准备的命令。例如,当主机切换到辅助角色时,让脚本导出HAST支持的ZFS池时行不通的——当hastd运行脚本时,池的后端就不见了。但是,当主机承担主要角色时,你可以让脚本导入ZFS池。

脚本参数和执行

在角色更改时,hastd会运行带有四个参数的命令:role、资源名称、旧角色、新角色。两个主机都运行脚本。

当两台主机上的hastd首次互相连接时,两者都会运行带有两个参数的脚本:connnect和HAST资源名称。该脚本对主机共享的每个资源运行一次。同样,当两个主机失去其hastd连接时,脚本将使用参数disconnect和资源名运行。

同步事件使用事件名称(syncstart、syncdone或syncintr表示中断)和资源名称运行脚本。同步事件仅触发主节点上的脚本。

最后,如果发生大脑分裂,脚本将使用参数split brain和资源名称运行。该脚本在两个主节点上运行。

hastd(8) 脚本

hastd脚本是一组嵌套的case语句。我建议从测试服务器上的一个简单脚本开始。记录这些参数,并在脚本运行时记录。滥用你的测试服务器,看看记录了什么样的消息。

在这里,我将脚本用于两个功能。

首先,每当HAST资源开始同步时,它都会向服务器的根帐户发送邮件。理想情况下,您应该用SNMP陷阱或对监控系统的另一次调用替换电子邮件。理论上,HAST资源应该始终保持同步。故意同步意味着两台主机之间的连接出了问题。(每当两台主机切换角色时,您还会看到同步,因此此通知具有双重作用。)

其次,当主机升级为主角色时,它会导入一个以资源命名的ZFS池。也就是说,当这个主机成为HAST资源shared1的主主机时,它会导入一个ZFS池,也称为shared1。您也可以在此处启动流程。最后,记录所有事件。

根据需要为你的环境添加其他案例。

请注意,脚本在成为主节点和导入ZFS池之间等待三秒。HAST需要一点时间来执行切换并验证所有内容是否都已完成。如果你有大型HAST设备,请测试开关并根据需要调整时序。

如果你在HAST上使用UFS,你可以用调用fsck和mount命令替换zpool导入。为挂载点和HAST资源命名将简化你的脚本。

hast.conf、hastd和hastctl手册页对配置和使用HAST有更多的介绍。但是,要了解如何在主机发生故障时自动切换主设备,请阅读下一章。