如何防止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
http://www.bkjia.com/PHPjc/1073479.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/1073479.htmlTechArticle如何防止HA叢集的腦裂 1. 引言 腦裂(split-brain),指在一個高可用(HA)系統中,當聯絡著的兩個節點斷開聯絡時,本來為一個整體的系統...