阿里巴巴是个快乐的青年
分类: 云计算
2013-02-18 17:06:19
一、高可用集群管理器和分布式文件同步系统(DRBD)
本文档记录RabbitMQ实现active-passive高可用的传统技术。使用起来很方便且不会在故障转移时增加延迟。
在面对各种类型的失败时,有多种形式的高可用、复制和恢复方式。RabbitMQ可以以active-passive模式工作,这样持久化消息写到active节点的磁盘上,在active节点失效时,可以从passive节点恢复消息。非持久化消息将会丢失,passive节点提升需要一点时间,因为要从磁盘读取消息。
同时RabbitMQ也支持。集群的目的是便于扩展而不是便于可用。因此,在集群中,如果队列所在节点失效,则队列会丢失。而高可用则是当节点失效时,该节点上的持久化队列和持久化消息能从别的节点恢复。
图1 Pacemaker
集群和高可用组合起来创建一个集群,该集群既能规模超过单节点又能在节点失效时保持持久化消息和持久化资源。
二、要求
本指南假设你打算使用 Ha stack来管理和监控资源。该指南也将使用来提供共享存储区用于active节点写入消息。如果你有NAS或者SAN或者其它的方式来为两个节点提供可靠共享存储,则你可以以此来代替DRBD。如果你打算使用DRBD,需要注意的是我们随后使用的DRBD OCF脚本仅仅在DRBD 8.3.2这个版本中出现。
本指南使用CoroSync(OpenAIS的精简版)来作为消息层下面的Pacemaker,其优先于Heartbeat。然而,为了能够访问Heartbeat的OCF脚本,我们也安装了Heartbeat。你会发现不管你使用的是CoroSync、Heartbeat还是OpenAIS,指令都能很好的工作。
需要注意的是CoroSync依赖多播来发现资源。在一些环境(例如:EC2)中,多播不可用。Heartbeat能使用单播来发现资源,其是在多播不可用时推荐的解决方案。
本指南不会告诉你怎样安装Pacemaker、Heartbeat、OpenAIS、CoroSync或者DRBD,因为下面有一些非常不错的指南来帮助你完成这些工作:
(1)
(2)通用Pacemaker文档:尤其是see the Clusters from scratch guides
(3)
在2010年8月,Debian testing and Ubuntu lucid都含有非常新版本的Heartbeat、CoroSync和Pacemaker,其将没必要再从源码来编译。
如果你正从源码编译Pacemaker等,要注意的是各种autoconf配置脚本好像没有针对各种库做足够的测试,即使配置脚本通过,也可能因缺少库而导致编译最终失败。在Debian Sid上,虽然会随不同发布版而不同,但我们不得不安装如下扩展包:
autoconf libtool pkg-config libglib2.0-dev libxml2 libxml2-dev uuid-dev uuid-runtime libnss3-dev groff libxslt1.1 libxslt1-dev libesmtp5 libesmtp-dev libssl-dev ssmping xsltproc.
需要注意的是我们会发现如果build失败,只安装必需的库不足以使build通过,在其能发现新库和编译正确前,我们不得不倒回去,然后重新运行autotool和configure。
三、假定
如果你打算使用它,我们假设你已经在两个不同的机器上安装了Pacemaker和DRBD,这两台机器能互相访问。你应该已经配置了DRBD,因此为了DRBD资源设置一个节点为primary,其它节点为secondary。在两个节点之间的初始同步已经完成了。我们DRBD资源drbd1配置像下面这样:
global {
usage-count no;
}
resource drbd1 {
device /dev/drbd1;
disk /dev/vdb;
meta-disk internal;
protocol C;
on ha-node-1 {
address 192.168.100.21:7789;
}
on ha-node-2 {
address 192.168.100.22:7789;
}
}
你已经在上面设置了文件系统且能mount和unmount它,我们假设如果你突然停止DRBD设备。crm configure show如下:
ha-node-2:~# crm configure show
node ha-node-1
node ha-node-2
property $id="cib-bootstrap-options" \
dc-version="1.0.7-6fab059e5cbe82faa65f5aac2042ecc0f2f535c7" \
cluster-infrastructure="openais" \
expected-quorum-votes="2" \
stonith-enabled="false" \
no-quorum-policy="ignore" \
last-lrm-refresh="1268667122"
rsc_defaults $id="rsc-options" \
resource-stickiness="100"
需要注意的是这里我们已经设置stickiness到100,禁用STONITH,且告知Pacemaker不用担心没有quorum(看上面章节Create an Active/Passive cluster in the Clusters from scratch guides referenced)。实际上,你可能会有STONITH设备,且集群至少有3个节点,所以在节点失败时quorum能够保持,ring-fencing能出现。
四、DRBD在Pacemaker中
如果你计划使用DRBD来提供共享存储,你需要获取Pacemaker来管理它。这儿的指令是适合我们的设置,但是与中的Clusters from scratch guides几乎一样。DRBD一次仅允许一个节点访问共享设备,所以多个节点写入相同的数据在这儿没有任何风险。如果你使用其它方式来提供共享存储,你也许希望使用Pacemaker来确保任何时候只有一个节点mount共享存储区域。
ha-node-2:~# crm
crm(live)# cib new drbd
INFO: drbd shadow CIB created
crm(drbd)# configure primitive drbd ocf:linbit:drbd params drbd_resource="drbd1" op monitor interval="60s"
crm(drbd)# configure ms drbd_ms drbd meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" notify="true"
crm(drbd)# configure primitive drbd_fs ocf:heartbeat:Filesystem params device="/dev/drbd1" directory="/media/drbd1" fstype="ext3"
crm(drbd)# configure colocation fs_on_drbd inf: drbd_fs drbd_ms:Master
crm(drbd)# configure order fs_after_drbd inf: drbd_ms:promote drbd_fs:start
crm(drbd)# cib commit drbd
crm(drbd)# cib use live
crm(live)# cib delete drbd
crm(live)# exit
bye
ha-node-2:~#
这儿第一个configure命令创建一个drbd资源。然后在第二个configure中调用drbd_ms将其纳入一个master+slave资源。接下来创建一个drbd_fs资源,它知道怎样mount DRBD设备。可以只mount DRBD资源在master这个节点上,所以接下来创建一个托管指令fs_on_drbd,它声明drbd_fs和drbd_ms:master资源必须在同一节点上。最后,我们知道我们只能mount一次文件系统,节点已成功成为master节点的DRBD设备。类似地,在节点退化为secondary前,我们必须un-mount DRBD设备。它们都是通过创建指令来完成的,该指令指示在启动brbd_fs资源前提升drbd_ms资源。需要注意的是在提交shadow CIB成为live CIB后,切换回live CIB,我们删除旧的shadow CIB,这纯粹是一个管理运用而不是必不可少的。
如果你不适用DRBD,你只想配置一个单一的ocf:heartbeat:Filesystem,其能mount共享存储。
五、简单HA Rabbit
HA Rabbit的主要技巧是确保:当passive节点成为active节点时,其必须跟失败节点有相同的节点名称。它必须也能读和写共享存储中的文件,如果它也将要成为集群的一部分,则它必须也有相同的Erlang cookie。
首先在两个节点上安装RabbitMQ服务器。服务器以用户rabbitmq运行,rabbitmq是rabbitmq组的成员。必须确保两个节点的用户和组有相同的UIDs和GIDs。在安装RabbitMQ服务器前,你可以在所有节点上通过显示创建拥有相同UID和GID的rabbitmq用户和组来节省时间。如果需要,分别在两个节点上编辑/etc/passwd和/etc/group,然后重新安装RabbitMQ服务器以确保所有必要的文件属于正确的用户和组。也确保rabbitmq用户具有权限来写和读到共享存储挂载点(/media/drbd1在上面的例子中)。在这个阶段,虽然不是必需的,但是下一步要确保所有节点共享相同的Erlang cookie。rabbitmq主目录通常在/var/lib/rabbitmq,所以:
ha-node-2:~# scp /var/lib/rabbitmq/.erlang.cookie ha-node-1:/var/lib/rabbitmq/
ha-node-1:~# chown rabbitmq: /var/lib/rabbitmq/.erlang.cookie
ha-node-1:~# chmod 400 /var/lib/rabbitmq/.erlang.cookie
我们也需要确保:当节点启动时,他们不会start Rabbit。因此,编辑初始脚本(通常在/etc/init.d/rabbitmq-server),在顶部的注释之后插入exit 0(更准确的解决方案是使用类似update-rc.d rabbitmq-server disable S 2 3 4 5,具体取决于你的平台)。现在在Pacemaker中为Rabbit创建一个资源:
ha-node-2:~# crm
crm(live)# cib new bunny
INFO: bunny shadow CIB created
crm(bunny)# configure primitive bunny ocf:rabbitmq:rabbitmq-server params mnesia_base="/media/drbd1"
crm(bunny)# configure colocation bunny_on_fs inf: bunny drbd_fs
crm(bunny)# configure order bunny_after_fs inf: drbd_fs bunny
crm(bunny)# cib commit bunny
crm(bunny)# cib use live
crm(live)# cib delete bunny
crm(live)# exit
bye
ha-node-2:~#
我们通过调用bunny来创建资源,bunny是RabbitMQ实例。我们配置它,并将配置信息放到DRBD-backed挂载点中的数据库文件中。然后,我们需要bunny和drbd_fs资源,也需要确保在启动bunny资源前挂载了文件系统(同样,在un-mount文件系统前,停止bunny资源)。在任何active节点上都能够通过运行crm status看到资源的状态。
ha-node-2:~# crm status
============
Last updated: Mon Apr 12 17:29:50 2010
Stack: openais
Current DC: ha-node-2 - partition with quorum
Version: 1.0.7-6fab059e5cbe82faa65f5aac2042ecc0f2f535c7
2 Nodes configured, 2 expected votes
4 Resources configured.
============
Online: [ ha-node-2 ha-node-1 ]
bunny (ocf::rabbitmq:rabbitmq-server): Started ha-node-1
Master/Slave Set: drbd_ms
Masters: [ ha-node-1 ]
Slaves: [ ha-node-2 ]
drbd_fs (ocf::heartbeat:Filesystem): Started ha-node-1
因此,我们可以看到bunny和drbd_fs资源只运行在ha-node-1上,而且drbd_ms资源以ha-node-1作为其master,以ha-node-2作为其slave。mount的输出应该显示/media/drbd1,其只在ha-node-1上,并且运行ps ax|grep [r]abbit应该显示RabbitMQ只运行在ha-node-1上。
如果你停止ha-node-1(关掉它或者只停止Heartbeat或者CoroSync),你会发现所有的服务迁移到其它节点了。如果你创建一些持久化资源(例如:持久化队列或者交换机),你会发现这些保存下来的资源迁移到其它的节点上了。在这个挂载点上停止和启动节点是个好主意。在任何一个当前运行的节点上运行状态命令(rabbitmqctl –n rabbit@localhost status和rabbitmqctl –n rabbit@localhost cluster_status)都能查看RabbitMQ服务器的状态。
在所有节点上,ocf:rabbitmq:rabbitmq-server脚本默认设置节点名称为rabbit@localhost。这是最简单的解决方案,因为其总是能够解析localhost,但是也意味着不能加入集群。
为了停止bunny资源,必须运行crm resource bunny stop,启动它也类似:crm resource bunny start。需要注意的是在升级Rabbit前你需要手工做这件事情,就好像在使用二进制包前禁用rabbitmq-server初始脚本一样,这意味着在升级源码前包管理器将无法安全地停止RabbitMQ。除非升级正在改变数据库模式或者持久化数据的格式,你应该能够在passive节点上升级RabbitMQ,然后迁移RabbitMQ到passive节点上(crm resource migrate bunny),并且升级active节点。
六、通过一个IP地址访问 Rabbit
你可能希望高可用Rabbit总是能够通过相同的IP来访问。有很多方法能够实现这一点,例如:你可以使用TCP负载均衡器。这里我们演示通过Pacemaker来迁移一个IP地址和Rabbit。需要注意的是如果你乐意将各种IP build到你的客户端里,这样Rabbit也许是可用的,那么你也许根本不需要这一步。
在测试中,我们发现如果IP地址迁移是在同一个网络作为同一节点的另一个IP接口,IP迁移经常被Pacemaker终止。在这里示例,两个节点IP分别是192.168.100.21和192.168.100.22(都在/24)。我们应该选择Rabbit一直可用的IP 192.168.50.1(例如:一个不同的网络)。这似乎使IP地址迁移更可靠。
ha-node-2:~# crm
crm(live)# cib new ip
INFO: bunny shadow CIB created
crm(ip)# configure primitive ip ocf:heartbeat:IPaddr2 params ip="192.168.50.1" cidr_netmask="24"
crm(ip)# configure colocation bunny_on_ip inf: bunny ip
crm(ip)# configure order bunny_after_ip inf: ip bunny
crm(ip)# cib commit ip
crm(ip)# cib use live
crm(live)# cib delete ip
crm(live)# exit
bye
ha-node-2:~#
这都应该看起来很熟悉了。我们创建IP资源,bunny资源必须运行在同一节点作为IP资源,我们确保bunny资源在其之后启动,在其之前停止。RabbitMQ服务器默认监听所有网络接口,所以我们都做了,但是我们也能明确地告诉它监听我们选择的IP:
ha-node-2:~# crm_resource --resource bunny --set-parameter ip --parameter-value 192.168.50.1
ha-node-2:~#
上面的命令一样进入crm,使用configure编辑bunny,添加参数ip=” 192.168.50.1”。为了使Pacemaker检测到配置已经发生改变,RabbitMQ服务器应该立即重新启动。现在你会发现:当一个节点失败,所有资源转移到备用节点,然后Rabbit会在相同IP地址启动。因此,客户端能够完全无视哪个是active节点,哪个是passive节点。你也能通过编辑来为Rabbit指定IP地址。然而,那样也就需要同步所有HA节点上的配置文件,然后重启bunny资源。
七、RabbitMQ集群内的HA
最后,我们希望能构建一个Rabbit集群,其能容错并仍然是一个集群。我们的计划是构建一个拥有2个RabbitMQ服务器的集群,这2个服务器跨3个节点,其中一个节点是其它2个节点的passive节点。因此如果一个节点失败,要么passive节点死掉了,要么active节点死掉了,导致迁移到共享passive节点。
图2 Pacemaker-cluster
悲剧的是这里我们受限于DRBD:理想情况下,我们希望对于每个RabbitMQ服务,任意3个节点可能是active,并且任意3个节点可能是secondary。这就要求DRBD能有一个primary节点和多个secondary节点,并且在发生故障时能提升任意secondary节点为primary节点,然后重新添加任何失败节点为secondary,一般情况下做不到这点。然而,如果使用NAS或者SAN,则可能达到这种额外的弹性。
到目前为止,我们有两个节点:ha-node-1和ha-node-2,都完全配置有DRBD、RabbitMQ、移动IP和文件系统。我们现在再一次重复上述所有指令,但是这次用新机器ha-node-3和已经存在的ha-node-2。这个想法是ha-node-1和ha-node-3将是active节点,而ha-node-2将是passive节点。因此,当添加所有额外资源时,需要确保所有资源名称都不相同,并且明显需要考虑改变主机名、不同的挂载点、DRBD资源名称和不同的移动IP地址(我们假设是192.168.50.2)。
集群内Rabbit节点需要能够解析所有其它节点的主机名。目前一般设置节点名称为rabbit@localhost,但这必须改变,因为集群内其它节点不能解析localhost。我们不能直接使用IP地址(例如:我们不能用rabbit@192.168.50.1作为节点名称)而不转换为long-name,我们计划用rabbit-ha-1代替192.168.50.1作为主机名称,用rabbit-ha-2代替192.168.50.2作为主机名称,然后设置节点名称为rabbit@rabbit-ha-1和rabbit@rabbit-ha-2。有很多种方式能做到这一点:要么在DNS服务器中这样配置,要么编辑所有/etc/hosts文件来确保所有HA节点能够解析rabbit-ha-1和rabbit-ha-2。
做完这些后,你应该能够发布了(假设第二个RabbitMQ服务器资源叫bunny2):
ha-node-2:~# crm_resource --resource bunny --set-parameter nodename --parameter-value "rabbit@rabbit-ha-1"
ha-node-2:~# crm_resource --resource bunny2 --set-parameter nodename --parameter-value "rabbit@rabbit-ha-2"
ha-node-2:~#
bunny和bunny2资源应该立即重启,这反映在节点名称的变化。根据我们已经确保了所有HA节点都有相同的Erlang cookie,然后使用如下指令来构建集群:
ha-node-3:~# rabbitmqctl stop_app
ha-node-3:~# rabbitmqctl reset
ha-node-3:~# rabbitmqctl cluster rabbit@rabbit-ha-1 rabbit@rabbit-ha-2
ha-node-3:~# rabbitmqctl start_app
ha-node-3:~# rabbitmqctl cluster_status
Cluster status of node 'rabbit@rabbit-ha-2' ...
[{nodes,[{disc,['rabbit@rabbit-ha-1','rabbit@rabbit-ha-2']}]},
{running_nodes,['rabbit@rabbit-ha-1','rabbit@rabbit-ha-2']}]
...done.
ha-node-3:~#
这样就可以了,你会发现你杀死任何HA节点,Rabbit资源都会转移到passive节点,Rabbit将会重启,并会成功重连回集群。
八、配置参数
配置参数能够在Pacemaker的脚本ocf:rabbitmq:rabbitmq-server中提供,RABBITMQ_*环境变量的所有逻辑变化能够用于配置Rabbit,具体如下:
server:rabbitmq-server脚本路径
ctl:rabbitmqctl脚本路径
nodename:rabbitmq-server节点名称
ip:rabbitmq-server监听的IP地址
port:rabbitmq-server监听的端口号
config_file:配置文件位置
log_base:日志路径
mnesia_base:mnesia路径