運維必備:Zookeeper集群“腦裂”問題處理大全
本文重點分享Zookeeper腦裂問題的處理辦法。ZooKeeper是用來協(xié)調(diào)(同步)分布式進程的服務,提供了一個簡單高性能的協(xié)調(diào)內(nèi)核,用戶可以在此之上構建更多復雜的分布式協(xié)調(diào)功能。
腦裂通常會出現(xiàn)在集群環(huán)境中,比如ElasticSearch、Zookeeper集群。而這些集群環(huán)境有一個統(tǒng)一的特點,就是它們有一個大腦,比如ElasticSearch集群中有Master節(jié)點,Zookeeper集群中有Leader節(jié)點。
一、 Zookeeper集群節(jié)點為什么要部署成奇數(shù)
Zookeeper容錯指的是當宕掉幾個Zookeeper節(jié)點服務器之后,剩下的個數(shù)必須大于宕掉的個數(shù),也就是剩下的節(jié)點服務數(shù)必須大于n/2,這樣Zookeeper集群才可以繼續(xù)使用,無論奇偶數(shù)都可以選舉Leader。例如5臺Zookeeper節(jié)點機器最多宕掉2臺,還可以繼續(xù)使用,因為剩下3臺大于5/2。
至于為什么最好為奇數(shù)個節(jié)點?
這樣是為了以最大容錯服務器個數(shù)的條件下,能節(jié)省資源。
比如,最大容錯為2的情況下,對應的Zookeeper服務數(shù),奇數(shù)為5,而偶數(shù)為6,也就是6個Zookeeper服務的情況下最多能宕掉2個服務。
所以從節(jié)約資源的角度看,沒必要部署6(偶數(shù))個Zookeeper服務節(jié)點。
Zookeeper集群有這樣一個特性:集群中只要有過半的機器是正常工作的,那么整個集群對外就是可用的。
也就是說如果有2個Zookeeper節(jié)點,那么只要有1個Zookeeper節(jié)點死了,那么Zookeeper服務就不能用了,因為1沒有過半,所以2個Zookeeper的死亡容忍度為0。
同理,要是有3個Zookeeper,一個死了,還剩下2個正常的,過半了,所以3個Zookeeper的容忍度為1。
同理也可以多列舉幾個:2->0; 3->1; 4->1; 5->2; 6->2 就會發(fā)現(xiàn)一個規(guī)律,2n和2n-1的容忍度是一樣的,都是n-1,所以為了更加高效,何必增加那一個不必要的Zookeeper呢。
所以說,根據(jù)以上可以得出結論:從資源節(jié)省的角度來考慮,Zookeeper集群的節(jié)點最好要部署成奇數(shù)個!
二、 Zookeeper集群中的"腦裂"場景說明
對于一個集群,想要提高這個集群的可用性,通常會采用多機房部署,比如現(xiàn)在有一個由6臺zkServer所組成的一個集群,部署在了兩個機房:
圖1
正常情況下,此集群只會有一個Leader,那么如果機房之間的網(wǎng)絡斷了之后,兩個機房內(nèi)的zkServer還是可以相互通信的。如果不考慮過半機制,那么就會出現(xiàn)每個機房內(nèi)部都將選出一個Leader。
圖2
這就相當于原本一個集群,被分成了兩個集群,出現(xiàn)了兩個"大腦",這就是所謂的"腦裂"現(xiàn)象。
對于這種情況,其實也可以看出來,原本應該是統(tǒng)一的一個集群對外提供服務的,現(xiàn)在變成了兩個集群同時對外提供服務,如果過了一會,斷了的網(wǎng)絡突然聯(lián)通了,那么此時就會出現(xiàn)問題了。兩個集群剛剛都對外提供服務了,數(shù)據(jù)該怎么合并,數(shù)據(jù)沖突怎么解決等等問題。
剛剛在說明腦裂場景時有一個前提條件就是沒有考慮過半機制,所以實際上Zookeeper集群中是不會輕易出現(xiàn)腦裂問題的,原因就在于過半機制。
Zookeeper的過半機制:在領導者選舉的過程中,如果某臺zkServer獲得了超過半數(shù)的選票,則此zkServer就可以成為Leader了。
舉個簡單的例子:如果現(xiàn)在集群中有5臺zkServer,那么half=5/2=2,那么也就是說,領導者選舉的過程中至少要有三臺zkServer投了同一個zkServer,才會符合過半機制,才能選出來一個Leader。
那么Zookeeper選舉的過程中為什么一定要有一個過半機制驗證?
因為這樣不需要等待所有zkServer都投了同一個zkServer就可以選舉出來一個Leader了。這樣比較快,所以叫快速領導者選舉算法。
Zookeeper過半機制中為什么是大于,而不是大于等于?
這就是跟腦裂問題有關系了。比如回到上文出現(xiàn)腦裂問題的場景 (如上圖1):
當機房中間的網(wǎng)絡斷掉之后,機房1內(nèi)的三臺服務器會進行領導者選舉,但是此時過半機制的條件是 "節(jié)點數(shù) > 3",也就是說至少要4臺zkServer才能選出來一個Leader。
所以對于機房1來說它不能選出一個Leader,同樣機房2也不能選出一個Leader,這種情況下整個集群當機房間的網(wǎng)絡斷掉后,整個集群將沒有Leader。
而如果過半機制的條件是 "節(jié)點數(shù) >= 3",那么機房1和機房2都會選出一個Leader,這樣就出現(xiàn)了腦裂。這就可以解釋為什么過半機制中是大于而不是大于等于,目的就是為了防止腦裂。
如果假設我們現(xiàn)在只有5臺機器,也部署在兩個機房:
圖3
此時過半機制的條件是 "節(jié)點數(shù) > 2",也就是至少要3臺服務器才能選出一個Leader。
此時機房件的網(wǎng)絡斷開了,對于機房1來說是沒有影響的,Leader依然還是Leader;對于機房2來說是選不出來Leader的,此時整個集群中只有一個Leader。
因此總結得出,有了過半機制,對于一個Zookeeper集群來說,要么沒有Leader,要么只有1個Leader,這樣Zookeeper也就能避免了腦裂問題。
三、 Zookeeper集群"腦裂"問題處理
1、什么是腦裂?
簡單點來說,腦裂(Split-Brain) 就是比如當你的 cluster 里面有兩個節(jié)點,它們都知道在這個 cluster 里需要選舉出一個 master。那么當它們兩個之間的通信完全沒有問題的時候,就會達成共識,選出其中一個作為 master。
但是如果它們之間的通信出了問題,那么兩個結點都會覺得現(xiàn)在沒有 master,所以每個都把自己選舉成 master,于是 cluster 里面就會有兩個 master。
對于Zookeeper來說有一個很重要的問題,就是到底是根據(jù)一個什么樣的情況來判斷一個節(jié)點死亡down掉了?在分布式系統(tǒng)中這些都是有監(jiān)控者來判斷的,但是監(jiān)控者也很難判定其他的節(jié)點的狀態(tài),唯一一個可靠的途徑就是心跳,所以Zookeeper也是使用心跳來判斷客戶端是否仍然活著。
使用ZooKeeper來做Leader HA基本都是同樣的方式:
- 每個節(jié)點都嘗試注冊一個象征Leader的臨時節(jié)點,其他沒有注冊成功的則成為follower,并且通過watch機制 (這里有介紹) 監(jiān)控著leader所創(chuàng)建的臨時節(jié)點;
- Zookeeper通過內(nèi)部心跳機制來確定leader的狀態(tài),一旦Leader出現(xiàn)意外Zookeeper能很快獲悉并且通知其他的follower,其他flower在之后作出相關反應,這樣就完成了一個切換。這種模式也是比較通用的模式,基本大部分都是這樣實現(xiàn)的。
但是這里面有個很嚴重的問題,如果注意不到會導致短暫的時間內(nèi)系統(tǒng)出現(xiàn)腦裂。因為心跳出現(xiàn)超時可能是Leader掛了,但是也可能是Zookeeper節(jié)點之間網(wǎng)絡出現(xiàn)了問題,導致Leader假死的情況。
Leader其實并未死掉,但是與ZooKeeper之間的網(wǎng)絡出現(xiàn)問題導致Zookeeper認為其掛掉了然后通知其他節(jié)點進行切換,這樣follower中就有一個成為了Leader。
但是原本的Leader并未死掉,這時候client也獲得Leader切換的消息,仍然會有一些延時,Zookeeper通訊需要一個一個通知。
這時候整個系統(tǒng)在混亂中,很有可能有一部分client已經(jīng)通知到了連接到新的Leader上去了,而有的client仍然連接在老的Leader上。
如果同時有兩個client需要對Leader的同一個數(shù)據(jù)更新,并且剛好這兩個client此刻分別連接在新老的Leader上,就會出現(xiàn)很嚴重問題。
這里做下小總結:
- 假死:由于心跳超時(網(wǎng)絡原因導致的)認為Leader死了,但其實leader還存活著;
- 腦裂:由于假死會發(fā)起新的Leader選舉,選舉出一個新的Leader,但舊的Leader網(wǎng)絡又通了,導致出現(xiàn)了兩個Leader ,有的客戶端連接到老的Leader,而有的客戶端則連接到新的leader。
2、Zookeeper腦裂是什么原因導致的?
主要原因是Zookeeper集群和Zookeeper client判斷超時并不能做到完全同步,也就是說可能一前一后,如果是集群先于client發(fā)現(xiàn),那就會出現(xiàn)上面的情況。
同時,在發(fā)現(xiàn)并切換后通知各個客戶端也有先后快慢。一般出現(xiàn)這種情況的幾率很小,需要Leader節(jié)點與Zookeeper集群網(wǎng)絡斷開,但是與其他集群角色之間的網(wǎng)絡沒有問題,還要滿足上面那些情況,但是一旦出現(xiàn)就會引起很嚴重的后果,數(shù)據(jù)不一致。
3、Zookeeper是如何解決"腦裂"問題的?
要解決Split-Brain腦裂的問題,一般有下面幾種種方法:
- Quorums (法定人數(shù)) 方式: 比如3個節(jié)點的集群,Quorums = 2, 也就是說集群可以容忍1個節(jié)點失效,這時候還能選舉出1個lead,集群還可用。比如4個節(jié)點的集群,它的Quorums = 3,Quorums要超過3,相當于集群的容忍度還是1,如果2個節(jié)點失效,那么整個集群還是無效的。這是Zookeeper防止"腦裂"默認采用的方法;
- Redundant communications (冗余通信)方式:集群中采用多種通信方式,防止一種通信方式失效導致集群中的節(jié)點無法通信。
- Fencing (共享資源) 方式:比如能看到共享資源就表示在集群中,能夠獲得共享資源的鎖的就是Leader,看不到共享資源的,就不在集群中。
- 仲裁機制方式;
- 啟動磁盤鎖定方式。
要想避免Zookeeper"腦裂"情況其實也很簡單,在follower節(jié)點切換的時候不在檢查到老的Leader節(jié)點出現(xiàn)問題后馬上切換,而是在休眠一段足夠的時間,確保老的leader已經(jīng)獲知變更并且做了相關的shutdown清理工作了,然后再注冊成為master就能避免這類問題了。
這個休眠時間一般定義為與Zookeeper定義的超時時間就夠了,但是這段時間內(nèi)系統(tǒng)可能是不可用的,但是相對于數(shù)據(jù)不一致的后果來說還是值得的。
1)ZooKeeper默認采用了Quorums這種方式來防止"腦裂"現(xiàn)象
即只有集群中超過半數(shù)節(jié)點投票才能選舉出Leader。
這樣的方式可以確保Leader的唯一性,要么選出唯一的一個Leader,要么選舉失敗。在zookeeper中Quorums作用如下:
- 集群中最少的節(jié)點數(shù)用來選舉Leader保證集群可用;
- 通知客戶端數(shù)據(jù)已經(jīng)安全保存前集群中最少數(shù)量的節(jié)點數(shù)已經(jīng)保存了該數(shù)據(jù)。一旦這些節(jié)點保存了該數(shù)據(jù),客戶端將被通知已經(jīng)安全保存了,可以繼續(xù)其他任務。而集群中剩余的節(jié)點將會最終也保存了該數(shù)據(jù)。
假設某個Leader假死,其余的followers選舉出了一個新的Leader。這時,舊的Leader復活并且仍然認為自己是Leader,這個時候它向其他followers發(fā)出寫請求也是會被拒絕的。
因為每當新Leader產(chǎn)生時,會生成一個epoch標號(標識當前屬于那個Leader的統(tǒng)治時期),這個epoch是遞增的,followers如果確認了新的Leader存在,知道其epoch,就會拒絕epoch小于現(xiàn)任Leader epoch的所有請求。
那有沒有follower不知道新的Leader存在呢?有可能,但肯定不是大多數(shù),否則新Leader無法產(chǎn)生。Zookeeper的寫也遵循quorum機制,因此,得不到大多數(shù)支持的寫是無效的,舊Leader即使各種認為自己是leader,依然沒有什么作用。
Zookeeper除了可以采用上面默認的Quorums方式來避免出現(xiàn)"腦裂",還可以可采用下面的預防措施:
2)添加冗余的心跳線,例如雙線條線,盡量減少“裂腦”發(fā)生機會
3)啟用磁盤鎖
正在服務一方鎖住共享磁盤,"裂腦"發(fā)生時,讓對方完全"搶不走"共享磁盤資源。但使用鎖磁盤也會有一個不小的問題,如果占用共享盤的一方不主動"解鎖",另一方就永遠得不到共享磁盤。
現(xiàn)實中假如服務節(jié)點突然死機或崩潰,就不可能執(zhí)行解鎖命令。后備節(jié)點也就接管不了共享資源和應用服務。于是有人在HA中設計了"智能"鎖。即正在服務的一方只在發(fā)現(xiàn)心跳線全部斷開(察覺不到對端)時才啟用磁盤鎖。平時就不上鎖了。
4)設置仲裁機制
例如設置參考IP(如網(wǎng)關IP),當心跳線完全斷開時,2個節(jié)點都各自ping一下 參考IP,不通則表明斷點就出在本端,不僅"心跳"、還兼對外"服務"的本端網(wǎng)絡鏈路斷了,即使啟動(或繼續(xù))應用服務也沒有用了,那就主動放棄競爭,讓能夠ping通參考IP的一端去起服務。
更保險一些,ping不通參考IP的一方干脆就自我重啟,以徹底釋放有可能還占用著的那些共享資源。