PB 级大规模 Elasticsearch 集群运维与调优实践
作者:bellen,騰訊云大數據研發工程師。
騰訊云 Elasticsearch 被廣泛應用于日志實時分析、結構化數據分析、全文檢索等場景中,本文將以情景植入的方式,向大家介紹與騰訊云客戶合作過程中遇到的各種典型問題,以及相應的解決思路與方法,希望與大家一同交流。
背景
某中型互聯網公司的游戲業務,使用了騰訊云的 Elasticsearch 產品,采用 ELK 架構存儲業務日志。
因為游戲業務本身的日志數據量非常大(寫入峰值在 100w qps),在服務客戶的幾個月中,踩了不少坑,經過數次優化與調整,把客戶的 ES 集群調整得比較穩定,避免了在業務高峰時客戶集群的讀寫異常,并且降低了客戶的資金成本和使用成本。
場景 1:與客戶的初次交鋒
解決方案架構師 A: bellen, XX 要上線一款新游戲,日志存儲決定用 ELK 架構,他們決定在 XX 云和我們之間二選一,我們首先去他們公司和他們交流一下,爭取拿下!
bellen: 好,隨時有空!
和架構師一起前往該公司,跟負責底層組件的運維部門的負責人進行溝通。
XX 公司運維老大:不要講你們的 PPT 了,先告訴我你們能給我們帶來什么!
bellen:呃,我們有很多優勢,比如可以靈活擴縮容集群,還可以一鍵平滑升級集群版本,并且提供有跨機房容災的集群從而實現高可用……
XX 公司運維老大:你說的這些別的廠商也有,我就問一個問題,我們現在要存儲一年的游戲日志,不能刪除數據,每天就按 10TB 的數據量算,一年也得有個 3PB 多的數據,這么大的數量,都放在 SSD 云盤上,成本太高了。你們有什么方案既能夠滿足我們存儲這么大數據量的需求,同時能夠降低我們的成本嗎?
bellen: 我們本身提供的有冷熱模式的集群,熱節點采用 SSD 云硬盤,冷節點采用 SATA 盤,采用 ES 自帶的 ILM 索引生命周期管理功能,定期把較老的索引從熱節點遷移到冷節點上,這樣從整體上可以降低成本。另外,也可以定期把更老的索引通過 snapshot 快照備份到 COS 對象存儲中,然后刪除索引,這樣成本就更低了。
XX 公司運維老大:存儲到 COS 就是冷存儲唄,我們需要查詢 COS 里的數據時,還得再把數據恢復到 ES 里?這樣不行,速度太慢了,業務等不了那么長時間,我們的數據不能刪除,只能放在 ES 里!你們能不能給我們提供一個 API, 讓老的索引數據雖然存儲在 COS 里,但是通過這個 API 依然可以查詢到數據,而不是先恢復到 ES, 再進行查詢?
bellen:呃,這個可以做,但是需要時間。是否可以采用 hadoop on COS 的架構,把存量的老的索引數據通過工具導入到 COS,通過 hive 去查詢,這樣成本會非常低,數據依然是隨時可查的。
XX 公司運維老大:那不行,我們只想用成熟的 ELK 架構來做,再增加 hadoop 那一套東西,我們沒那么多人力搞這個事!
bellen: 好吧,那可以先搞一個集群測試起來,看看性能怎么樣。關于存量數據放在 COS 里但是也需要查詢的問題,我們可以先制定方案,盡快實施起來。
XX 公司運維老大:行吧,我們現在按每天 10TB 數據量預估,先購買一個集群,能撐 3 個月的數據量就行,能給一個集群配置的建議嗎?
bellen: 目前支持單節點磁盤最大 6TB, cpu 和內存的話可以放到 8 核 32G 單節點,單節點跑 2w qps 寫入沒有問題,后面也可以進行縱向擴容和橫向擴容。
XX 公司運維老大:好,我們先測試一下。
場景 2:集群扛不住壓力了
N 天后,架構師 A 直接在微信群里反饋:bellen, 客戶反饋這邊的 ES 集群性能不行啊,使用 logstash 消費 kafka 中的日志數據,跑了快一天了數據還沒追平,這是線上的集群,麻煩緊急看一下吧。
我一看,一臉懵, 什么時候已經上線了啊,不是還在測試中嗎?
XX 公司運維 B: 我們購買了 8 核 32G*10 節點的集群,單節點磁盤 6TB, 索引設置的 10 分片 1 副本,現在使用 logstash 消費 kafka 中的數據,一直沒有追平,kafka 中還有很多數據積壓,感覺是 ES 的寫入性能有問題。
隨后我立即查看了集群的監控數據,發現 cpu 和 load 都很高,jvm 堆內存使用率平均都到了 90%,節點 jvm gc 非常頻繁了,部分節點因為響應緩慢,不停的離線又上線。
經過溝通,發現用戶的使用姿勢是 filebeat+kafka+logstash+elasticsearch, 當前已經在 kafka 中存儲了有 10 天的日志數據,啟動了 20 臺 logstash 進行消費,logstash 的 batch size 也調到了 5000,性能瓶頸是在 ES 這一側。客戶 8 核 32G*10 節點的集群,理論上跑 10w qps 沒有問題,但是 logstash 消費積壓的數據往 ES 寫入的 qps 遠不止 10w,所以是 ES 扛不住寫入壓力了,只能對 ES 集群進行擴容,為了加快存量數據的消費速度,先縱向擴容單節點的配置到 32 核 64GB,之后再橫向增加節點,以保證 ES 集群能夠最大支持 100w qps 的寫入(這里需要注意的是,增加節點后索引的分片數量也需要調整)。
所以一般新客戶接入使用 ES 時,必須要事先評估好節點配置和集群規模,可以從以下幾個方面進行評估:
存儲容量:要考慮索引副本數量、數據膨脹、ES 內部任務額外占用的磁盤空間(比如 segment merge)以及操作系統占用的磁盤空間等因素,如果再需要預留 50%的空閑磁盤空間,那么集群總的存儲容量大約為源數據量的 4 倍
計算資源:主要考慮寫入,2 核 8GB 的節點可以支持 5000qps 的寫入,隨著節點數量和節點規格的提升,寫入能力基本呈線性增長
索引和分片數量評估:一般一個 shard 的數據量在 30-50GB 為宜,可以以此確定索引的分片數量以及確定按天還是按月建索引。需要控制單節點總的分片數量,1GB 堆內存支持 20-30 個分片為宜;另外需要控制集群整體的分片數量,集群總體的分片數量一般不要超過 3w。
場景 3:logstash 消費 kafka 性能調優
上面遇到的問題是業務上線前沒有對集群配置和規模進行合理的評估,導致上線后 ES 集群扛不住了。通過合理的擴容處理,集群最終抗住了寫入壓力,但是新的問題又隨之出現了。
因為 kafka 積壓的數據比較多,客戶使用 logstash 消費 kafka 數據時,反饋有兩個問題:
增加多臺 logstash 消費 kafka 數據,消費速度沒有線性提升
kafka 的不同 topic 消費速度不均勻、topic 內不同 partition 消費的速度也不均勻
經過分析客戶 logstash 的配置文件,發現問題出現的原因主要是:
topic 的 partition 數量少,雖然 logstash 機器數量多,但是卻沒有充分利用機器資源并行消費數據,導致消費速度一直上不去
所有 logstash 的配置文件都相同,使用一個 group 同時消費所有的 topic,存在資源競爭的問題
分析后,對 kafka 和 logstash 進行了如下優化:
提高 kafka topic 的分區數量
對 logstash 進行分組;對于數據量較大的 topic,可以單獨設置一個消費組進行消費,有一組 logstash 單獨使用這個消費組對該 topic 進行消費;其它的數據量較小的 topic,可以共用一個消費組和一組 logstash
每組 logstash 中總的 consumer_threads 數量和消費組總的 partion 數量保持一致,比如有 3 個 logstash 進程,消費的 topic 的 partition 數量為 24, 那么每個 logstash 配置文件中的 consumer_threads 就設置為 8
通過上述優化,最終使得 logstash 機器資源都被充分利用上,很快消費完堆積的 kafka 數據,待消費速度追平生成速度后,logstash 消費 kafka 一直穩定運行,沒有出現積壓。
另外,客戶一開始使用的是 5.6.4 版本的 logstash,版本較老,使用過程中出現因為單個消息體過長導致 logstash 拋異常后直接退出的問題:
whose?size?is?larger?than?the?fetch?size?4194304?and?hence?cannot?be?ever?returned.?Increase?the?fetch?size?on?the?client?(using?max.partition.fetch.bytes),?or?decrease?the?maximum?message?size?the?broker?will?allow?(using?message.max.bytes)通過把 logstash 升級至高版本 6.8 避免了這個問題(6.x 版本的 logstash 修復了這個問題,避免了 crash)。
場景 4:磁盤要滿了,緊急擴容?
客戶的游戲上線有一個月了,原先預估每天最多有 10TB 的數據量,實際則是在運營活動期間每天產生 20TB 的數據,原先 6TB*60=360TB 總量的數據盤使用率也達到了 80%。針對這種情況,我們建議客戶使用冷熱分離的集群架構,在原先 60 個熱節點的基礎上,增加一批 warm 節點存儲冷數據,利用 ILM(索引生命周期管理)功能定期遷移熱節點上的索引到 warm 節點上。
通過增加 warm 節點的方式,客戶的集群磁盤總量達到了 780TB, 可以滿足最多三個月的存儲需求。但是客戶的需求還沒有滿足:
XX 公司運維老大:給我們一個能存放一年數據的方案吧,總是通過加節點擴容磁盤的方式不是長久之計,我們得天天盯著這個集群,運維成本很高!并且一直加節點,ES 會扛不住吧?
bellen: 可以嘗試使用我們新上線的支持本地盤的機型,熱節點最大支持 7.2TB 的本地 SSD 盤,warm 節點最大支持 48TB 的本地 SATA 盤。一方面熱節點的性能相比云盤提高了,另外 warm 節點可以支持更大的磁盤容量。單節點可以支持的磁盤容量增大了,節點數量就不用太多了,可以避免踩到因為節點數量太多而觸發的坑。
XX 公司運維老大:現在用的是云盤,能替換成本地盤嗎,怎么替換?
bellen: 不能直接替換,需要在集群中新加入帶本地盤的節點,把數據從老的云盤節點遷移到新的節點上,遷移完成后再剔除掉舊的節點,這樣可以保證服務不會中斷,讀寫都可以正常進行。
XX 公司運維老大:好,可以實施,盡快搞起來!
云盤切換為本地盤,是通過調用云服務后臺的 API 自動實施的。在實施之后,觸發了數據從舊節點遷移到新節點的流程,但是大約半個小時候,問題又出現了:
XX 公司運維小: bellen, 快看一下,ES 的寫入快掉 0 了。
通過查看集群監控,發現寫入 qps 直接由 50w 降到 1w,寫入拒絕率猛增,通過查看集群日志,發現是因為當前小時的索引沒有創建成功導致寫入失敗。
緊急情況下,執行了以下操作定位到了原因:
GET _cluster/health
發現集群健康狀態是 green,但是有大約 6500 個 relocating_shards, number_of_pending_tasks 數量達到了數萬。
GET _cat/pending_tasks?v
發現大量的"shard-started"任務在執行中,任務優先級是"URGENT", 以及大量的排在后面的"put mapping"的任務,任務優先級是"HIGH";"URGENT"優先級比"HIGH"優先級要高,因為大量的分片從舊的節點遷移到新的節點上,造成了索引創建的任務被阻塞,從而導致寫入數據失敗。
GET _cluster/settings
為什么會有這么多的分片在遷移中?通過 GET _cluster/settings 發現"cluster.routing.allocation.node_concurrent_recoveries"的值為 50,而目前有 130 個舊節點在把分片遷移到 130 個新節點中,所以有 130*50=6500 個遷移中的分片。而"cluster.routing.allocation.node_concurrent_recoveries"參數的值默認為 2,應該是之前在執行縱向擴容集群時,為了加快分片遷移速度人為修改了這個值(因為集群一開始節點數量沒有很多,索引同時遷移中的分片也不會太多,所以創建新索引不會被阻塞)。
PUT _cluster/settings
現在通過 PUT _cluster/settings 把"cluster.routing.allocation.node_concurrent_recoveries"參數修改為 2。但是因為"put settings"任務的優先級也是"HIGH", 低于"shard-started"任務的優先級,所以更新該參數的操作還是會被阻塞,ES 報錯執行任務超時。此時,進行了多次重試,最終成功把"cluster.routing.allocation.node_concurrent_recoveries"參數修改為了 2。
取消 exclude 配置
現在通過 GET _cluster/health 看到遷移中的分片數量在逐漸減少,為了不增加新的遷移任務,把執行數據遷移的 exclude 配置取消掉:
PUT?_cluster/settings {"transient":?{"cluster.routing.allocation.exclude._name":?""} }加速分片遷移
同時調大分片恢復時節點進行數據傳輸的每秒最大字節數(默認為 40MB),加速存量的分片遷移任務的執行:
PUT?_cluster/settings {"transient":?{"indices":?{"recovery":?{"max_bytes_per_sec":?"200mb"}}} }提前創建索引
現在看到遷移中的分片數量慢慢減少,新索引已經創建成功了,寫入恢復正常了。到下個整點時,發現新建索引還是比較慢,因為還有幾百個分片在遷移中,創建新索引大概耗時 5 分鐘,這 5 分鐘內寫入也是失敗的。
等幾百個遷移中的分片都執行完畢后,新建索引就比較快了,也不會再寫入失敗了。但是問題是當前正在執行云盤節點切換為本地盤的流程,需要把數據從舊的130個節點上遷移到新的130個節點上,數據遷移的任務不能停,那該怎么辦?既然新創建索引比較慢,那就只好提前把索引都創建好,避免了在每個整點數據寫入失敗的情況。通過編寫python腳本,每天執行一次,提前把第二天的每個小時的索引創建好,創建完成了再把"cluster.routing.allocation.exclude._name"更改為所有的舊節點,保證數據遷移任務能夠正常執行。
結果展示
總量 400TB 的數據,大約經過 10 天左右,終于完成遷移了。配合提前新建索引的 python 腳本,這 10 天內也沒有出現寫入失敗的情況。
經過了這次擴容操作,總結了如下經驗:
分片數量過多時,如果同時進行遷移的分片數量過多,會阻塞索引創建和其它配置更新操作,所以在進行數據遷移時,要保證"cluster.routing.allocation.node_concurrent_recoveries"參數和"cluster.routing.allocation.cluster_concurrent_rebalance"為較小的值。
如果必須要進行數據遷移,則可以提前創建好索引,避免 ES 自動創建索引時耗時較久,從而導致寫入失敗。
場景 5:10 萬個分片?
在穩定運行了一陣后,集群又出問題了。
XX 公司運維 B: bellen, 昨晚凌晨 1 點鐘之后,集群就沒有寫入了,現在 kafka 里有大量的數據堆積,麻煩盡快看一下?
通過 cerebro 查看集群,發現集群處于 yellow 狀態,然后發現集群有大量的錯誤日志:
{"message":"blocked?by:?[SERVICE_UNAVAILABLE/1/state?not?recovered?/?initialized];:?[cluster_block_exception]?blocked?by:?[SERVICE_UNAVAILABLE/1/state?not?recovered?/?initialized];","statusCode":503,"error":"Service?Unavailable"}然后再進一步查看集群日志,發現有"master not discovered yet..."之類的錯誤日志,檢查三個 master 節點,發現有兩個 master 掛掉,只剩一個了,集群無法選主。
登陸到掛了的 master 節點機器上,發現保活程序無法啟動 es 進程,第一直覺是 es 進程 oom 了;此時也發現 master 節點磁盤使用率 100%, 檢查了 JVM 堆內存快照文件目錄,發現有大量的快照文件,于是刪除了一部分文件,重啟 es 進程,進程正常啟動了;但是問題是堆內存使用率太高,gc 非常頻繁,master 節點響應非常慢,大量的創建索引的任務都超時,阻塞在任務隊列中,集群還是無法恢復正常。
看到集群 master 節點的配置是 16 核 32GB 內存,JVM 實際只分配了 16GB 內存,此時只好通過對 master 節點原地增加內存到 64GB(虛擬機,使用的騰訊云 CVM, 可以調整機器規格,需要重啟),master 節點機器重啟之后,修改了 es 目錄 jvm.options 文件,調整了堆內存大小,重新啟動了 es 進程。
3 個 master 節點都恢復正常了,但是分片還需要進行恢復,通過 GET _cluster/health 看到集群當前有超過 10w 個分片,而這些分片恢復還需要一段時間,通過調大"cluster.routing.allocation.node_concurrent_recoveries", 增大分片恢復的并發數量。實際上 5w 個主分片恢復得是比較快的了,但是副本分片的恢復就相對慢很多,因為部分副本分片需要從主分片上同步數據才能恢復。此時可以采取的方式是把部分舊的索引副本數量調為 0, 讓大量副本分片恢復的任務盡快結束,保證新索引能夠正常創建,從而使得集群能夠正常寫入。
總結這次故障的根本原因是:集群的索引和分片數量太多,集群元數據占用了大量的堆內存,而 master 節點本身的 JVM 內存只有 16GB(數據節點有 32GB), master 節點頻繁 full gc 導致 master 節點異常,從而最終導致整個集群異常。所以要解決這個問題,還是得從根本上解決集群的分片數量過多的問題。
目前日志索引是按照小時創建,60 分片 1 副本,每天有 24*60*2=2880 個分片,每個月就產生 86400 個分片,這么多的分片可能會帶來嚴重的問題。有以下幾種方式解決分片數量過多的問題:
可以在 ILM 的 warm phase 中開啟 shrink 功能,對老的索引從 60 分片 shrink 到 5 分片,分片數量可以降低 12 倍;
業務可以把每小時創建索引修改為每兩個小時或者更長,可以根據每個分片數量最多支持 50GB 的數據推算多長時間創建新索引合適;
對老的索引設置副本為 0,只保留主分片,分片數量能夠再下降近一倍,存儲量也下降近一倍;
定期關閉最老的索引,執行{index}/_close。
和客戶溝通過后,客戶表示可以接受方式 1 和方式 2,但是方式 3 和 4 不能接受,因為考慮到存在磁盤故障的可能性,必須保留一個副本來保證數據的可靠性;另外還必須保證所有數據都是隨時可查詢的,不能關閉。
場景 6:有點坑的 ILM
在上文中,雖然通過臨時給 master 節點增加內存,抗住了 10w 分片,但是不能從根本上解決問題。客戶的數據是計劃保留一年的,如果不進行優化,集群必然扛不住數十萬個分片。所以接下來需要著重解決集群整體分片數量過多的問題。前文也提到了,客戶可以接受開啟 shrink 以及降低索引創建粒度(經過調整后,每兩個小時創建一個索引),這在一定程度上減少了分片的數量,能夠使集群暫時穩定一陣。
輔助客戶在 kibana 上配置了如下的 ILM 策略:
在 warm phase, 把創建時間超過 360 小時的索引從 hot 節點遷移到 warm 節點上,保持索引的副本數量為 1。之所以使用 360 小時作為條件,而不是 15 天作為條件,是因為客戶的索引是按小時創建的,如果以 15 天作為遷移條件,則在每天凌晨都會同時觸發 15 天前的 24 個索引一共 24*120=2880 個分片同時開始遷移索引,容易引發前文介紹的由于遷移分片數量過多導致創建索引被阻塞的問題。所以以 360 小時作為條件,則在每個小時只會執行一個索引的遷移,這樣把 24 個索引的遷移任務打平,避免其它任務被阻塞的情況發生。
同時,也在 warm phase 階段,設置索引 shrink,把索引的分片數縮成 5 個,因為老的索引已經不執行寫入了,所以也可以執行 force merge, 強制把 segment 文件合并為 1 個,可以獲得更好的查詢性能。
另外,設置了 ILM 策略后,可以在索引模板里增加 index.lifecycle.name 配置,使得所有新創建的索引都可以和新添加的 ILM 策略關聯,從而使得 ILM 能夠正常運行。
客戶使用的 ES 版本是 6.8.2, 在運行 ILM 的過程中, 也發現一些問題:
新添加的策略只能對新創建的索引生效,存量的索引只能通過批量修改索引 settings 里的 index.lifecycle.name 執行策略;
如果一個策略進行了修改,那么所有存量的索引,不管是有沒有執行過該策略,都不會執行修改后的策略,也即修改后的策略只對修改成功后新創建的索引生效。比如一開始的策略沒有開啟 shrink, 現在修改策略內容添加了 shrink 操作,那么只有之后新創建的索引在達到策略觸發條件(比如索引已經創建超過 360 個小時)后才會執行 shrink, 而之前的所有索引都不會執行 shrink,此時若想對存量的索引也執行 shrink,只能夠通過腳本批量執行了;
在 warm phase 同時執行索引遷移和 shrink 會觸發 es 的 bug, 如上圖中的 ILM 策略,索引本身包含 60 分片 1 副本,初始時都在 hot 節點上,在創建完成 360 小時之后,會執行遷移,把索引都遷移到 warm 節點上,同時又需要把分片 shrink 到 5,在實際執行中,發現一段時間后有大量的 unassigned shards,分片無法分配的原因如下:
這是因為 shrink 操作需要新把索引完整的一份數據都遷移到一個節點上,然后在內存中構建新的分片元數據,把新的分片通過軟鏈接指向到幾個老的分片的數據,在 ILM 中執行 shrink 時,ILM 會對索引進行如下配置:
?{"index.routing"?:?{"allocation"?:?{"require"?:?{"temperature"?:?"warm","_id"?:?"LKsSwrDsSrSPRZa-EPBJPg"}}}}問題是索引包含副本,而主分片和副本分片又不能在同一個節點上,所以會出現部分分片無法分配的情況(不是全部,只有一部分)。這里應該是觸發了 6.8 版本的 ILM 的 bug,需要查看源碼才能定位解決這個 bug,目前還在研究中。當前的 workaround 是通過腳本定期掃描出現 unassigned shards 的索引,修改其 settings:
?{"index.routing"?:?{"allocation"?:?{"require"?:?{"temperature"?:?"warm","_id"?:?"LKsSwrDsSrSPRZa-EPBJPg"}}}}優先保證分片先從 hot 節點遷移到 warm 節點,這樣后續的 shrink 才能順利執行(也可能執行失敗,因為 60 個分片都在一個節點上,可能會觸發 rebalance, 導致分片遷移走,shrink 的前置條件又不滿足,導致執行失敗)。要完全規避這個問題,還得在 ILM 策略中設置,滿足創建時間超過 360 個小時的索引,副本直接調整為 0,但是客戶又不接受,沒辦法。
場景 7:自己實現 SLM
上文介紹了 10w 個分片會給集群帶來的影響和通過開啟 shrink 來降低分片數量,但是仍然有兩個需要重點解決的問題:
索引不斷新建,如何保證一年內,集群總的分片數量不高于 10w,穩定在一個較低的水位?
ILM 中執行 shrink 可能會導致部分分片未分配以及 shrink 執行失敗,怎么徹底解決呢?
可以估算一下,按小時建索引,60 分片 1 副本,一年的分片數為 24*120*365=1051200 個分片,執行 shrink 后分片數量 24*10*350 + 24*120*15 = 127200(15 天內的新索引為了保障寫入性能和數據可靠性,仍然保持 60 分片 1 副本,舊的索引 shrink 為 5 分片 1 副本), 仍然有超過 10w 個分片。結合集群一年總的存儲量和單個分片可以支持的數據量大小進行評估,我們期望集群總體的分片數量可以穩定為 6w~8w,怎么優化?
可以想到的方案是執行數據冷備份,把比較老的索引都冷備到其它的存儲介質上比如 HDFS、S3、騰訊云的 COS 對象存儲等。但是問題是這些冷備的數據如果也要查詢,需要先恢復到 ES 中才可查,恢復速度比較慢,客戶無法接受。由此也產生了新的想法,目前老的索引仍然是 1 副本,可以把老索引先進行冷備份,再把副本調為 0,這樣做有以下幾點好處:
集群整體分片數量能降低一半;
數據存儲量也能降低一半,集群可以存儲更多數據;
老的索引仍然隨時可查;
極端情況下,磁盤故障引起只有一個副本的索引數據無法恢復時,可以從冷備介質中進行恢復。
經過和客戶溝通,客戶接受了上述方案,計劃把老索引冷備到騰訊云的對象存儲 COS 中,實施步驟為:
所有存量的老索引,需要批量處理,盡快地備份到 COS 中,然后批量修改副本數量為 0;
最近新建的索引,采用按天備份的策略,結合 ILM, 修改策略,在 ILM 執行過程中修改索引副本數為 0(ILM 的 warm phase 和 cold phase 都支持設置副本數量)。
其中第一個步驟的實施可以通過腳本實現,本案例中就采用了騰訊云 SCF 云函數進行實施,方便快捷可監控。實施要點有:
按天創建 snapshot,批量備份每天產生的 24 個索引,如果是按月或者更大粒度創建快照,因數據量太大如果執行快照過程中出現中斷,則必須全部重來,耗時耗力;按小時創建快照也不適用,會造成快照數量太多,可能會踩到坑。
每創建一個快照,后續需要輪詢快照的狀態,保證前一個快照 state 為"SUCCESS"之后,再創建下一個快照;因為快照是按天創建的,快照名字可以為 snapshot-2020.06.01, 該快照只備份 6 月 1 號的所有索引。而在檢查到 snapshot-2020.06.01 快照執行成功后,然后新建下一個快照時,需要知道要對哪天的索引打快照,因此需要記錄當前正在執行哪一個快照。有兩種方式記錄,一是把當前正在執行的快照日期后綴"2020.06.01"寫入到文件中, 腳本通過定時任務輪詢時,每次都讀文件;另外一種方式是創建一個臨時的索引,把"2020.06.01"寫入到這個臨時索引的一個 doc 中,之后對該 doc 進行查詢或者更新。
創建快照時,可以把"include_global_state"置為 false, 不對集群的全局狀態信息進行備份。
在實施完第一個步驟之后,就可以批量把對索引進行過備份的索引副本數都調為 0, 這樣一次性釋放了很多磁盤空間,并且顯著降低了集群整體的分片數量。
接下來實施第二個步驟,需要每天執行一次快照,多創建時間較久的索引進行備份。實施比較簡單,可以通過 crontab 定時執行腳本或者使用騰訊云 SCF 執行。
之后,就可以修改 ILM 策略,開啟 cold phase, 修改索引副本數量為 0:
此處的 timing 是創建時間 20 天后,需要保證在第二步驟中,對過去老索引數據備份先執行完成后才可以進入到 cold phase.
通過老索引數據冷備并且降低索引副本,我們可以把集群整體的分片數量維持在一個較低的水位。但是還有另外一個問題待解決,也即 shrink 失敗的問題。剛好,我們可以利用對老索引數據冷備并且降低索引副本的方案,來徹底解決 shrink 失敗的問題。
在前文有提到,shrink 失敗歸根結底是因為索引的副本數量為 1, 現在我們可以把數據備份和降低副本提前,讓老索引進入到 ILM 的 warm phase 中時已經是 0 副本,之后再執行 shrink 操作就不會有問題了;同時,因為副本降低了,索引從 hot 節點遷移到 warm 節點遷移的數據量也減少了一半,從而降低了集群負載,一舉兩得。
因此,我們需要修改 ILM 策略,在 warm phase 就把索引的副本數量調整為 0, 然后去除 cold phase。
另外一個可選的優化項是:對老的索引進行凍結,凍結索引是指把索引常駐內存的一些數據從內存中清理掉(比如 FST, 元數據等), 從而降低內存使用量,而在查詢已經凍結的索引時,會重新構建出臨時的索引數據結構存放在內存中,查詢完畢再清理掉;需要注意的是,默認情況下是無法查詢已經凍結的索引的,需要在查詢時顯式的增加"ignore_throttled=false"參數。
經過上述優化,我們最終解決了集群整體分片數量過多和 shrink 失敗的問題。在實施過程中引入了額外的定時任務腳本實施自動化快照,實際上在 7.4 版本的 ES 中,已經有這個功能了,特性名稱為SLM(快照生命周期管理),并且可以結合 ILM 使用,在 ILM 中增加了"wait_for_snapshot"的 ACTION, 但是卻只能在 delete phase 中使用,不滿足我們的場景。
場景 8:客戶十分喜歡的 Searchable Snapshots!
在上述的場景中,我們花費大量的精力去解決問題和優化使用方式,保證 ES 集群能夠穩定運行,支持 PB 級別的存儲。溯本回原,如果我們能有一個方案使得客戶只需要把熱數據放在 SSD 盤上,然后冷數據存儲到 COS/S3 上,但同時又使冷數據能夠支持按需隨時可查,那我們前面碰到的所有問題都迎刃而解了。可以想象得到的好處有:
只需要更小規模的集群和非常廉價的 COS/S3 對象存儲就可以支持 PB 級別的數據量,客戶的資金成本非常低;
小規模的集群只需要能夠支撐熱索引的寫入和查詢即可,集群整體的分片數不會太多,從而避免了集群不穩定現象的發生。
而這正是目前 es 開源社區正在開發中的 Searchable Snapshots 功能,從Searchable Snapshots API的官方文檔上可以看到,我們可以創建一個索引,將其掛載到一個指定的快照中,這個新的索引是可查詢的,雖然查詢時間可能會慢點,但是在日志場景中,對一些較老的索引進行查詢時,延遲大點一般都是可以接受的。
所以我認為,Searchable Snapshots 解決了很多痛點,將會給 ES 帶來新的繁榮!
總結
經歷過上述運維和優化 ES 集群的實踐,我們總結到的經驗有:
新集群上線前務必做好集群規模和節點規格的評估
集群整體的分片數量不能太多,可以通過調整使用方式并且借助 ES 本身的能力不斷進行優化,使得集群總體的分片數維持在一個較低的水位,保證集群的穩定性
Searchable Snapshots 利器會給 ES 帶來新的生命力,需要重點關注并研究其實現原理
從一開始和客戶進行接觸,了解客戶訴求,逐步解決 ES 集群的問題,最終使得 ES 集群能夠保持穩定,這中間的經歷讓我真真正正地領悟到"實踐出真知"這句話的真諦,只有不斷實踐,才能對異常情況迅速做出反應,以及對客戶提的優化需求迅速反饋。
小程序云開發實戰直播課由官方核心研發團隊傾力打造,共分為三期,內容包括現場編程展示云函數、云調用、云存儲、云開發數據庫等小程序云開發中的常用能力,結合具體場景介紹 kbone 小程序多端框架等應用和實踐經驗內容,并提供與研發工程師交流答疑等機會,共建小程序開發生態。
首期直播時間:
8月6日(本周四) 晚20:00
直播主題:
用云開發,快速做個小程序
點擊下方海報立即預約首期直播!
總結
以上是生活随笔為你收集整理的PB 级大规模 Elasticsearch 集群运维与调优实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java内存泄漏、性能优化、宕机死锁的N
- 下一篇: “秒开”浏览器实现起来有多难?