Elasticsearch 之所以變得如此廣泛流行,其中一個原因是,它可以很好地從只有幾個節(jié)點的小集群擴(kuò)展為擁有數(shù)百個節(jié)點的大集群。它的核心就是集群協(xié)調(diào)子系統(tǒng)。Elasticsearch 7 版本包含了一個新集群協(xié)調(diào)子系統(tǒng),與早期版本相比,它提供了很多優(yōu)點。本文將介紹在版本 7 中對這個子系統(tǒng)的改進(jìn),描述如何使用新的子系統(tǒng)、這些改變對版本 6 升級有哪些影響,以及這些改進(jìn)如何防止用戶無意中將數(shù)據(jù)置于危險之中,最后將淺析新子系統(tǒng)的工作原理。
什么是集群協(xié)調(diào)?
Elasticsearch 集群可以執(zhí)行需要多個節(jié)點協(xié)同工作的很多任務(wù)。例如,每個搜索必須路由到所有正確的分片,以確保結(jié)果的準(zhǔn)確性。在索引或刪除某些文檔時,必須更新每個副本。每個客戶端請求都必須從接收它的節(jié)點轉(zhuǎn)發(fā)到能處理它的節(jié)點。這些節(jié)點對集群概況分別有各自的認(rèn)識,并據(jù)此來執(zhí)行搜索、索引和其他協(xié)調(diào)活動。這個集群概況就是指集群狀態(tài)。集群狀態(tài)決定了每個索引的映射和設(shè)置、分配給每個節(jié)點的分片,以及同步的分片副本。在集群中保持該信息的一致性十分重要。最近推出的很多功能(包括基于序號的復(fù)制和跨集群復(fù)制)只有在群集狀態(tài)一致的情況下才能正常運行。
協(xié)調(diào)子系統(tǒng)的運行方式是選擇一個特定節(jié)點作為集群的主節(jié)點。這個選舉出的主節(jié)點需要確保其集群中的所有節(jié)點都能夠接收集群狀態(tài)的更新。這比開始聽起來要困難得多,因為類似 Elasticsearch 這樣的分布式系統(tǒng)必須為各種奇怪的情況做好準(zhǔn)備。例如,節(jié)點有時運行緩慢,有時垃圾回收會導(dǎo)致停頓,或者突然斷電。 網(wǎng)絡(luò)會出現(xiàn)分區(qū)、包丟失、高延遲時段,或者可能提交亂序消息。有時可能會同時出現(xiàn)多個這樣的問題,而且可能間歇性地發(fā)生。盡管如此,集群協(xié)調(diào)子系統(tǒng)必須能夠保證每個節(jié)點具有集群狀態(tài)的一致性視圖。
重要的是,Elasticsearch 必須有足夠的彈性來應(yīng)對各個節(jié)點的故障。通過只有在法定數(shù)量的節(jié)點接受集群狀態(tài)更新時,才將更新視為成功,Elasticsearch 實現(xiàn)了這種彈性。法定數(shù)量是從集群里符合主節(jié)點條件的節(jié)點中精心選出的一部分節(jié)點的數(shù)量。僅要求部分節(jié)點進(jìn)行響應(yīng)的優(yōu)勢在于,某些節(jié)點可能會失敗,但不會影響集群的可用性。在選擇法定數(shù)量節(jié)點時要十分小心,這樣集群就無法選舉出兩個獨立的主節(jié)點,從而避免兩個主節(jié)點作出不一致的決策而最終導(dǎo)致數(shù)據(jù)丟失。
通常,我們建議集群設(shè)置三個符合主節(jié)點條件的節(jié)點,這樣如果其中的一個節(jié)點失敗,其他兩個節(jié)點仍然可以安全地形成法定數(shù)量,并繼續(xù)提供服務(wù)。如果一個集群少于三個符合主節(jié)點條件的節(jié)點,那它將無法安全地容忍其中任何一個節(jié)點發(fā)生故障。相反,如果一個集群中符合主節(jié)點條件的節(jié)點遠(yuǎn)多于三個,則節(jié)點選舉和集群狀態(tài)更新可能需要更長的時間。
演進(jìn)還是根本性革新?
Elasticsearch 6.x 及之前的版本使用了一個稱為 Zen Discovery 的集群協(xié)調(diào)子系統(tǒng)。這個子系統(tǒng)經(jīng)過多年的進(jìn)化和完善,能夠成功地為大大小小的集群提供支持。不過,我們想做出一些改進(jìn),這需要對子系統(tǒng)的運行方式做出一些根本性的改變。
Zen Discovery 允許用戶通過使用 discovery.zen.minimum_master_nodes設(shè)置 來決定多少個符合主節(jié)點條件的節(jié)點可以形成法定數(shù)量。在每個節(jié)點上一定要正確配置此項設(shè)置,并在集群動態(tài)擴(kuò)展時也正確地更新它,這一點至關(guān)重要。系統(tǒng)無法檢測到用戶是否錯誤配置了此項設(shè)置,而且在實踐當(dāng)中,在添加或刪除節(jié)點后很容易忘記調(diào)整此項設(shè)置。因此,Zen Discovery 試圖通過在每次主節(jié)點選舉過程中等待幾秒來防止出現(xiàn)這種錯誤配置,并且對其他的超時機(jī)制通常也十分保守。這意味著,如果選舉的主節(jié)點失敗,在選擇替代節(jié)點之前,集群至少在幾秒鐘內(nèi)是不可用的。如果集群無法選舉出一個主節(jié)點,則有時會很難了解是什么原因。
在 Elasticsearch 7.0 中,我們重新設(shè)計并重建了集群協(xié)調(diào)子系統(tǒng):
移除了
minimum_master_nodes設(shè)置,讓 Elasticsearch 自己選擇可以形成法定數(shù)量的節(jié)點。典型的主節(jié)點選舉現(xiàn)在只需很短時間就能完成。
集群的擴(kuò)充和縮減變得更加安全和簡單,并且大幅降低了因系統(tǒng)配置不當(dāng)而可能造成數(shù)據(jù)丟失的風(fēng)險。
節(jié)點狀態(tài)記錄比以往清晰很多,有助于診斷它們不能加入集群的原因,或者為何不能選舉出主節(jié)點。
隨著節(jié)點的添加或刪除,Elasticsearch 會自動更新集群的選舉配置,以維護(hù)最佳的容錯級別。選舉配置是一組符合主節(jié)點條件的節(jié)點,在做出決策時,它們具有投票權(quán)。通常,選舉配置包含集群中所有符合主節(jié)點條件的節(jié)點。法定數(shù)量就是指選舉配置中的多數(shù)節(jié)點:所有集群狀態(tài)更新都需要選舉配置中超過半數(shù)的節(jié)點同意。由于選舉配置以及法定數(shù)量都由系統(tǒng)管理,因此,即使添加或刪除了節(jié)點,也可以避免任何可能導(dǎo)致數(shù)據(jù)丟失的錯誤配置。
從 7.0 開始,如果一個節(jié)點無法發(fā)現(xiàn)主節(jié)點,并且本身也無法贏得選舉,Elasticsearch 將會定期記錄警告消息,詳盡描述當(dāng)前的狀態(tài),以幫助診斷許多常見的問題。
另外,Zen Discovery 以前有一個非常罕見的故障模式(在 Elasticsearch 彈性狀態(tài)頁中描述為“Repeated network partitions can cause cluster state updates to be lost”(重復(fù)的網(wǎng)絡(luò)分區(qū)可能導(dǎo)致集群狀態(tài)更新丟失)),現(xiàn)在這個模式已不復(fù)存在。這個問題項現(xiàn)在已標(biāo)記為已解決。
如何使用?
如果您完全使用默認(rèn)配置啟動新安裝的 Elasticsearch 節(jié)點,它們會自動查找在同一主機(jī)中運行的其他節(jié)點,并在幾秒鐘內(nèi)形成一個集群。如果您在同一個主機(jī)中啟動更多節(jié)點,則默認(rèn)情況下它們也會發(fā)現(xiàn)并加入這個集群。因此,在 Elasticsearch 7.0 版本中啟動多節(jié)點開發(fā)集群與在之前版本一樣簡單。
這種全自動集群形成機(jī)制在單臺主機(jī)中可以很好地運行,但在生產(chǎn)或其他分布式環(huán)境中使用還顯得不夠強(qiáng)健。對于這類環(huán)境,該機(jī)制的風(fēng)險在于:各節(jié)點可能無法及時發(fā)現(xiàn)彼此,進(jìn)而可能形成兩個或多個獨立的集群。從版本 7.0 開始,如果您要啟動一個全新的集群,并在多臺主機(jī)上都有節(jié)點,則必須指定該集群在初次選舉中應(yīng)使用的一組符合主節(jié)點條件的節(jié)點作為選舉配置。這被稱為集群引導(dǎo),只在第一次形成集群時需要。已經(jīng)加入集群的節(jié)點會將選舉配置存儲在它們的數(shù)據(jù)文件夾中,并在重啟后重復(fù)使用這些配置,而將要加入現(xiàn)有集群的全新節(jié)點可以從集群選舉的主節(jié)點接收該信息。
您可以為 cluster.initial_master_nodes 設(shè)置指定一組符合主節(jié)點條件的初始節(jié)點的主機(jī)名稱或 IP 地址,以此來引導(dǎo)集群。您可以在命令行,或在一個或多個符合主節(jié)點條件的節(jié)點的 elasticsearch.yml 文件中提供此設(shè)置。此外,您還需要配置發(fā)現(xiàn)子系統(tǒng),以便節(jié)點知道如何發(fā)現(xiàn)彼此。
如果沒有設(shè)置 initial_master_nodes,則在啟動全新節(jié)點時會嘗試發(fā)現(xiàn)現(xiàn)有的集群。如果節(jié)點找不到可以加入的集群,則會定期記錄一條警告消息,指明
master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster,and [cluster.initial_master_nodes] is empty on this node
在向集群添加新的符合主節(jié)點條件的節(jié)點時,不再需要任何特殊的儀式。只需將新節(jié)點配置為發(fā)現(xiàn)現(xiàn)有集群,再啟動它們即可。在有新節(jié)點加入時,集群會安全地自動調(diào)整其選舉配置。只要不同時停止一半或更多符合主節(jié)點條件的節(jié)點,即使移除節(jié)點也是安全的。如果您需要停止一半或更多符合主節(jié)點條件的節(jié)點,或者您有更復(fù)雜的擴(kuò)展或編排需求,可以使用更有針對性的擴(kuò)展程序,使用一個 API 來直接調(diào)整選舉配置。
如何升級?
您可以通過滾動升級或完全重啟集群,將 Elasticsearch 集群從版本 6 升級到版本 7。我們建議使用滾動升級,因為這樣可以逐個升級節(jié)點,并同時保持集群在升級期間的可用性。在滾動升級到版本 7 之前,必須將版本 6 的集群升級到版本 6.7。您可以通過完全重啟集群,從任何 6.x 版本升級到版本 7,但是這需要關(guān)閉整個集群,然后再重新啟動。不論哪種方式,在版本 6 和版本 7 之間對 Elasticsearch 進(jìn)行的更改都遠(yuǎn)比此處描述的對集群協(xié)調(diào)的改進(jìn)多得多。為了確保順利升級,請始終仔細(xì)遵循詳細(xì)的升級說明。
如果執(zhí)行滾動升級,則集群會基于集群中的節(jié)點數(shù)量和任何現(xiàn)有的 minimum_master_nodes 設(shè)置自動進(jìn)行引導(dǎo)。這意味著在開始升級之前務(wù)必要確保設(shè)置正確。這里不需要設(shè)置initial_master_nodes,因為在執(zhí)行滾動升級時,集群引導(dǎo)會自動發(fā)生。在選舉主節(jié)點時,版本 7 符合主節(jié)點條件的節(jié)點會優(yōu)先投票給版本 6.7 的節(jié)點,因此,在升級過程中,版本 6.7 的節(jié)點將更有可能選舉為主節(jié)點,直到升級完所有符合主節(jié)點條件的節(jié)點。
如果您執(zhí)行完全重啟集群的升級方式,則必須按照上述的方式引導(dǎo)升級后的集群:在啟動新升級的集群之前,必須先將initial_master_nodes 設(shè)置為符合主節(jié)點條件的節(jié)點的主機(jī)名稱或 IP 地址。
在版本 6 以及更早的版本中,還有一些其他設(shè)置,允許您在 discovery.zen.* 命名空間中配置 Zen Discovery 的行為。其中有些設(shè)置不再有效,且已被刪除。其他的一些設(shè)置已被重新命名。如果某個設(shè)置被重命名,則它的舊名稱就會在版本 7 中棄用。您需要調(diào)整配置來使用新名稱:
| 舊名稱 | 新名稱 |
|---|---|
| discovery.zen.ping.unicast.hosts | discovery.seed_hosts |
| discovery.zen.hosts_provider | discovery.seed_providers |
| discovery.zen.no_master_block | cluster.no_master_block |
新的集群協(xié)調(diào)子系統(tǒng)包括一個新的故障檢測機(jī)制。這意味著 discovery.zen.fd.* 命名空間中的 Zen Discovery 故障檢測設(shè)置不再有效。大多數(shù)用戶應(yīng)在版本 7 以及更高版本中使用默認(rèn)的故障檢測配置,但是,如果需要進(jìn)行任何更改,可以使用 cluster.fault_detection.* 設(shè)置來完成。
安全第一
版本 7.0 之前的 Elasticsearch 有時會允許您無意中執(zhí)行一系列步驟,這有可能會導(dǎo)致集群狀態(tài)出現(xiàn)不一致。而在版本 7.0 以及更高版本中,將會讓您完全知道可能在執(zhí)行一些不安全的操作,并要求您確認(rèn)是否真地要繼續(xù)操作。
例如,如果有一半或更多符合主節(jié)點條件的節(jié)點永久丟失,Elasticsearch 7.0 集群將無法自動恢復(fù)。通常,在一個集群中有三個符合主節(jié)點條件的節(jié)點,以便 Elasticsearch 可以在不中斷的情況下容忍其中一個節(jié)點丟失。如果其中有兩個節(jié)點永久丟失,則剩下的節(jié)點就無法安全地繼續(xù)工作。
版本 7.0 之前的 Elasticsearch 會靜靜地等待集群從這種情況中恢復(fù)。用戶可以通過啟動新的、空的、符合主節(jié)點條件的節(jié)點來替換任意數(shù)量的丟失節(jié)點,從而使集群重新上線。從永久丟失的一半或更多符合主節(jié)點條件的節(jié)點進(jìn)行自動恢復(fù)是不安全的,因為剩余的節(jié)點都無法保證擁有最新集群狀態(tài)的副本。這就可能會導(dǎo)致數(shù)據(jù)丟失。例如,一個分片副本可能已從同步集中移除。如果其他節(jié)點不知道這一點,則這個過時的分片副本就可能被分配給一個主節(jié)點。最危險的部分就是,用戶完全沒有意識到這一系列步驟已經(jīng)將他們的集群置于危險之中。用戶可能需要幾周或幾個月之后才會注意到其中的不一致。
在 Elasticsearch 7.0 以及更高版本中,這種不安全活動受到了更多限制。集群寧愿保持不可用狀態(tài),也不會冒這種風(fēng)險。在沒有備份的極少數(shù)情況下,如果絕對有必要,仍然可以執(zhí)行這種不安全的操作。它會采取幾個額外的步驟,確認(rèn)您知道風(fēng)險的存在,并避免意外執(zhí)行不安全操作。
如果已經(jīng)丟失了一半或更多符合主節(jié)點條件的節(jié)點,則首先要做的就是將這些丟失的節(jié)點重新上線。如果節(jié)點的數(shù)據(jù)目錄仍然完好,則最好的辦法就是使用這些數(shù)據(jù)目錄啟動新節(jié)點。如果這種方法可行,則可安全地使用最新的集群狀態(tài)重新形成集群。
下一個要嘗試的方法就是從最近的快照中恢復(fù)集群。這可使集群進(jìn)入一種已知的健康狀態(tài),但會丟失自獲取快照以來寫入的數(shù)據(jù)。然后,您可以重新索引任何丟失的數(shù)據(jù),因為您知道丟失的時間段。由于快照是增量的,因此您可以非常頻繁地執(zhí)行這些操作。通常可以每 30 分鐘獲取一次快照,以將這種恢復(fù)過程中丟失的數(shù)據(jù)量限制在一定范圍。
如果這些恢復(fù)操作都不可行,則最后的手段就是采用 elasticsearch-node 不安全的恢復(fù)工具。這是一個命令行工具,系統(tǒng)管理員可用它來執(zhí)行一些不安全的操作(如從少數(shù)節(jié)點中選舉一個過時的主節(jié)點)。通過將可能破壞一致性狀態(tài)的步驟明晰化,Elasticsearch 7.0 消除了通過一系列不安全的操作無意中造成數(shù)據(jù)丟失的風(fēng)險。
工作原理?
如果您熟悉分布式系統(tǒng)理論,則可能會將集群協(xié)調(diào)看作是可使用分布式共識來解決的一個問題示例。 在 Elasticsearch 的開發(fā)之初,分布式共識并未得到廣泛理解,不過最近幾年,這項技術(shù)已經(jīng)有了長足的發(fā)展。
Zen Discovery 采用了很多分布式共識算法中的想法,但只是有機(jī)地采用,并沒有嚴(yán)格按照理論所規(guī)定的那個模型。而且,它還有一個非常保守的超時機(jī)制,使得它有時在出現(xiàn)故障后恢復(fù)非常慢。7.0 中引入的新集群協(xié)調(diào)子系統(tǒng)使我們有機(jī)會更緊密地遵循這個理論模型。
分布式協(xié)調(diào)被認(rèn)為是一個難以解決好的問題。我們曾經(jīng)嚴(yán)重依賴形式化的方法來預(yù)先驗證我們的設(shè)計,自動化工具在正確性和安全性方面都提供強(qiáng)有力的保證。您可以在公共 Elasticsearch 形式化模型資料庫中找到 Elasticsearch 新集群協(xié)調(diào)算法的形式化規(guī)范。這個算法的核心安全模塊簡單明了。在 Elasticsearch 資料庫中,形式化模型與生產(chǎn)代碼之間存在直接的一一對應(yīng)關(guān)系。
如果您熟悉 Paxos、Raft、Zab 和 Viewstamped Replication (VR) 等系列分布式共識算法,那么也該熟悉核心安全模塊。該模塊模擬了一個可重寫寄存器,并使用了主節(jié)點的概念,這個概念與 Paxos 投票、Raft 條款和 VR 的視圖特別類似。該核心安全模塊及其形式化模型還包括集群引導(dǎo)、跨節(jié)點重啟的持續(xù)性和動態(tài)重配置。所有這些功能對于確保系統(tǒng)能夠在所有環(huán)境中都正常運行十分重要。
圍繞這一理論強(qiáng)健的核心,我們構(gòu)建了一個活動層,用來確保無論集群發(fā)生什么故障,一旦網(wǎng)絡(luò)恢復(fù)并且有足夠的節(jié)點在線,就會選舉出一個主節(jié)點,并且該主節(jié)點可以發(fā)布集群狀態(tài)更新。這個活動層使用了大量的前沿技術(shù)來避免許多常見問題。選舉調(diào)度程序具有適應(yīng)性,可以根據(jù)網(wǎng)絡(luò)狀況改變自身行為,以避免出現(xiàn)過多的爭議選舉。Raft 形式的預(yù)投票階段可以在選舉開始之前就抑制無法取勝的選舉,從而避免惡意節(jié)點的干擾。延遲檢測可以防止節(jié)點在遠(yuǎn)遠(yuǎn)落后于主節(jié)點時破壞集群。主動雙向故障檢測可以確保集群中的節(jié)點始終能夠互相通信。大部分集群狀態(tài)更新都能夠以小的 diffs 高效發(fā)布出去,從而避免在節(jié)點之間復(fù)制整個集群狀態(tài)。被優(yōu)雅終止的領(lǐng)導(dǎo)節(jié)點將明確讓位給它們選擇的繼承者,通過避免全面選舉,減少在有計劃的故障轉(zhuǎn)移期間的中斷時間。我們開發(fā)了測試基礎(chǔ)架構(gòu)來有效模擬可能會持續(xù)幾秒、幾分鐘或幾小時的非正常中斷所帶來的影響,從而驗證一旦中斷得到解決后集群是否可以始終快速地恢復(fù)。
為什么不選擇 Raft?
我們經(jīng)常被問到的一個問題是,為什么不簡單地“插入”像 Raft 一樣的標(biāo)準(zhǔn)分布式共識算法。有很多公認(rèn)的算法,每種算法都有不同的利弊權(quán)衡。我們仔細(xì)評估了所有可以找到的文獻(xiàn),并從中汲取靈感。在我們早期的概念驗證中,有一個概念便使用了非常接近 Raft 的協(xié)議。我們從這一經(jīng)驗中了解到,將其與 Elasticsearch 完全集成,需要做出非常巨大的改變。此外,許多標(biāo)準(zhǔn)算法還規(guī)定了一些對于 Elasticsearch 來說不是最佳選擇的設(shè)計決策。例如:
標(biāo)準(zhǔn)算法通常是圍繞操作日志構(gòu)建的,而 Elasticsearch 的集群協(xié)調(diào)更多的是直接基于集群狀態(tài)本身。相比基于操作可能達(dá)到的優(yōu)化來說,集群協(xié)調(diào)可以更簡單地對批處理(將相關(guān)操作合并到單個廣播中)之類的操作進(jìn)行重大優(yōu)化。
標(biāo)準(zhǔn)算法在集群伸縮能力方面都相當(dāng)受限,需要一系列步驟來完成許多維護(hù)任務(wù),而 Elasticsearch 的集群協(xié)調(diào)可以在一個步驟中安全地執(zhí)行任意的重配置。這通過避免不確定的中間狀態(tài)簡化了周圍的系統(tǒng)。
標(biāo)準(zhǔn)算法通常過于關(guān)注安全性,而忽略了如何保證活動性的細(xì)節(jié),也沒有描述如果發(fā)現(xiàn)某個節(jié)點不健康時,集群該如何反應(yīng)。Elasticsearch 的健康檢測機(jī)制非常復(fù)雜,已經(jīng)在該領(lǐng)域經(jīng)過了多年的使用和優(yōu)化,對于我們來說,維持其現(xiàn)在的運行狀況十分重要。事實上,與保證系統(tǒng)的活動性相比,實現(xiàn)系統(tǒng)的安全性所需付出的成本要少得多。大多數(shù)的實現(xiàn)工作都集中在系統(tǒng)的活動性上。
項目的目標(biāo)之一是,支持從運行 Zen Discovery 的 6.7 集群零中斷滾動升級到運行新協(xié)調(diào)子系統(tǒng)的版本 7 集群。將任何標(biāo)準(zhǔn)算法調(diào)整為允許這種滾動升級的算法似乎都不可行。
開發(fā)一個完整的、以工業(yè)級強(qiáng)度實現(xiàn)的分布式共識算法需要大量的投入,并且必須超越學(xué)術(shù)文獻(xiàn)所概述的范圍。在實踐中,定制是不可避免的,但是協(xié)調(diào)協(xié)議十分復(fù)雜,任何定制都存在引入錯誤的風(fēng)險。從工程角度來看,將這些定制視為開發(fā)新協(xié)議可能更有意義。
總結(jié)
Elasticsearch 7.0 提供了一個更快、更安全且更易于使用的新集群協(xié)調(diào)子系統(tǒng)。它支持從 6.7 零中斷滾動升級,并為彈性數(shù)據(jù)復(fù)制提供了基礎(chǔ)。要嘗試用新的集群協(xié)調(diào)子系統(tǒng),請下載最新的 7.0 公測版,參考文檔,運行一下,然后給我們提供反饋。