原諒連接: https://www.elastic.co/blog/r...html
"嗶...嗶...譁",PagerDuty的報警通知又來了. 多是由於你又遭遇了節點宕機, 或者服務器機架不可用, 或者整個ElasticSearch集羣重啓了. 無論哪一種狀況, 當前集羣的狀態都成爲了RED
: 由於當前有些分片不可被指派(到某個節點), 從而致使部分數據不可用.node
這種狀況總會不期而至, 而你該怎麼辦!?服務器
在ElasticSearch的早期版本中, 一般須要具備諸如爆破專家般的分析能力的人才能找到問題根源: 分片爲什麼不可用!?. 你須要經過cluster state API
, cat-shards API
, cat-allocation API
, cat-indices API
, indices-recovery API
, indices-shard-stores API
等一系列API來判斷集羣狀態並分析當前可能遇到的問題根源.架構
好在如今的狀況大有改善, 只須要一個cluster-allocation-explain API, 你就能輕鬆分析當前的分片分配狀況.負載均衡
cluster-allocation-explain API
在ElasticSearch 5.0中初次引入,並在5.2版本中進行了重構. 這個API主要是爲了方便解決下面兩個問題:elasticsearch
對於不能指派(unassigned)的分片: 解釋這些分片不能被指派(到某個節點)的緣由.ide
對於已指派的分片: 解決這些分片指派到特定節點的理由.工具
須要注意的是, 分片分配的問題不該該在集羣中常常發生, 一般是節點或集羣配置問題所致(例如, 設置了錯誤的分片分配過濾參數), 或者集羣中的節點都保存了分片的副本卻互相鏈接不到, 又或者磁盤問題等等諸如此類. 當問題出現時, 集羣管理員須要使用恰當的工具來定位問題, 並把集羣恢復到健康狀態, 而這正是cluster allocation explain API
將要帶給咱們的.性能
本文目標就是經過幾個具體的示例給你們講述如何使用explain API來定位分片分配相關的問題.ui
分片分配就是把一個分片指派到集羣中某個節點的過程. 爲了能處理大規模的文檔數據,提供高可用的集羣能力, ElasticSearch把索引中的文檔拆分紅分片, 並把分片分配到集羣中的不一樣節點.
當主分片(primary shard)分配失敗時, 將會致使索引的數據丟失以及不能爲該索引寫入新的數據.
當副本分片(replica shard)分配失敗時, 若是相應的主分片完全壞掉(例如磁盤故障)時, 集羣將面臨數據丟失的困境.
當分片分配到較慢的節點上時, 數據傳輸量大的索引將由於這些較慢分片而遭受影響, 從而致使集羣的性能下降.
所以, 分配分片並指派到最優的節點無疑是ElasticSearch內部一項重要的基礎功能.
對於新建索引和已有索引, 分片分配過程也不盡相同. 不過無論哪一種場景, ElasticSearch都經過兩個基礎組件完成工做: allocators
和deciders
. Allocators嘗試尋找最優的節點來分配分片, deciders則負責判斷並決定是否要進行此次分配.
對於新建索引, allocators負責找出擁有分片數最少的節點列表, 並按分片數量增序排序, 所以分片較少的節點會被優先選擇. 因此對於新建索引, allocators的目標就是以更爲均衡的方式爲把新索引的分片分配到集羣的節點中. 而後deciders依次遍歷allocators給出的節點, 並判斷是否把分片分配到該節點. 例如, 若是分配過濾規則中禁止節點A持有索引idx中的任一分片, 那麼過濾器也阻止把索引idx分配到節點A中, 即使A節點是allocators從集羣負載均衡角度選出的最優節點. 須要注意的是allocators只關心每一個節點上的分片數, 而無論每一個分片的具體大小. 這剛好是deciders工做的一部分, 即阻止把分片分配到將超出節點磁盤容量閾值的節點上.
對於已有索引, 則要區分主分片仍是副本分片. 對於主分片, allocators只容許把主分片指定在已經擁有該分片完整數據的節點上. 若是allocators不這樣作, 並把主分片分配到那些沒有最新數據的節點上, 則集羣將不得不面臨數據丟失的困境. 而對於副本分片, allocators則是先判斷其餘節點上是否已有該分片的數據的拷貝(即使數據不是最新的). 若是有這樣的節點, allocators就優先把把分片分配到這其中一個節點. 由於副本分片一旦分配, 就須要從主分片中進行數據同步, 因此當一個節點只擁分片中的部分時, 也就意思着那些未擁有的數據必須從主節點中複製獲得. 這樣能夠明顯的提升副本分片的數據恢復過程.
出現不可指派的主分片大概是ElasticSearch中最糟糕的事情之一. 若是未指派的分片出如今新建立的索引, 則將不能向該分片索引數據; 若是出如今已有索引中, 則不但不能索引數據, 而且以前已索引的數據也將不可被搜索.
咱們先在一個擁有兩個節點A和B的集羣中建立一個名爲test_idx
的索引, 爲該索引只設定1個分片且不設置副本分片. 但在建立索引的時候, 爲其設置了分配過濾規則, 即該索引不能出如今節點A和B上. 索引建立命名以下:
PUT /test_idx?wait_for_active_shards=0 { "settings": { "number_of_shards": 1, "number_of_replicas": 0, "index.routing.allocation.exclude._name": "A,B" } }
雖然索引能建立成功, 但由於過濾規則的限制, 該索引中任何分片都不能分配到所在集羣的僅有的兩個節點A和B上. 這個例子是咱們人爲設置的, 聽起來真實場景中也許不可能發生. 但確實會存在由於分配過濾相關設置的錯誤配置而致使分片沒法指派.
此時, 集羣將處於RED狀態. 這時候咱們就能夠經過explain API
來得到第一個未指派的分片的一些狀況(上面例子中, 集羣中只有一個分片未進行指派).
GET /_cluster/allocation/explain
輸出信息以下:
{ "index" : "test_idx", "shard" : 0, "primary" : true, "current_state" : "unassigned", "unassigned_info" : { "reason" : "INDEX_CREATED", "at" : "2017-01-16T18:12:39.401Z", "last_allocation_status" : "no" }, "can_allocate" : "no", "allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes", "node_allocation_decisions" : [ { "node_id" : "tn3qdPdnQWuumLxVVjJJYQ", "node_name" : "A", "transport_address" : "127.0.0.1:9300", "node_decision" : "no", "weight_ranking" : 1, "deciders" : [ { "decider" : "filter", "decision" : "NO", "explanation" : "node matches index setting [index.routing.allocation.exclude.] filters [_name:\"A OR B\"]" } ] }, { "node_id" : "qNgMCvaCSPi3th0mTcyvKQ", "node_name" : "B", "transport_address" : "127.0.0.1:9301", "node_decision" : "no", "weight_ranking" : 2, "deciders" : [ { "decider" : "filter", "decision" : "NO", "explanation" : "node matches index setting [index.routing.allocation.exclude.] filters [_name:\"A OR B\"]" } ] } ] }
explain API
對索引test_idx
中的第一個主分片0進行了解釋: 由於索引剛剛建立(unassigned_info
所示), 因此還處於未指派狀態(current_state
所示). 但又由於沒有節點被容許分配給該分片(allocate_explanation
所示), 因此分片處於不可分配狀態(can_allocate
所示). 繼續看每一個節點的決策信息(node_allocation_decisions
), 能夠看到由於建立索引時過濾了節點A和節點B, 因此filter decider
(decider
所示)給A發出的決定是不容許在A上分配分片('node_decision'所示, decider
的explanation
也對此作了說明). 在解釋中也包含了改變當前狀態須要調整的配置參數.
經過下面的_settings API
來更新分配過濾配置:
PUT /test_idx/_settings { "index.routing.allocation.exclude._name": null }
而後再次執行explain API
將收到以下信息:
unable to find any unassigned shards to explain
也就是是當前已沒有未指派到節點的分片了, 由於索引test_idx
中惟一的一個分片已經成功分配過了. 若是隻對主分片執行explain API
, 以下(注意這裏是GET請求):
GET /_cluster/allocation/explain { "index": "test_idx", "shard": 0, "primary": true }
則將返回該分片被指派到的節點信息(對輸出信息作了縮減):
{ "index": "test_idx", "shard": 0, "primary": true, "current_state": "started", "current_node": { "id" : "tn3qdPdnQWuumLxVVjJJYQ", "name" : "A", "transport_address" : "127.0.0.1:9300", "weight_ranking" : 1 } }
能夠看出該分片已處於分配成功狀態(started
), 而且被指派到了節點A上.
好了, 讓咱們開始向索引test_idx
中寫入些數據, 而後主分片上就擁有了一些文檔. 這時候若是停掉節點A,那麼這個主分片也將隨之消失. 由於開始時設置不建立副本分片, 因此集羣狀態又會變成RED. 從新對主分片執行explain API
:
GET /_cluster/allocation/explain { "index": "test_idx", "shard": 0, "primary": true }
將返回以下信息:
{ "index" : "test_idx", "shard" : 0, "primary" : true, "current_state" : "unassigned", "unassigned_info" : { "reason" : "NODE_LEFT", "at" : "2017-01-16T17:24:21.157Z", "details" : "node_left[qU98BvbtQu2crqXF2ATFdA]", "last_allocation_status" : "no_valid_shard_copy" }, "can_allocate" : "no_valid_shard_copy", "allocate_explanation" : "cannot allocate because a previous copy of the primary shard existed but can no longer be found on the nodes in the cluster" }
輸出信息告訴咱們主分片當前處於未指派狀態(current_state
), 由於以前分配了該分片的節點已從集羣中離開(unassigned_info
). unassigned_info
告訴咱們當前不能分配分片的緣由是集羣中沒有該分片的可用備份數據(can_allocate
), allocate_explanation
給出了更詳細的信息.
explain API
告知咱們那個主分片已沒有任何可用的分片複製數據, 也就是說集羣中任一擁有該分片可用的複製信息的節點都不存在了. 當前惟一能作的事就是等待節點恢復並從新加入集羣. 在一些更極端場景, 這些節點被永久移除, 而此時只能接受數據丟失的事實, 並經過reroute commends來從新分配空的主分片.
回到上面的索引test_idx
, 並把其副本分片數增長到1:
PUT /test_idx/_settings { "number"_of_replicas": 1 }
而後對於test_idx
, 咱們就擁有了2個分片: 主分片shard 0和副本分片shard 0. 由於節點A上已經分配了主分片, 因此副本分片應該指派到節點B上, 以達到集羣的分配均衡. 如今對副本分片執行explain API
(這裏也是GET請求):
GET /_cluster/allocation/explain { "index": "test_idx", "shard": 0, "primary": false }
輸出結果以下:
{ "index" : "test_idx", "shard" : 0, "primary" : false, "current_state" : "started", "current_node" : { "id" : "qNgMCvaCSPi3th0mTcyvKQ", "name" : "B", "transport_address" : "127.0.0.1:9301", "weight_ranking" : 1 }, … }
結果顯示副本分片已經被分配到節點B上.
接下來, 咱們再在該索引上設置分片分配過濾, 不過此次咱們只阻止向節點B分配分片數據:
PUT /text_idx/_settings { "index.routing.allocation.exclude._name": "B" }
重啓節點B, 而後從新爲副本節點執行explain API
, 這時候的結果以下:
{ "index" : "test_idx", "shard" : 0, "primary" : false, "current_state" : "unassigned", "unassigned_info" : { "reason" : "NODE_LEFT", "at" : "2017-01-16T19:10:34.478Z", "details" : "node_left[qNgMCvaCSPi3th0mTcyvKQ]", "last_allocation_status" : "no_attempt" }, "can_allocate" : "no", "allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes", "node_allocation_decisions" : [ { "node_id" : "qNgMCvaCSPi3th0mTcyvKQ", "node_name" : "B", "transport_address" : "127.0.0.1:9301", "node_decision" : "no", "deciders" : [ { "decider" : "filter", "decision" : "NO", "explanation" : "node matches index setting [index.routing.allocation.exclude.] filters [_name:\"B\"]" } ] }, { "node_id" : "tn3qdPdnQWuumLxVVjJJYQ", "node_name" : "A", "transport_address" : "127.0.0.1:9300", "node_decision" : "no", "deciders" : [ { "decider" : "same_shard", "decision" : "NO", "explanation" : "the shard cannot be allocated to the same node on which a copy of the shard already exists [[test_idx][0], node[tn3qdPdnQWuumLxVVjJJYQ], [P], s[STARTED], a[id=JNODiTgYTrSp8N2s0Q7MrQ]]" } ] } ] }
結果顯示副本分片當前處於不可分配狀態(can_allocate
), 由於分配過濾規則設置了禁止把分片分配到節點B上(explanation
). 由於節點A上已經指派了主分片, 因此不容許再把該分片的其餘備份信息指派到A節點(explanation
)--由於在同一臺機器上分配兩份徹底相同的數據沒有什麼意義, 因此ElasticSearch拒絕這樣作.
若是分片能正常分配, 爲何還要關注它的explain
信息呢? 一般的理由也許是某個索引(主索引或副本索引)已經分配到一個節點, 而後你又經過分配過濾設置但願把該分片從當前節點移到另一個節點上(也許你正想嘗試hot-warm架構), 但出於一些其餘緣由, 這個分片依然駐留在當前節點上. 這也正是explain API
能幫助咱們清晰當前分片分配過程的重要場景.
下面咱們先清除掉索引test_idx
的分配過濾設置, 以容許主分片和副本分片均可以正常分配:
PUT /test_idx/_settings { "index.routing.allocation.exclude._name": null }
如今咱們從新設置過濾規則, 以使主分片從當前節點移出:
PUT /test_idx/_settings { "index.routing.allocation.exclude._name": "A" }
咱們指望的結果是該過濾規則使主分片從當前的節點A中移出到另外一個節點, 然而卻事與願違. 下面經過explain API
來分析其中的起因:
GET /_cluster/allocation/explain { "index": "test_idx", "shard": 0, "primary": true }
輸出結果以下:
{ "index" : "test_idx", "shard" : 0, "primary" : true, "current_state" : "started", "current_node" : { "id" : "tn3qdPdnQWuumLxVVjJJYQ", "name" : "A", "transport_address" : "127.0.0.1:9300" }, "can_remain_on_current_node" : "no", "can_remain_decisions" : [ { "decider" : "filter", "decision" : "NO", "explanation" : "node matches index setting [index.routing.allocation.exclude.] filters [_name:\"A\"]" } ], "can_move_to_other_node" : "no", "move_explanation" : "cannot move shard to another node, even though it is not allowed to remain on its current node", "node_allocation_decisions" : [ { "node_id" : "qNgMCvaCSPi3th0mTcyvKQ", "node_name" : "B", "transport_address" : "127.0.0.1:9301", "node_decision" : "no", "weight_ranking" : 1, "deciders" : [ { "decider" : "same_shard", "decision" : "NO", "explanation" : "the shard cannot be allocated to the same node on which a copy of the shard already exists [[test_idx][0], node[qNgMCvaCSPi3th0mTcyvKQ], [R], s[STARTED], a[id=dNgHLTKwRH-Dp-rIX4Hkqg]]" } ] } ] }
經過對結果的分析, 咱們看到主分片依然駐留在節點A(current_node
). 雖然集羣明確表示該分片已不該該再繼續滯留在當前節點(can_remain_on_current_node
), 理由是當前節點符合設置的分配過濾規則(can_remain_decisions
). 然而explain API
還表示該分片也不能被分配到另一個節點(can_move_to_other_node
), 由於集羣只有惟一一個另外的節點(節點B), 而且節點B上已經有了一份副本分片, 而同一份數據並不容許同時在一個節點上分配屢次, 因此主分片當前不能被移到B上, 從而也不能從節點A上移出(node_allocation_decisions
).
在這篇文章中, 咱們經過對三個不一樣的場景的介紹, 來幫忙ElasticSearch管理員經過explain API
來理解集羣中的分片分配過程. explain API
還有其餘的一些使用場景, 例如經過展現節點的權重以解釋分片爲什麼處於當前節點而未被均衡到其餘節點. explain API
是診斷生產環境集羣分片分配過程的一件利器, 即使在ElasticSearch的開發過程當中咱們已經獲得巨大的幫助並節省了不少時間, 同時咱們的不少客戶也經過explain API
在診斷集羣狀態過程當中受益不淺.