集群腦裂是什么?
所謂腦裂問題(類似於精神分裂),就是同一個集群中的不同節點,對於集群的狀態有了不一樣的理解。
由於某些節點的失效,部分節點的網絡連接會斷開,並形成一個與原集群一樣名字的集群,這種情況成為集群腦裂(split-brain)現象。這個問題非常危險,因為兩個新形成的集群會同時索引和修改集群的數據。
今天,Elasticsearch集群出現了查詢極端緩慢的情況,通過以下命令查看集群狀態:
curl -XGET 'http://master:9200/_cluster/health'
或者
curl -XGET 'http://localhost:9200/_cluster/health'
發現,集群的總體狀態是red,本來9個節點的集群,在結果中只顯示了4個;但是,將請求發向不同的節點之后,我卻發現即使是總體狀態是red的,但是可用的節點數量卻不一致。
正常情況下,集群中的所有的節點,應該對集群中master的選擇是一致的,這樣獲得的狀態信息也應該是一致的,不一致的狀態信息,說明不同的節點對master節點的選擇出現了異常——也就是所謂的腦裂問題。這樣的腦裂狀態直接讓節點失去了集群的正確狀態,導致集群不能正常工作。
ES集群腦裂可能導致的原因:
1. 網絡: 由於是內網通信, 網絡通信問題造成某些節點認為 master 死掉, 而另選 master的可能性較小; 進而檢查 Ganglia 集群監控, 也沒有發現異常的內網流量, 故此原因可以排除。
內網一般不會出現es集群的腦裂問題,可以監控內網流量狀態。外網的網絡出現問題的可能性大些。
2. 節點負載: 由於 master 節點與 data 節點都是混合在一起的, 所以當工作節點的負載較大( 確實也較大) 時, 導致對應的 ES 實例停止響應, 而這台服務器如果正充當着 master節點的身份, 那么一部分節點就會認為這個 master 節點失效了, 故重新選舉新的節點, 這時就出現了腦裂; 同時由於 data 節點上 ES 進程占用的內存較大, 較大規模的內存回收操作也能造成 ES 進程失去響應。 所以, 這個原因的可能性應該是最大的。
3、回收內存
由於data節點上es進程占用的內存較大,較大規模的內存回收操作也能造成es進程失去響應。
ES集群腦裂應對問題的辦法:
1、對應於上面的分析, 推測出原因應該是由於節點負載導致了 master 進程停止響應, 繼而導致了部分節點對於 master 的選擇出現了分歧。 為此, 一個直觀的解決方案便是將 master節點與 data 節點分離。 為此, 我們添加了三台服務器進入 ES 集群, 不過它們的角色只是master 節點, 不擔任存儲和搜索的角色, 故它們是相對輕量級的進程。 可以通過以下配置來限制其角色:
node.master: true
node.data: false
當然, 其它的節點就不能再擔任 master 了, 把上面的配置反過來即可。 這樣就做到了將 master 節點與 data 節點分離。當然,為了使新加入的節點快速確定master位置,可以將data節點的默認的master發現方式有multicast修改為unicast:
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping.unicast.hosts: ["master1", "master2", "master3"]
還有兩個直觀的參數可以減緩腦裂問題的出現:
2、discovery.zen.ping_timeout( 默認值是 3 秒) : 默認情況下, 一個節點會認為, 如果 master節點在 3 秒之內沒有應答, 那么這個節點就是死掉了, 而增加這個值, 會增加節點等待響應的時間, 從一定程度上會減少誤判。
3、discovery.zen.minimum_master_nodes( 默認是 1) : 這個參數控制的是, 一個節點需要看到的具有 master 節點資格的最小數量, 然后才能在集群中做操作。 官方的推薦值是(N/2)+1, 其中 N 是具有 master 資格的節點的數量( 我們的情況是 3, 因此這個參數設置為2, 但對於只有 2 個節點的情況, 設置為 2 就有些問題了, 一個節點 DOWN 掉后, 你肯定連不上 2 台服務器了, 這點需要注意) 。
以上的解決方法只能是減緩這種現象的發生, 並沒有從根本上杜絕。
如果發生了腦裂, 如何解決?
當腦裂發生后, 唯一的修復辦法是解決這個問題並重啟集群。 這兒有點復雜和可怕。 當elasticsearch 集群啟動時, 會選出一個主節點( 一般是啟動的第一個節點被選為主) 。 由於索引的兩份拷貝已經不一樣了, elasticsearch 會認為選出來的主保留的分片是“主拷貝”並將這份拷貝推送給集群中的其他節點。 這很嚴重。 讓我們設想下你是用的是 node 客戶端並且一個節點保留了索引中的正確數據。 但如果是另外的一個節點先啟動並被選為主, 它會將一份過期的索引數據推送給另一個節點, 覆蓋它, 導致丟失了有效數據。
總結
所以怎么從腦裂中恢復?
第一個建議是給所有數據重新索引。
第二, 如果腦裂發生了, 要十分小心的重啟你的集群。 停掉所有節點並決定哪一個節點第一個啟動。 如果需要, 單獨啟動每個節點並分析它保存的數據。 如果不是有效的, 關掉它, 並刪除它數據目錄的內容( 刪前先做個備份) 。 如果你找到了你想要保存數據的節點, 啟動它並且檢查日志確保它被選為主節點。 這之后你可以安全的啟動你集群里的其他節點了。
詳細分析es節點的幾種角色