Heim  >  Artikel  >  php教程  >  如何防止HA集群的脑裂

如何防止HA集群的脑裂

WBOY
WBOYOriginal
2016-06-13 08:50:371019Durchsuche

如何防止HA集群的脑裂

1. 引言

脑裂(split-brain),指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏。

对于无状态服务的HA,无所谓脑裂不脑裂;但对有状态服务(比如MySQL)的HA,必须要严格防止脑裂。(但有些生产环境下的系统按照无状态服务HA的那一套去配置有状态服务,结果可想而知...)

2. 如何防止HA集群脑裂

一般采用2个方法
1. 仲裁
当两个节点出现分歧时,由第3方的仲裁者决定听谁的。这个仲裁者,可能是一个锁服务,一个共享盘或者其它什么东西。
2. fencing
当不能确定某个节点的状态时,通过fencing把对方干掉,确保共享资源被完全释放,前提是必须要有可靠的fence设备。

理想的情况下,以上两者一个都不能少。
但是,如果节点没有使用共享资源,比如基于主从复制的数据库HA,我们也可以安全的省掉fence设备,只保留仲裁。而且很多时候我们的环境里也没有可用的fence设备,比如在云主机里。

那么可不可以省掉仲裁,只留fence设备呢?
不可以。因为,当两个节点互相失去联络时会同时fencing对方。如果fencing的方式是reboot,那么两台机器就会不停的重启。如果fencing的方式是power off,那么结局有可能是2个节点同归于尽,也有可能活下来一个。但是如果两个节点互相失去联络的原因是其中一个节点的网卡故障,而活下来的正好又是那个有故障的节点,那么结局一样是悲剧。

所以,单纯的双节点,无论如何也防止不了脑裂。

3. 没有fence设备是否安全

以PostgreSQL或MySQL的数据复制为例来说明这个问题。
在基于复制的场景下,主从节点没有共享资源,所以2个节点都活着本身没有问题。问题是客户端会不会访问到本该死掉的那个节点。这又牵扯到客户端路由的问题。

客户端路由有几种方式,基于VIP,基于Proxy,基于DNS或者干脆客户端维护一个服务端地址列表自己判断主从。不管采用哪种方式,主从切换的时候都要更新路由。

基于DNS的路由是不太靠谱的,因为DNS可能会被客户端缓存,很难清干净。

基于VIP的路由有一些变数,如果本该死掉的节点没有摘掉自己身上的VIP,那么它随时可能出来捣乱(即使新主已经通过arping更新了所有主机上的arp缓存,如果某个主机的arp过期,发一个arp查询,那么就会发生ip冲突)。所以可以认为VIP也是一种特殊的共享资源,必需把它从故障节点上摘掉。至于怎么摘,最简单的办法就是故障节点发现自己失联后自己摘,如果它还活着的话(如果它死了,也就不用摘了)。如果负责摘vip的进程无法工作怎么办?这时候就可以用上本来不太靠谱的软fence设备了(比如ssh)。

基于Proxy的路由是比较靠谱的,因为Proxy是唯一的服务入口,只要把Proxy一个地方更新了,就不会发生客户端误访问的问题了,但是也要考虑Proxy的高可用。

至于基于服务端地址列表的方法,客户端需要通过后台服务判断主从(比如PostgreSQL/MySQL会话是否处于只读模式)。这时,如果出现2个主,客户端就会错乱。为防止这个问题,原主节点发现自己失联后要自己把服务停掉,这和前面摘vip的道理是一样的。

因此,为了不让故障节点捣乱,故障节点应该在失联后自己释放资源,为了应对释放资源的进程本身出现故障,可以加上软fence。在这个前提下,可以认为没有可靠的物理fence设备也是安全的。

4. 主从切换后数据能否保证不丢

主从切换后数据会不会丢和脑裂可以认为是2个不同的问题。还以PostgreSQL或MySQL的数据复制为例来说明。

对PostgreSQL,如果配置成同步流复制,可以做到不管路由是否正确,都不会丢数据。因为路由到错误节点的客户端根本写不进任何数据,它会一直等待从节点的反馈,而它以为的从节点,现在已经是主子了,当然不会理它。当然如果老是这样也不好,但它给集群监视软件纠正路由错误提供了充足的时间。

对MySQL,即使配置成半同步复制,在超时发生后,它可能会自动降级为异步复制。为了防止MySQL的复制降级,可以设置一个超大的rpl_semi_sync_master_timeout,同时保持rpl_semi_sync_master_wait_no_slave为on(即默认值)。但是,这时如果从宕了,主也会hang住。这个问题的破解方法和PostgreSQL是一样的,或者配置成1主2从,只要不是2个从都宕机就没事,或者由外部的集群监视软件动态切换半同步和异步。
如果本来就是配置的异步复制,那就是说已经做好丢数据的准备了。这时候,主从切换时丢点数据也没啥大不了,但要控制自动切换的次数。比如控制已经被failover掉的原主不允许自动上线,否则如果因为网络抖动导致故障切换,那么主从就会不停的来回切,不停的丢数据,破坏数据的一致性。

5. 如何实现上面的策略

你可以自己完全从头开始实现一套符合上述逻辑的脚本。但我更倾向于基于成熟的集群软件去搭建,比如Pacemaker+Corosync+合适的资源Agent。Keepalived我是极不推荐的,它就不适合用于有状态服务的HA,即使你把仲裁,fence那些东西都加到方案里,总觉得别扭。

使用Pacemaker+Corosync的方案也有一些注意事项
1)了解资源Agent的功能和原理
了解资源Agent的功能和原理,才能知道它适用的场景。比如pgsql的资源Agent是比较完善的,支持同步和异步流复制,并且可以在两者之前自动切换,并且可以保证同步复制下数据不会丢失。但目前MySQL的资源Agent就很弱了,没有使用GTID又没有日志补偿,很容易丢数据,还是不要用的好,继续用MHA吧(但是,部署MHA时务必要防范脑裂)。

2)确保法定票数(quorum)
quorum可以认为是Pacemkaer自带的仲裁机制,集群的所有节点中的多数选出一个协调者,集群的所有指令都由这个协调者发出,可以完美的杜绝脑裂问题。为了使这套机制有效运转,集群中至少有3个节点,并且把no-quorum-policy设置成stop,这也是默认值。(很多教程为了方便演示,都把no-quorum-policy设置成ignore,生产环境如果也这么搞,又没有其它仲裁机制,是很危险的!)

但是,如果只有2个节点该怎么办?
一是拉一个机子借用一下凑足3个节点,再设置location限制,不让资源分配到那个节点上。
二是把多个不满足quorum小集群拉到一起,组成一个大的集群,同样适用location限制控制资源的分配的位置。

但是如果你有很多双节点集群,找不到那么多用于凑数的节点,又不想把这些双节点集群拉到一起凑成一个大的集群(比如觉得不方便管理)。那么可以考虑第三种方法。
第三种方法是配置一个抢占资源,以及服务和这个抢占资源的colocation约束,谁抢到抢占资源谁提供服务。这个抢占资源可以是某个锁服务,比如基于zookeeper包装一个,或者干脆自己从头做一个,就像下面这个例子。
http://my.oschina.net/hanhanztj/blog/515065
(这个例子是基于http协议的短连接,更细致的做法是使用长连接心跳检测,这样服务端可以及时检出连接断开而释放锁)
但是,一定要同时确保这个抢占资源的高可用,可以把提供抢占资源的服务做成lingyig高可用的,也可以简单点,部署3个服务,双节点上个部署一个,第三个部署在另外一个专门的仲裁节点上,至少获取3个锁中的2个才视为取得了锁。这个仲裁节点可以为很多集群提供仲裁服务(因为一个机器只能部署一个Pacemaker实例,否则可以用部署了N个Pacemaker实例的仲裁节点做同样的事情。)。但是,如非迫不得已,尽量还是采用前面的方法,即满足Pacemaker法定票数,这种方法更简单,可靠。

6.参考

http://blog.chinaunix.net/uid-20726500-id-4461367.html
http://my.oschina.net/hanhanztj/blog/515065
http://clusterlabs.org/doc/en-US/Pacemaker/1.1-plugin/html-single/Pacemaker_Explained/index.html
http://clusterlabs.org/wiki/PgSQL_Replicated_Cluster
http://mysqllover.com/?p=799
http://gmt-24.net/archives/1077

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn