Elasticsearch作為一個(gè)分布式搜索引擎,自身是高可用的;但也架不住一些特殊情況的發(fā)生,如:
集群超過(guò)半數(shù)的master節(jié)點(diǎn)丟失,ES的節(jié)點(diǎn)無(wú)法形成一個(gè)集群,進(jìn)而導(dǎo)致集群不可用;
索引shard的文件損壞,分片無(wú)法被正常恢復(fù),進(jìn)而導(dǎo)致索引無(wú)法正常提供服務(wù)
本地盤節(jié)點(diǎn),多數(shù)據(jù)節(jié)點(diǎn)故障,舊節(jié)點(diǎn)無(wú)法再次加入集群,數(shù)據(jù)丟失
針對(duì)上述的情況,今天來(lái)聊一聊相關(guān)的解決方案。
在聊解決方案之前,首先來(lái)看一看ES集群層面的基本知識(shí),es的集群組成通常如圖1-1所示
圖 1-1 es常用集群架構(gòu)
如圖1-1所示,為生產(chǎn)環(huán)境es集群的經(jīng)典架構(gòu),主要由專有主節(jié)點(diǎn)、專有協(xié)調(diào)節(jié)點(diǎn)和數(shù)據(jù)節(jié)點(diǎn)組成:
如果沒(méi)有顯示設(shè)置節(jié)點(diǎn)角色,es的每個(gè)節(jié)點(diǎn)都會(huì)含有以上三種角色。除此之后還有Remote-eligible node 、ml-node和Transform nodes等角色需要顯示的配置,節(jié)點(diǎn)才會(huì)有該角色。
集群完全啟動(dòng)主要包含選舉主節(jié)點(diǎn)、元信息、主分片、數(shù)據(jù)恢復(fù)等重要階段;如圖2-1所示[1]。
圖 2-1 es集群?jiǎn)?dòng)流程
主節(jié)點(diǎn)選舉的過(guò)程,不是本文的重點(diǎn),而是集群元信息的選舉。被選舉出的master和集群元信息新舊程度沒(méi)有關(guān)系;master節(jié)點(diǎn)被選舉出來(lái)之后,它所要完成的第一個(gè)任務(wù),即是選舉集群元信息。
(1)Master選舉成功之后,判斷其持有的集群狀態(tài)中是否存在STATE_NOT_RECOVERED_BLOCK,如果不存在,則說(shuō)明元數(shù)據(jù)已經(jīng)恢復(fù),跳過(guò)gateway恢復(fù)過(guò)程,否則等待。org.elasticsearch.gateway.GatewayService#clusterChanged
//跳過(guò)元數(shù)據(jù)恢復(fù)if (state.blocks().hasGlobalBlock(STATE_NOT_RECOVERED_BLOCK) == false) { // already recovered return; } //此處省略部分代碼。 //進(jìn)入gateway恢復(fù)過(guò)程 performStateRecovery(enforceRecoverAfterTime, reason);
(2)Master從各個(gè)節(jié)點(diǎn)主動(dòng)獲取元數(shù)據(jù)信息。org.elasticsearch.gateway.Gateway#performStateRecovery
# 獲取元信息核心代碼 final String[] nodesIds = clusterService.state().nodes().getMasterNodes().keys().toArray(String.class); logger.trace("performing state recovery from {}", Arrays.toString(nodesIds)); final TransportNodesListGatewayMetaState.NodesGatewayMetaState nodesState = listGatewayMetaState.list(nodesIds, null).actionGet();
(3)從獲取的元數(shù)據(jù)信息中選擇版本號(hào)最大的作為最新元數(shù)據(jù);元信息包括集群級(jí)、索引級(jí)。
## org.elasticsearch.gateway.Gateway#performStateRecovery public void performStateRecovery(final GatewayStateRecoveredListener listener) throws GatewayException {# 省略若干行代碼## 進(jìn)入allocation階段;## final Gateway.GatewayStateRecoveredListener recoveryListener = new GatewayRecoveryListener();## listener為 GatewayStateRecoveredListener listener.onSuccess(builder.build()); }
(4)兩者確定之后,調(diào)用allocation模塊的reroute,對(duì)未分配 的分片執(zhí)行分配,主分片分配過(guò)程中會(huì)異步獲取各個(gè)shard級(jí)別元數(shù)據(jù)。
#主要實(shí)現(xiàn)方法為如下方法 #org.elasticsearch.gateway.GatewayService.GatewayRecoveryListener#onSuccess## 主要工作是構(gòu)建集群狀態(tài)(ClusterState),其中的內(nèi)容路由表 依賴allocation模塊協(xié)助完成,調(diào)用 allocationService.reroute 進(jìn) 入下一階段:異步執(zhí)行分片層元數(shù)據(jù)的恢復(fù),以及分片分配。updateTask線程結(jié)束.
ES中存儲(chǔ)的數(shù)據(jù):(1)state元數(shù)據(jù)信息;(2)index Lucene生成的索引文件;(3)translog事務(wù)日志。元數(shù)據(jù)信息:
上述信息被持久化到磁盤:持久化的state不包括某個(gè)分片存在于哪個(gè)節(jié)點(diǎn)這種內(nèi)容路由信息,集群完全重啟時(shí),依靠gateway的recovery過(guò)程重建RoutingTable和RoutingNode。當(dāng)讀取某個(gè)文檔時(shí), 根據(jù)路由算法確定目的分片后,再?gòu)腞outingTable中查找分片位于哪個(gè)節(jié)點(diǎn),然后將請(qǐng)求轉(zhuǎn)發(fā)到目的節(jié)點(diǎn)[1]。
?? 注意:在es7.0.0之后es的元信息存儲(chǔ)方式發(fā)生變化;es7.0.0之后元信息存儲(chǔ)改使用lucene的方式存儲(chǔ),見(jiàn)pr50928 Move metadata storage to Lucene)
7.10.2 專有主節(jié)點(diǎn),集群元數(shù)據(jù)
./|-- _state| |-- _39h.cfe| |-- _39h.cfs| |-- _39h.si| |-- node-0.st| |-- segments_50d| `-- write.lock`-- node.lock
6.8.13 專有主節(jié)點(diǎn),集群元數(shù)據(jù)
./|-- _state| |-- global-230.st| `-- node-2.st|-- indices| |-- -hiy4JnoRfqUJHTJoNUt4Q| | `-- _state| | `-- state-4.st| `-- ylJKVlqISGOi8EkpxHE_2A| `-- _state| `-- state-6.st`-- node.lock
?? 注意本文所述的master節(jié)點(diǎn)個(gè)數(shù),假設(shè)前提均為3個(gè)
master節(jié)點(diǎn)是控制整個(gè)集群;當(dāng)該種節(jié)點(diǎn)角色丟失過(guò)半,由于集群中投票節(jié)點(diǎn)永遠(yuǎn)不可能達(dá)到quorum無(wú)法選主,將無(wú)法維持es節(jié)點(diǎn)形成一個(gè)集群;雖然集群無(wú)法形成一個(gè)集群,但所仍幸master-eligible節(jié)點(diǎn)存活,我們可以使用如下手段進(jìn)行處理。
discovery.zen.minimum_master_nodes: 1discovery.zen.ping.unicast.hosts:- masters-0
在es7.0.0版本之后,由于es修改集群的啟動(dòng)配置,新增配置discovery.seed_hosts 和cluster.initial_master_nodes;es集群第一次啟動(dòng)時(shí)稱為bootstrap,該過(guò)程將配置文件中的cluster.initial_master_node作為初始的投票節(jié)點(diǎn)Voting configurations,投票節(jié)點(diǎn)具有選舉master和commit cluster state的權(quán)利,超過(guò)半數(shù)以上同意即投票成功。如果在集群健康的場(chǎng)景下,我們需要下線超過(guò)半數(shù)的master-eligible;則必須首先使用投票配置排除API從投票配置中排除受影響的節(jié)點(diǎn)。
POST _cluster/voting_config_exclusions?node_names={node_names}POST _cluster/voting_config_exclusions?node_ids={node_ids}DELETE _cluster/voting_config_exclusions
但是如果丟失的master節(jié)點(diǎn)超過(guò)半數(shù),則可以使用新的集群處理工具elasticsearch-node unsafe-bootstrap pr37696 和elasticsearch-node detach-cluster pr37979
面對(duì)丟失半數(shù)master-eligible,es7.0.0(包含)版本之后的處理步驟如下:1 使用bin/elasticsearch-node unsafe-bootstrap命令讓唯一主節(jié)點(diǎn)以不安全的方式改寫投票節(jié)點(diǎn),就像重新進(jìn)行bootstrap一樣,自己使用持久化的cluster state形成一個(gè)新集群2 其他數(shù)據(jù)節(jié)點(diǎn)無(wú)法加入新集群因?yàn)閁UID不同(es使用UUID作為節(jié)點(diǎn)和集群的唯一表示,每個(gè)節(jié)點(diǎn)都會(huì)持久化當(dāng)前集群的UUID),使用bin/elasticsearch-node detach-cluster命令讓節(jié)點(diǎn)離開(kāi)之前的集群3 啟動(dòng)數(shù)據(jù)節(jié)點(diǎn)和新的master-eligible節(jié)點(diǎn)(如下補(bǔ)充兩個(gè)新的master-eligible),他會(huì)加入新集群中
cluster.initial_master_nodes:- {master-0}- {new-master-1}- {new-master-2}discovery.seed_hosts:- {master-ip-0}- {new-master-ip-1}- {new-master-ip-2}
1 關(guān)閉 security 功能(如果開(kāi)啟了, 最好先關(guān)閉security插件功能):
1.1 因?yàn)樾聠?dòng)的master節(jié)點(diǎn), 沒(méi)有數(shù)據(jù)節(jié)點(diǎn)(如果只配置了一個(gè)master的角色), security插件的初始化無(wú)法完成, 各類接口不好調(diào)用
1.2 如果給新啟動(dòng)的master節(jié)點(diǎn), 配置了master and data角色, 則security插件會(huì)初始化成功. 會(huì)插入index, 但是這個(gè)index會(huì)和原來(lái)的data節(jié)點(diǎn)上保存的沖突. 不知道怎么解.elastic官方xpack-security;關(guān)閉鑒權(quán):xpack.security.enabled:false2 啟動(dòng)足夠的新master-eligible節(jié)點(diǎn)形成一個(gè)新集群.
discovery.zen.minimum_master_nodes: 2discovery.zen.ping.unicast.hosts:- {new-masters-1}- {new-masters-2}- {new-masters-3}
3 修改數(shù)據(jù)節(jié)點(diǎn)的為新master的地址,并且刪除掉節(jié)點(diǎn)上的_state(因?yàn)樾录旱腸luster UUID不一致),同上
4 啟動(dòng)數(shù)據(jù)節(jié)點(diǎn),數(shù)據(jù)被恢復(fù)加入到集群
已經(jīng)沒(méi)有cluster state了,唯一的希望是數(shù)據(jù)節(jié)點(diǎn)上的index數(shù)據(jù);恢復(fù)方式借助elasticsearch-node 工具
1 關(guān)閉security功能(如果開(kāi)啟了, 最好先關(guān)閉security插件功能),原因同上
2 啟動(dòng)足夠的新master-eligible節(jié)點(diǎn)形成一個(gè)新集群
cluster.initial_master_nodes:- {new-master-0}- {new-master-1}- {new-master-2}discovery.seed_hosts:- {new-master-ip-0}- {new-master-ip-1}- {new-master-ip-2}
3 bin/elasticsearch-node detach-cluster命令讓數(shù)據(jù)節(jié)點(diǎn)離開(kāi)之前的集群
./bin/elasticsearch-node detach-cluster------------------------------------------------------------------------ WARNING: Elasticsearch MUST be stopped before running this tool.------------------------------------------------------------------------You should only run this tool if you have permanently lost all of themaster-eligible nodes in this cluster and you cannot restore the clusterfrom a snapshot, or you have already unsafely bootstrapped a new clusterby running `elasticsearch-node unsafe-bootstrap` on a master-eligiblenode that belonged to the same cluster as this node. This tool can causearbitrary data loss and its use should be your last resort.Do you want to proceed?Confirm [y/N] yNode was successfully detached from the cluster
4 查詢dangling索引,GET /_dangling, 改api 引入es7.9版本于 pr581765 啟動(dòng)數(shù)據(jù)節(jié)點(diǎn)并使用Import dangling indexAPI將index數(shù)據(jù)import到cluster state中(官方推薦,es7.9版本之后). 或者 配置gateway.auto_import_dangling_indices: true引入于es7.6版本pr49174(es7.6.0-7.9.0可用該配置,在7.6版本之前不需要配置默認(rèn)加載dangling索引)并啟動(dòng)數(shù)據(jù)節(jié)點(diǎn)
POST /_dangling/{index-uuid}?accept_data_loss=true
6 導(dǎo)入完成之后,索引recovery之后即可進(jìn)行讀寫
注意
Q1: 為什么7.6.0之后需要配置,才能處理懸空索引(dangling index)才能讓數(shù)據(jù)加入新集群,7.6.0之后沒(méi)有懸空索引嗎?A1: 其實(shí)也是有的,只不過(guò)在es2版本將配置移除(對(duì)應(yīng)pr10016),默認(rèn)自動(dòng)加載dangling index(es2.0-es7.6); 具體實(shí)現(xiàn)于org.elasticsearch.gateway.DanglingIndicesState#processDanglingIndices es7.6再次引入dangling配置,es7.9引入dangling index rest api
Q2: 什么是 dangling 索引?A2: 當(dāng)一個(gè)節(jié)點(diǎn)加入集群時(shí),如果發(fā)現(xiàn)存儲(chǔ)在其本地?cái)?shù)據(jù)目錄中的任何分片(shard)不存在于集群中,將認(rèn)為這些分片屬于“懸空”索引。懸空索引產(chǎn)生的場(chǎng)景(1)在 Elasticsearch 節(jié)點(diǎn)離線時(shí)刪除了多個(gè)cluster.indices.tombstones.size 索引,節(jié)點(diǎn)再次加入集群集群 (2)master節(jié)點(diǎn)丟失,數(shù)據(jù)節(jié)點(diǎn)重新加入新的集群等
數(shù)據(jù)節(jié)點(diǎn)災(zāi)難故障之后,無(wú)法恢復(fù)加入集群;可將數(shù)據(jù)物理復(fù)制到新的節(jié)點(diǎn),然后按照master節(jié)點(diǎn)丟失的方式,將數(shù)據(jù)節(jié)點(diǎn)加入集群即可。
查看索引分片為什么無(wú)法分配,POST_cluster/allocation/explain
如果分片數(shù)據(jù)正常,那么我們可以嘗試重試分配分片任務(wù);POST _cluster/reroute?retry_failed
獲取索引的shard在那些節(jié)點(diǎn)上,使用_shard_stores api
GET indexName1/_shard_stores使用cluster reroute重新分配
# 嘗試分配副本 POST /_cluster/reroute{ "commands": [ { "allocate_replica": { "index": "{indexName1}", "shard": {shardId}, "node": "{nodes-9}" } } ]}
如果是主分片無(wú)法分配,可以嘗試如下命令進(jìn)行分配
POST /_cluster/reroute{ "commands": [ { "allocate_stale_primary": { "index": "{indexName1}", "shard": {shardId}, "node": {nodes-9}, "accept_data_loss": true } } ]}

如果主分片確實(shí)是無(wú)法分配,只能選擇丟失該分片的數(shù)據(jù),分配一個(gè)空的主分片
POST /_cluster/reroute{ "commands": [ { "allocate_empty_primary": { "index": "{indexName1}", "shard": {shardId}, "node": "{nodes-9}", "accept_data_loss": true } } ]}es5.0版本之前參考;https://www.elastic.co/guide/en/elasticsearch/reference/2.4/cluster-reroute.html
錯(cuò)誤參考Corrupted elastic index
shard-tool es6.5版本引入,該操作需要stop節(jié)點(diǎn)elasticsearch-shard 工具es6.5版本引入 pr33848elasticsearch-shard remove-corrupted-data 的 es7.0.0引入 pr32281
bin/elasticsearch-shard remove-corrupted-data --index {indexName} --shard-id {shardId}## 示列:修復(fù)索引twitter的0號(hào)分片bin/elasticsearch-shard remove-corrupted-data --index twitter --shard-id 0## 如果--index和--shard-id換成索引分片目錄參數(shù)--dir,則直接修復(fù)data和translogbin/elasticsearch-shard remove-corrupted-data --dir /var/lib/elasticsearchdata/nodes/0/indices/P45vf_YQRhqjfwLMUvSqDw/0
修復(fù)完成之后,啟動(dòng)節(jié)點(diǎn),如果分片不能夠自動(dòng)分配,使用reroute命令進(jìn)行shard分片
POST /_cluster/reroute{ "commands":[ { "allocate_stale_primary":{ "index":"index42", "shard":0, "node":"node-1", "accept_data_loss":false } } ]}

5版本之前可以通過(guò)索引級(jí)別配置,進(jìn)行修復(fù)index.shard.check_on_startup: fix ,該配置在es6.5版本移除 pr32279
修復(fù)translog操作,需要stop節(jié)點(diǎn)。
修復(fù)工具 elasticsearch-translog es5.0.0 引入pr19342elasticsearch-shard remove-corrupted-data translog的 es7.4.1開(kāi)始引入,pr47866elasticsearch-shard 可以直接清除translog,也可以像上文中指定--dir那樣進(jìn)行修復(fù)translog
bin/elasticsearch-shard remove-corrupted-data --index --shard-id --truncate-clean-translog## 示列:修復(fù)索引twitter的0號(hào)分片bin/elasticsearch-shard remove-corrupted-data --index twitter --shard-id 0 --truncate-clean-translog
清除完成之后使用cluster reroute 進(jìn)行恢復(fù)
5版本之前可以通過(guò)索引級(jí)別配置,進(jìn)行修復(fù)index.shard.check_on_startup: fix ,該配置在es6.5版本移除 pr32279
該種場(chǎng)景的文件損壞是最難修復(fù)的;官方還未提供工具,我們正在自己調(diào)研中
[1] elasticsearch集群?jiǎn)?dòng)流程
[2]https://www.elastic.co/guide/en/elasticsearch/reference/7.9/dangling-indices-list.html
[3]https://www.elastic.co/guide/en/elasticsearch/reference/7.10/node-tool.html
本文鏈接:http://m.www897cc.com/showinfo-26-81719-0.htmlElasticSearch集群災(zāi)難:別放棄,也許能再搶救一下
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com
上一篇: 探索Python-Patterns模塊:從設(shè)計(jì)模式到實(shí)際應(yīng)用,助力編程效率提升!