背景
某中型互聯(lián)網(wǎng)公司的游戲業(yè)務(wù)说贝,使用了騰訊云的Elasticsearch產(chǎn)品议惰,采用ELK架構(gòu)存儲業(yè)務(wù)日志。因?yàn)橛螒驑I(yè)務(wù)本身的日志數(shù)據(jù)量非常大(寫入峰值在100w qps)乡恕,在服務(wù)客戶的幾個月中言询,踩了不少坑,經(jīng)過數(shù)次優(yōu)化與調(diào)整傲宜,把客戶的ES集群調(diào)整的比較穩(wěn)定运杭,避免了在業(yè)務(wù)高峰時客戶集群的讀寫異常,并且降低了客戶的資金成本和使用成本函卒。下面把服務(wù)客戶過程中遇到的典型問題進(jìn)行梳理辆憔,總結(jié)經(jīng)驗(yàn),避免再次踩坑报嵌。
場景1:初次交鋒
解決方案架構(gòu)師A: bellen, XX要上線一款新游戲虱咧,日志存儲決定用ELK架構(gòu),他們決定在XX云和我們之間二選一锚国,我們首先去他們公司和他們交流一下腕巡,爭取拿下!
bellen: 好跷叉,隨時有空逸雹!
。云挟。梆砸。
和架構(gòu)師一起前往該公司,跟負(fù)責(zé)底層組件的運(yùn)維部門的負(fù)責(zé)人進(jìn)行溝通园欣。
XX公司運(yùn)維老大:不要講你們的PPT了帖世,先告訴我你們能給我們帶來什么!
bellen: 沸枯。日矫。。呃绑榴,我們有很多優(yōu)勢哪轿。。翔怎。比如靈活地?cái)U(kuò)容縮容集群窃诉,還可以一鍵平滑升級集群版本杨耙,并且提供有跨機(jī)房容災(zāi)的集群從而實(shí)現(xiàn)高可用。飘痛。
XX公司運(yùn)維老大:你說的這些別的廠商也有珊膜,我就問一個問題,我們現(xiàn)在要存儲一年的游戲日志宣脉,不能刪除數(shù)據(jù)车柠,每天就按10TB的數(shù)據(jù)量算,一年也得有個3PB多的數(shù)據(jù)塑猖,這么大的數(shù)量竹祷,都放在SSD云盤上,我們的成本太高了萌庆,你們有什么方案既能夠滿足我們存儲這么大數(shù)據(jù)量的需求溶褪,同時能夠降低我們的成本嗎?
bellen: 我們本身提供的有冷熱模式的集群践险,熱節(jié)點(diǎn)采用SSD云硬盤猿妈,冷節(jié)點(diǎn)采用SATA盤,采用ES自帶的ILM索引生命周期管理功能定期把較老的索引從熱節(jié)點(diǎn)遷移到冷節(jié)點(diǎn)上巍虫,這樣從整體上可以降低成本彭则。另外一方面,也可以定期把更老的索引通過snapshot快照備份到COS對象存儲中占遥,然后刪除索引俯抖,這樣成本就更低了。
XX公司運(yùn)維老大:存儲到COS就是冷存儲唄瓦胎,我們需要查詢COS里的數(shù)據(jù)時芬萍,還得再把數(shù)據(jù)恢復(fù)到ES里?這樣不行搔啊,速度太慢了柬祠,業(yè)務(wù)等不了那么長時間,我們的數(shù)據(jù)不能刪除负芋,只能放在ES里漫蛔!你們能不能給我們提供一個API, 讓老的索引數(shù)據(jù)雖然存儲在COS里,但是通過這個API依然可以查詢到數(shù)據(jù)旧蛾,而不是先恢復(fù)到ES莽龟, 再進(jìn)行查詢?
bellen: 锨天。毯盈。。呃病袄,這個可以做奶镶,但是需要時間迟赃。是否可以采用hadoop on COS的架構(gòu)陪拘,把存量的老的索引數(shù)據(jù)通過工具導(dǎo)入到COS厂镇,通過hive去查詢,這樣成本會非常低左刽,數(shù)據(jù)依然是隨時可查的捺信。
XX公司運(yùn)維老大:那不行,我們只想用成熟的ELK架構(gòu)來做欠痴,再增加hadoop那一套東西迄靠,我們沒那么多人力搞這個事!
bellen: 好吧,那可以先搞一個集群測試起來喇辽,看看性能怎么樣掌挚。關(guān)于存量數(shù)據(jù)放在COS里但是也需要查詢的問題,我們可以先制定方案菩咨,盡快實(shí)施起來吠式。
XX公司運(yùn)維老大:行吧劳秋,我們現(xiàn)在按每天10TB數(shù)據(jù)量預(yù)估滩褥,先購買一個集群胸懈,能撐3個月的數(shù)據(jù)量就行胁后,能給一個集群配置的建議嗎贬蛙?
bellen: 目前支持單節(jié)點(diǎn)磁盤最大6TB, cpu和內(nèi)存的話可以放到8核32G單節(jié)點(diǎn)鹰晨,單節(jié)點(diǎn)跑2w qps寫入沒有問題忧侧,后面也可以進(jìn)行縱向擴(kuò)容和橫向擴(kuò)容年枕。
XX公司運(yùn)維老大:好标捺,我們先測試一下懊纳。
場景2:沒有評估好節(jié)點(diǎn)配置和規(guī)模,上線后集群扛不住寫入壓力
N 天后亡容,架構(gòu)師A直接在微信群里反饋:"bellen, 客戶反饋這邊的ES集群性能不行啊嗤疯,使用logstash消費(fèi)kafka中的日志數(shù)據(jù),跑了快一天了數(shù)據(jù)還沒追平萍倡,這是線上的集群身弊,麻煩緊急看一下吧。列敲。"
我一看阱佛,一臉懵, 什么時候已經(jīng)上線了啊,不是還在測試中嗎戴而?
XX公司運(yùn)維小B: 我們購買了8核32G*10節(jié)點(diǎn)的集群凑术,單節(jié)點(diǎn)磁盤6TB, 索引設(shè)置的10分片1副本,現(xiàn)在使用logstash消費(fèi)kafka中的數(shù)據(jù)所意,一直沒有追平淮逊,kafka中還有很多數(shù)據(jù)積壓催首,感覺是ES的寫入性能有問題。
隨后我立即查看了集群的監(jiān)控?cái)?shù)據(jù)泄鹏,發(fā)現(xiàn)cpu和load都很高郎任,jvm堆內(nèi)存使用率平均都到了90%,節(jié)點(diǎn)jvm gc非常頻繁了备籽,部分節(jié)點(diǎn)因?yàn)轫憫?yīng)緩慢舶治,不停的離線又上線。车猬。
經(jīng)過溝通霉猛,發(fā)現(xiàn)用戶的使用姿勢是filebeat+kafka+logstash+elasticsearch, 當(dāng)前已經(jīng)在kafka中存儲了有10天的日志數(shù)據(jù),啟動了20臺logstash進(jìn)行消費(fèi)珠闰,logstash的batch size也調(diào)到了5000惜浅,性能瓶頸是在ES這一側(cè)》龋客戶8核32G*10節(jié)點(diǎn)的集群坛悉,理論上跑10w qps沒有問題,但是logstash消費(fèi)積壓的數(shù)據(jù)往ES寫入的qps遠(yuǎn)不止10w阅仔,所以是ES扛不住寫入壓力了吹散,所以只能對ES集群進(jìn)行擴(kuò)容,為了加快存量數(shù)據(jù)的消費(fèi)速度八酒,先縱向擴(kuò)容單節(jié)點(diǎn)的配置到32核64GB空民,之后再橫向增加節(jié)點(diǎn),以保證ES集群能夠最大支持100w qps的寫入(這里需要注意的是羞迷,增加節(jié)點(diǎn)后索引的分片數(shù)量也需要調(diào)整)界轩。
所以一般新客戶接入使用ES時,必須要事先評估好節(jié)點(diǎn)配置和集群規(guī)模衔瓮,可以從以下幾個方面進(jìn)行評估:
- 存儲容量:要考慮索引副本數(shù)量浊猾、數(shù)據(jù)膨脹、ES內(nèi)部任務(wù)額外占用的磁盤空間(比如segment merge)以及操作系統(tǒng)占用的磁盤空間等因素热鞍,如果再需要預(yù)留50%的空閑磁盤空間葫慎,那么集群總的存儲容量大約為源數(shù)據(jù)量的4倍
- 計(jì)算資源:主要考慮寫入,2核8GB的節(jié)點(diǎn)可以支持5000qps的寫入薇宠,隨著節(jié)點(diǎn)數(shù)量和節(jié)點(diǎn)規(guī)格的提升偷办,寫入能力基本呈線性增長
- 索引和分片數(shù)量評估:一般一個shard的數(shù)據(jù)量在30-50GB為宜,可以以此確定索引的分片數(shù)量以及確定按天還是按月建索引澄港。需要控制單節(jié)點(diǎn)總的分片數(shù)量椒涯,1GB堆內(nèi)存支持20-30個分片為宜;另外需要控制集群整體的分片數(shù)量回梧,集群總體的分片數(shù)量一般不要超過3w废岂。
場景3:logstash消費(fèi)kafka性能調(diào)優(yōu)
上述場景2遇到的問題是業(yè)務(wù)上線前沒有對集群配置和規(guī)模進(jìn)行合理的評估祖搓,導(dǎo)致上線后ES集群負(fù)載就很高,通過合理的擴(kuò)容處理湖苞,集群最終抗住了寫入壓力拯欧。但是又有新的問題出現(xiàn)了。
因?yàn)閗afka積壓的數(shù)據(jù)比較多袒啼,客戶使用logstash消費(fèi)kafka數(shù)據(jù)時哈扮,反饋有兩個問題:
- 增加多臺logstash消費(fèi)kafka數(shù)據(jù),消費(fèi)速度沒有線性提升
- kafka的不同topic消費(fèi)速度不均勻蚓再、topic內(nèi)不同partition消費(fèi)的速度也不均勻
經(jīng)過分析客戶logstash的配置文件,發(fā)現(xiàn)問題出現(xiàn)的原因主要是:
- topic的partition數(shù)量少包各,雖然logstash機(jī)器數(shù)量多摘仅,但是卻沒有充分利用機(jī)器資源并行消費(fèi)數(shù)據(jù),導(dǎo)致消費(fèi)速度一直上不去
- 所有l(wèi)ogstash的配置文件都相同问畅,使用一個group同時消費(fèi)所有的topic娃属,存在資源競爭的問題
分析后,對kafka和logstash進(jìn)行了如下優(yōu)化:
- 提高kafka topic的分區(qū)數(shù)量
- 對logstash進(jìn)行分組护姆;對于數(shù)據(jù)量較大的topic矾端,可以單獨(dú)設(shè)置一個消費(fèi)組進(jìn)行消費(fèi),有一組logstash單獨(dú)使用這個消費(fèi)組對該topic進(jìn)行消費(fèi)卵皂;其它的數(shù)據(jù)量較小的topic秩铆,可以共用一個消費(fèi)組和一組logstash
- 每組logstash中總的consumer_threads數(shù)量和消費(fèi)組總的partion數(shù)量保持一致,比如有3個logstash進(jìn)程灯变,消費(fèi)的topic的partition數(shù)量為24殴玛, 那么每個logstash配置文件中的consumer_threads就設(shè)置為8
通過上述優(yōu)化,最終使得logstash機(jī)器資源都被充分利用上添祸,很快消費(fèi)完堆積的kafka數(shù)據(jù)滚粟,待消費(fèi)速度追平生成速度后,logstash消費(fèi)kafka一直穩(wěn)定運(yùn)行刃泌,沒有出現(xiàn)積壓凡壤。
另外,客戶一開始使用的是5.6.4版本的logstash耙替,版本較老亚侠,使用過程中出現(xiàn)因?yàn)閱蝹€消息體過長導(dǎo)致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修復(fù)了這個問題,避免了crash)林艘。
場景4:磁盤要滿了盖奈,緊急擴(kuò)容?
客戶的游戲上線有一個月了狐援,原先預(yù)估每天最多有10TB的數(shù)據(jù)量钢坦,實(shí)際則是在運(yùn)營活動期間每天產(chǎn)生20TB的數(shù)據(jù)究孕,原先6TB*60=360TB總量的數(shù)據(jù)盤使用率也達(dá)到了80%。針對這種情況爹凹,我們建議客戶使用冷熱分離的集群架構(gòu)厨诸,在原先60個熱節(jié)點(diǎn)的基礎(chǔ)上,增加一批warm節(jié)點(diǎn)存儲冷數(shù)據(jù)禾酱,利用ILM(索引生命周期管理)功能定期遷移熱節(jié)點(diǎn)上的索引到warm節(jié)點(diǎn)上微酬。
通過增加warm節(jié)點(diǎn)的方式,客戶的集群磁盤總量達(dá)到了780TB颤陶, 可以滿足最多三個月的存儲需求颗管。但是客戶的需求還沒有滿足:
XX公司運(yùn)維老大:給我們一個能存放一年數(shù)據(jù)的方案吧,總是通過加節(jié)點(diǎn)擴(kuò)容磁盤的方式不是長久之計(jì)滓走,我們得天天盯著這個集群垦江,運(yùn)維成本很高!并且一直加節(jié)點(diǎn)搅方,ES會扛不住吧比吭?
bellen: 可以嘗試使用我們新上線的支持本地盤的機(jī)型,熱節(jié)點(diǎn)最大支持7.2TB的本地SSD盤姨涡,warm節(jié)點(diǎn)最大支持48TB的本地SATA盤衩藤。一方面熱節(jié)點(diǎn)的性能相比云盤提高了,另外warm節(jié)點(diǎn)可以支持更大的磁盤容量涛漂。單節(jié)點(diǎn)可以支持的磁盤容量增大了赏表,節(jié)點(diǎn)數(shù)量就不用太多了,可以避免踩到因?yàn)楣?jié)點(diǎn)數(shù)量太多而觸發(fā)的坑怖喻。
XX公司運(yùn)維老大:現(xiàn)在用的是云盤底哗,能替換成本地盤嗎,怎么替換锚沸?
bellen: 不能直接替換跋选,需要在集群中新加入帶本地盤的節(jié)點(diǎn),把數(shù)據(jù)從老的云盤節(jié)點(diǎn)遷移到新的節(jié)點(diǎn)上哗蜈,遷移完成后再剔除掉舊的節(jié)點(diǎn)前标,這樣可以保證服務(wù)不會中斷,讀寫都可以正常進(jìn)行距潘。
XX公司運(yùn)維老大:好炼列,可以實(shí)施,盡快搞起來音比!
云盤切換為本地盤俭尖,是通過調(diào)用云服務(wù)后臺的API自動實(shí)施的。在實(shí)施之后,觸發(fā)了數(shù)據(jù)從舊節(jié)點(diǎn)遷移到新節(jié)點(diǎn)的流程稽犁,但是大約半個小時候焰望,問題又出現(xiàn)了:
XX公司運(yùn)維小B: bellen, 快看一下,ES的寫入快掉0了已亥。
bellen: 熊赖。。虑椎。
通過查看集群監(jiān)控震鹉,發(fā)現(xiàn)寫入qps直接由50w降到1w,寫入拒絕率猛增捆姜,通過查看集群日志传趾,發(fā)現(xiàn)是因?yàn)楫?dāng)前小時的索引沒有創(chuàng)建成功導(dǎo)致寫入失敗。
緊急情況下娇未,執(zhí)行了以下操作定位到了原因:
GET _cluster/health; 發(fā)現(xiàn)集群健康狀態(tài)是green墨缘,但是有大約6500個relocating_shards, number_of_pending_tasks數(shù)量達(dá)到了數(shù)萬零抬。
GET _cat/pending_tasks?v; 發(fā)現(xiàn)大量的"shard-started"任務(wù)在執(zhí)行中,任務(wù)優(yōu)先級是"URGENT", 以及大量的排在后面的"put mapping"的任務(wù)宽涌,任務(wù)優(yōu)先級是"HIGH"平夜;"URGENT"優(yōu)先級比"HIGH"優(yōu)先級要高,因?yàn)榇罅康姆制瑥呐f的節(jié)點(diǎn)遷移到新的節(jié)點(diǎn)上卸亮,造成了索引創(chuàng)建的任務(wù)被阻塞忽妒,從而導(dǎo)致寫入數(shù)據(jù)失敗。
為什么會有這么多的分片在遷移中兼贸?通過GET _cluster/settings發(fā)現(xiàn)"cluster.routing.allocation.node_concurrent_recoveries"的值為50,而目前有130個舊節(jié)點(diǎn)在把分片遷移到130個新節(jié)點(diǎn)中段直,所以有130*50=6500個遷移中的分片。而"cluster.routing.allocation.node_concurrent_recoveries"參數(shù)的值默認(rèn)為2溶诞,應(yīng)該是之前在執(zhí)行縱向擴(kuò)容集群時為了加快分片遷移速度人為修改了這個值(因?yàn)榧阂婚_始節(jié)點(diǎn)數(shù)量沒有很多鸯檬,索引同時遷移中的分片也不會太多,所以創(chuàng)建新索引不會被阻塞)螺垢。
現(xiàn)在通過PUT _cluster/settings把"cluster.routing.allocation.node_concurrent_recoveries"參數(shù)修改為2喧务。但是因?yàn)?put settings"任務(wù)的優(yōu)先級也是"HIGH", 低于"shard-started"任務(wù)的優(yōu)先級枉圃,所以更新該參數(shù)的操作還是會被阻塞功茴,ES報(bào)錯執(zhí)行任務(wù)超時。此時孽亲,進(jìn)行了多次重試坎穿,最終成功把把"cluster.routing.allocation.node_concurrent_recoveries"參數(shù)修改為了2。
-
現(xiàn)在通過GET _cluster/health看到遷移中的分片數(shù)量在逐漸減少,為了不增加新的遷移任務(wù)玲昧,把執(zhí)行數(shù)據(jù)遷移的exclude配置取消掉:
PUT _cluster/settings { "transient": { "cluster.routing.allocation.exclude._name": "" } }
-
同時調(diào)大分片恢復(fù)時節(jié)點(diǎn)進(jìn)行數(shù)據(jù)傳輸?shù)拿棵胱畲笞止?jié)數(shù)(默認(rèn)為40MB)栖茉,加速存量的分片遷移任務(wù)的執(zhí)行:
PUT _cluster/settings { "transient": { "indices": { "recovery": { "max_bytes_per_sec": "200mb" } } } }
-
現(xiàn)在看到遷移中的分片數(shù)量慢慢減少,新索引已經(jīng)創(chuàng)建成功了酌呆,寫入恢復(fù)正常了衡载。到下個整點(diǎn)時,發(fā)現(xiàn)新建索引還是比較慢隙袁,因?yàn)檫€有幾百個分片在遷移中痰娱,創(chuàng)建新索引大概耗時5分鐘,這5分鐘內(nèi)寫入也是失敗的菩收。
等幾百個遷移中的分片都執(zhí)行完畢后梨睁,新建索引就比較快了,也不會再寫入失敗了娜饵。但是問題是當(dāng)前正在執(zhí)行云盤節(jié)點(diǎn)切換為本地盤的流程坡贺,需要把數(shù)據(jù)從舊的130個節(jié)點(diǎn)上遷移到新的130個節(jié)點(diǎn)上,數(shù)據(jù)遷移的任務(wù)不能停箱舞,那該怎么辦遍坟?既然新創(chuàng)建索引比較慢,那就只好提前把索引都創(chuàng)建好晴股,避免了在每個整點(diǎn)數(shù)據(jù)寫入失敗的情況愿伴。通過編寫python腳本,每天執(zhí)行一次电湘,提前把第二天的每個小時的索引創(chuàng)建好隔节,創(chuàng)建完成了再把"cluster.routing.allocation.exclude._name"更改為所有的舊節(jié)點(diǎn),保證數(shù)據(jù)遷移任務(wù)能夠正常執(zhí)行寂呛。
總量400TB的數(shù)據(jù)怎诫,大約經(jīng)過10天左右,終于完成遷移了贷痪;配合提前新建索引的python腳本幻妓,這10天內(nèi)也沒有出現(xiàn)寫入失敗的情況。
經(jīng)過了這次擴(kuò)容操作呢诬,總結(jié)了如下經(jīng)驗(yàn):
- 分片數(shù)量過多時涌哲,如果同時進(jìn)行遷移的分片數(shù)量過多,會阻塞索引創(chuàng)建和其它配置更新操作尚镰,所以在進(jìn)行數(shù)據(jù)遷移時阀圾,要保證"cluster.routing.allocation.node_concurrent_recoveries"參數(shù)和"cluster.routing.allocation.cluster_concurrent_rebalance"為較小的值。
- 如果必須要進(jìn)行數(shù)據(jù)遷移狗唉,則可以提前創(chuàng)建好索引初烘,避免ES自動創(chuàng)建索引時耗時較久,從而導(dǎo)致寫入失敗。
場景5:10萬個分片肾筐?
在穩(wěn)定運(yùn)行了一陣后哆料,集群又出問題了。吗铐。
XX公司運(yùn)維小B: bellen, 昨晚凌晨1點(diǎn)鐘之后东亦,集群就沒有寫入了,現(xiàn)在kafka里有大量的數(shù)據(jù)堆積唬渗,麻煩盡快看一下典阵?
bellen: 。镊逝。壮啊。
通過cerebro查看集群,發(fā)現(xiàn)集群處于yellow狀態(tài)撑蒜,然后發(fā)現(xiàn)集群有大量的錯誤日志:
{"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"}
然后再進(jìn)一步查看集群日志歹啼,發(fā)現(xiàn)有"master not discovered yet..."之類的錯誤日志,檢查三個master節(jié)點(diǎn)座菠,發(fā)現(xiàn)有兩個master掛掉狸眼,只剩一個了,集群無法選主浴滴。
登陸到掛了了master節(jié)點(diǎn)機(jī)器上份企,發(fā)現(xiàn)保活程序無法啟動es進(jìn)程巡莹,第一直覺是es進(jìn)程oom了;此時也發(fā)現(xiàn)master節(jié)點(diǎn)磁盤使用率100%甜紫, 檢查了JVM堆內(nèi)存快照文件目錄降宅,發(fā)現(xiàn)有大量的快照文件,于是刪除了一部分文件囚霸,重啟es進(jìn)程腰根,進(jìn)程正常啟動了;但是問題是堆內(nèi)存使用率太高拓型,gc非常頻繁额嘿,master節(jié)點(diǎn)響應(yīng)非常慢,大量的創(chuàng)建索引的任務(wù)都超時劣挫,阻塞在任務(wù)隊(duì)列中册养,集群還是無法恢復(fù)正常。
看到集群master節(jié)點(diǎn)的配置是16核32GB內(nèi)存压固,JVM實(shí)際只分配了16GB內(nèi)存球拦,此時只好通過對master節(jié)點(diǎn)原地增加內(nèi)存到64GB(虛擬機(jī),使用的騰訊云CVM, 可以調(diào)整機(jī)器規(guī)格坎炼,需要重啟)愧膀,master節(jié)點(diǎn)機(jī)器重啟之后,修改了es目錄jvm.options文件谣光,調(diào)整了堆內(nèi)存大小檩淋,重新啟動了es進(jìn)程。
3個master節(jié)點(diǎn)都恢復(fù)正常了萄金,但是分片還需要進(jìn)行恢復(fù)蟀悦,通過GET _cluster/health看到集群當(dāng)前有超過10w個分片,而這些分片恢復(fù)還需要一段時間捡絮,通過調(diào)大"cluster.routing.allocation.node_concurrent_recoveries"熬芜, 增大分片恢復(fù)的并發(fā)數(shù)量。實(shí)際上5w個主分片恢復(fù)的是比較快的了福稳,但是副本分片的恢復(fù)就相對慢很多涎拉,因?yàn)椴糠指北痉制枰獜闹鞣制贤綌?shù)據(jù)才能恢復(fù)。此時可以采取的方式是把部分舊的索引副本數(shù)量調(diào)為0的圆, 讓大量副本分片恢復(fù)的任務(wù)盡快結(jié)束鼓拧,保證新索引能夠正常創(chuàng)建,從而使得集群能夠正常寫入越妈。
總結(jié)這次故障的根本原因是集群的索引和分片數(shù)量太多季俩,集群元數(shù)據(jù)占用了大量的堆內(nèi)存,而master節(jié)點(diǎn)本身的JVM內(nèi)存只有16GB(數(shù)據(jù)節(jié)點(diǎn)有32GB)梅掠, master節(jié)點(diǎn)頻繁full gc導(dǎo)致master節(jié)點(diǎn)異常酌住,從而最終導(dǎo)致整個集群異常。所以要解決這個問題阎抒,還是得從根本上解決集群的分片數(shù)量過多的問題酪我。
目前日志索引是按照小時創(chuàng)建,60分片1副本且叁,每天有24*60*2=2880個分片都哭,每個月就產(chǎn)生86400個分片,這么多的分片可能會帶來嚴(yán)重的問題逞带。有以下幾種方式解決分片數(shù)量過多的問題:
- 可以在ILM的warm phase中開啟shrink功能欺矫,對老的索引從60分片shrink到5分片,分片數(shù)量可以降低12倍
- 業(yè)務(wù)可以把每小時創(chuàng)建索引修改為每兩個小時或者更長展氓,可以根據(jù)每個分片數(shù)量最多支持50GB的數(shù)據(jù)推算多長時間創(chuàng)建新索引合適
- 對老的索引設(shè)置副本為0穆趴,只保留主分片,分片數(shù)量能夠再下降近一倍带饱,存儲量也下降近一倍
- 定期關(guān)閉最老的索引毡代,執(zhí)行{index}/_close
和客戶溝通過后阅羹,客戶表示可以接受方式1和方式2,但是方式3和4不能接受教寂,因?yàn)榭紤]到存在磁盤故障的可能性捏鱼,必須保留一個副本來保證數(shù)據(jù)的可靠性;另外還必須保證所有數(shù)據(jù)都是隨時可查詢的酪耕,不能關(guān)閉导梆。
場景6:有點(diǎn)坑的ILM
在場景5中,雖然通過臨時給master節(jié)點(diǎn)增加內(nèi)存迂烁,抗住了10w分片看尼,但是不能從根本上解決問題∶瞬剑客戶的數(shù)據(jù)是計(jì)劃保留一年的藏斩,如果不進(jìn)行優(yōu)化,集群必然扛不住數(shù)十萬個分片却盘。所以接下來需要著重解決集群整體分片數(shù)量過多的問題狰域,在場景5的最后提到了,用戶可以接受開啟shrink以及降低索引創(chuàng)建粒度(經(jīng)過調(diào)整后黄橘,每兩個小時創(chuàng)建一個索引)兆览,這在一定程度上減少了分片的數(shù)量,能夠使集群暫時穩(wěn)定一陣塞关。
輔助客戶在kibana上配置了如下的ILM策略:
在warm phase, 把創(chuàng)建時間超過360小時的索引從hot節(jié)點(diǎn)遷移到warm節(jié)點(diǎn)上抬探,保持索引的副本數(shù)量為1,之所以使用360小時作為條件帆赢,而不是15天作為條件小压,是因?yàn)榭蛻舻乃饕前葱r創(chuàng)建的,如果以15天作為遷移條件椰于,則在每天凌晨都會同時觸發(fā)15天前的24個索引一共24*120=2880個分片同時開始遷移索引场航,容易引發(fā)場景4中介紹的由于遷移分片數(shù)量過多導(dǎo)致創(chuàng)建索引被阻塞的問題,所以以360小時作為條件廉羔,則在每個小時只會執(zhí)行一個索引的遷移,這樣把24個索引的遷移任務(wù)打平僻造,避免其它任務(wù)被阻塞的情況發(fā)生憋他。
同時,也在warm phase階段髓削,設(shè)置索引shrink竹挡,把索引的分片數(shù)縮成5個,因?yàn)槔系乃饕呀?jīng)不執(zhí)行寫入了立膛,所以也可以執(zhí)行force merge, 強(qiáng)制把segment文件合并為1個揪罕,可以獲得更好的查詢性能梯码。
另外,設(shè)置了ILM策略后好啰,可以在索引模板里增加index.lifecycle.name配置轩娶,使得所有新創(chuàng)建的索引都可以和新添加的ILM策略關(guān)聯(lián),從而使得ILM能夠正常運(yùn)行框往。
客戶使用的ES版本是6.8.2鳄抒, 在運(yùn)行ILM的過程中, 也發(fā)現(xiàn)一些問題:
新添加的策略只能對新創(chuàng)建的索引生效椰弊,存量的索引只能通過批量修改索引settings里的index.lifecycle.name執(zhí)行策略许溅。
如果一個策略進(jìn)行了修改,那么所有存量的索引秉版,不管是有沒有執(zhí)行過該策略贤重,都不會執(zhí)行修改后的策略,也即修改后的策略只對修改成功后新創(chuàng)建的索引生效清焕。比如一開始的策略沒有開啟shrink, 現(xiàn)在修改策略內(nèi)容添加了shrink操作并蝗,那么只有之后新創(chuàng)建的索引在達(dá)到策略觸發(fā)條件(比如索引已經(jīng)創(chuàng)建超過360個小時)后才會執(zhí)行shrink, 而之前的所有索引都不會執(zhí)行shrink,此時若想對存量的索引也執(zhí)行shrink耐朴,只能夠通過腳本批量執(zhí)行了借卧。
-
在warm phase同時執(zhí)行索引遷移和shrink會觸發(fā)es的bug, 如上圖中的ILM策略筛峭,索引本身包含60分片1副本铐刘,初始時都在hot節(jié)點(diǎn)上,在創(chuàng)建完成360小時之后影晓,會執(zhí)行遷移镰吵,把索引都遷移到warm節(jié)點(diǎn)上,同時又需要把分片shrink到5挂签,在實(shí)際執(zhí)行中疤祭,發(fā)現(xiàn)一段時間后有大量的unassigned shards,分片無法分配的原因如下:
"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 [[x-2020.06.19-13][58], node[LKsSwrDsSrSPRZa-EPBJPg], [P], s[STARTED], a[id=iRiG6mZsQUm5Z_xLiEtKqg]]" }, { "decider" : "awareness", "decision" : "NO", "explanation" : "there are too many copies of the shard allocated to nodes with attribute [ip], there are [2] total configured shard copies for this shard id and [130] total attribute values, expected the allocated shard count per attribute [2] to be less than or equal to the upper bound of the required number of shards per attribute [1]" }
這是因?yàn)閟hrink操作需要新把索引完整的一份數(shù)據(jù)都遷移到一個節(jié)點(diǎn)上饵婆,然后在內(nèi)存中構(gòu)建新的分片元數(shù)據(jù)勺馆,把新的分片通過軟鏈接指向到幾個老的分片的數(shù)據(jù)煞赢,在ILM中執(zhí)行shrink時先朦,ILM會對索引進(jìn)行如下配置:
```
{
"index.routing" : {
"allocation" : {
"require" : {
"temperature" : "warm",
"_id" : "LKsSwrDsSrSPRZa-EPBJPg"
}
}
}
}
```
問題是索引包含副本,而主分片和副本分片又不能在同一個節(jié)點(diǎn)上雕旨,所以會出現(xiàn)部分分片無法分配的情況(不是全部搓译,只有一部分)悲柱,這里應(yīng)該是觸發(fā)了6.8版本的ILM的bug,需要查看源碼才能定位解決這個bug些己,目前還在研究中豌鸡。當(dāng)前的workaround是通過腳本定期掃描出現(xiàn)unassigned shards的索引嘿般,修改其settings:
```
{
"index.routing" : {
"allocation" : {
"require" : {
"temperature" : "warm",
"_id" : null
}
}
}
}
```
優(yōu)先保證分片先從hot節(jié)點(diǎn)遷移到warm節(jié)點(diǎn),這樣后續(xù)的shrink才能順利執(zhí)行(也可能執(zhí)行失敗涯冠,因?yàn)?0個分片都在一個節(jié)點(diǎn)上炉奴,可能會觸發(fā)rebalance, 導(dǎo)致分片遷移走,shrink的前置條件又不滿足功偿,導(dǎo)致執(zhí)行失敗)盆佣。要完全規(guī)避這個問題,還得在ILM策略中設(shè)置械荷,滿足創(chuàng)建時間超過360個小時的索引共耍,副本直接調(diào)整為0,但是客戶又不接受吨瞎,沒辦法痹兜。
場景7:自己實(shí)現(xiàn)SLM
在場景5和6中,介紹了10w個分片會給集群帶來的影響和通過開啟shrink來降低分片數(shù)量颤诀,但是仍然有兩個需要重點(diǎn)解決的問題:
- 索引不斷新建字旭,如何保證一年內(nèi),集群總的分片數(shù)量不高于10w崖叫,穩(wěn)定在一個較低的水位
- ILM中執(zhí)行shrink可能會導(dǎo)致部分分片未分配以及shrink執(zhí)行失敗遗淳,怎么徹底解決
可以估算一下,按小時建索引心傀,60分片1副本屈暗,一年的分片數(shù)為24*120*365=1051200個分片,執(zhí)行shrink后分片數(shù)量24*10*350 + 24*120*15 = 127200(15天內(nèi)的新索引為了保障寫入性能和數(shù)據(jù)可靠性脂男,仍然保持60分片1副本养叛,舊的索引shrink為5分片1副本), 仍然有超過10w個分片。結(jié)合集群一年總的存儲量和單個分片可以支持的數(shù)據(jù)量大小進(jìn)行評估宰翅,我們期望集群總體的分片數(shù)量可以穩(wěn)定為6w~8w弃甥,怎么優(yōu)化?
可以想到的方案是執(zhí)行數(shù)據(jù)冷備份汁讼,把比較老的索引都冷備到其它的存儲介質(zhì)上比如HDFS淆攻,S3,騰訊云的COS對象存儲等嘿架,但是問題是這些冷備的數(shù)據(jù)如果也要查詢卜录,需要先恢復(fù)到ES中才可查,恢復(fù)速度比較慢眶明,客戶無法接受。由此也產(chǎn)生了新的想法筐高,目前老的索引仍然是1副本搜囱,可以把老索引先進(jìn)行冷備份丑瞧,再把副本調(diào)為0,這樣做有以下幾點(diǎn)好處:
- 集群整體分片數(shù)量能降低一半
- 數(shù)據(jù)存儲量也能降低一半蜀肘,集群可以存儲更多數(shù)據(jù)
- 老的索引仍然隨時可查
- 極端情況下绊汹,磁盤故障引起只有一個副本的索引數(shù)據(jù)無法恢復(fù)時,可以從冷備介質(zhì)中進(jìn)行恢復(fù)
經(jīng)過和客戶溝通扮宠,客戶接受了上述方案西乖,計(jì)劃把老索引冷備到騰訊云的對象存儲COS中,實(shí)施步驟為:
- 所有存量的老索引坛增,需要批量處理获雕,盡快地備份到COS中,然后批量修改副本數(shù)量為0
- 最近新建的索引收捣,采用按天備份的策略届案,結(jié)合ILM, 修改策略,在ILM執(zhí)行過程中修改索引副本數(shù)為0(ILM的warm phase 和cold phase都支持設(shè)置副本數(shù)量)
其中步驟1的實(shí)施可以通過腳本實(shí)現(xiàn)罢艾,本案例中采用騰訊云SCF云函數(shù)進(jìn)行實(shí)施楣颠,方便快捷可監(jiān)控。實(shí)施要點(diǎn)有:
- 按天創(chuàng)建snapshot咐蚯,批量備份每天產(chǎn)生的24個索引童漩,如果是按月或者更大粒度創(chuàng)建快照,因數(shù)據(jù)量太大如果執(zhí)行快照過程中出現(xiàn)中斷春锋,則必須全部重來矫膨,耗時耗力;按小時創(chuàng)建快照也不適用看疙,會造成快照數(shù)量太多豆拨,可能會踩到坑
- 每創(chuàng)建一個快照,后續(xù)需要輪詢快照的狀態(tài)能庆,保證前一個快照state為"SUCCESS"之后施禾,再創(chuàng)建下一個快照;因?yàn)榭煺帐前刺靹?chuàng)建的搁胆,快照名字可以為snapshot-2020.06.01, 該快照只備份6月1號的所有索引弥搞。而在檢查到snapshot-2020.06.01快照執(zhí)行成功后,然后新建下一個快照時渠旁,需要知道要對哪天的索引打快照攀例,因此需要記錄當(dāng)前正在執(zhí)行哪一個快照。有兩種方式記錄顾腊,一是把當(dāng)前正在執(zhí)行的快照日期后綴"2020.06.01"寫入到文件中, 腳本通過定時任務(wù)輪詢時粤铭,每次都讀文件;另外一種方式是創(chuàng)建一個臨時的索引杂靶,把"2020.06.01"寫入到這個臨時索引的一個doc中梆惯,之后對該doc進(jìn)行查詢或者更新酱鸭。
- 創(chuàng)建快照時,可以把"include_global_state"置為false, 不對集群的全局狀態(tài)信息進(jìn)行備份垛吗。
在實(shí)施完步驟1之后凹髓,就可以批量把對索引進(jìn)行過備份的索引副本數(shù)都調(diào)為0, 這樣一次性釋放了很多磁盤空間怯屉,并且顯著降低了集群整體的分片數(shù)量蔚舀。
接下來實(shí)施步驟2,需要每天執(zhí)行一次快照锨络,多創(chuàng)建時間較久的索引進(jìn)行備份赌躺,實(shí)施比較簡單,可以通過crontab定時執(zhí)行腳本或者使用騰訊云SCF執(zhí)行足删。
步驟2實(shí)施之后寿谴,就可以修改ILM策略,開啟cold phase, 修改索引副本數(shù)量為0:
此處的timing是創(chuàng)建時間20天后失受,需要保證步驟2中對過去老索引數(shù)據(jù)備份先執(zhí)行完成才可以進(jìn)入到cold phase.
通過老索引數(shù)據(jù)冷備并且降低索引副本讶泰,我們可以把集群整體的分片數(shù)量維持在一個較低的水位,但是還有另外一個問題待解決拂到,也即shrink失敗的問題痪署。剛好,我們可以利用對老索引數(shù)據(jù)冷備并且降低索引副本的方案兄旬,來徹底解決shrink失敗的問題狼犯。
在場景5中有提到,shrink失敗歸根接地是因?yàn)樗饕母北緮?shù)量為1领铐, 現(xiàn)在我們可以吧數(shù)據(jù)備份和降低副本提前悯森,讓老索引進(jìn)入到ILM的warm phase中時已經(jīng)是0副本,之后再執(zhí)行shrink操作就不會有問題了绪撵;同時瓢姻,因?yàn)楦北窘档土耍饕龔膆ot節(jié)點(diǎn)遷移到warm節(jié)點(diǎn)遷移的數(shù)據(jù)量也減少了一半音诈,從而降低了集群負(fù)載幻碱,一舉兩得。
因此细溅,我們需要修改ILM策略褥傍,在warm phase就把索引的副本數(shù)量調(diào)整為0, 然后去除cold phase喇聊。
另外一個可選的優(yōu)化項(xiàng)是恍风,對老的索引進(jìn)行凍結(jié),凍結(jié)索引是指把索引常駐內(nèi)存的一些數(shù)據(jù)從內(nèi)存中清理掉(比如FST, 元數(shù)據(jù)等), 從而降低內(nèi)存使用量朋贬,而在查詢已經(jīng)凍結(jié)的索引時鸥咖,會重新構(gòu)建出臨時的索引數(shù)據(jù)結(jié)構(gòu)存放在內(nèi)存中,查詢完畢再清理掉兄世;需要注意的是,默認(rèn)情況下是無法查詢已經(jīng)凍結(jié)的索引的啊研,需要在查詢時顯式的增加"ignore_throttled=false"參數(shù)御滩。
經(jīng)過上述優(yōu)化,我們最終解決了集群整體分片數(shù)量過多和shrink失敗的問題党远。在實(shí)施過程中引入了額外的定時任務(wù)腳本實(shí)施自動化快照削解,實(shí)際上在7.4版本的ES中,已經(jīng)有這個功能了沟娱,特性名稱為SLM(快照生命周期管理)氛驮,并且可以結(jié)合ILM使用,在ILM中增加了"wait_for_snapshot"的ACTION, 但是卻只能在delete phase中使用济似,不滿足我們的場景矫废。
場景8:客戶十分喜歡的Searchable Snapshots!
在上述的場景4-7中,我們花費(fèi)大量的精力去解決問題和優(yōu)化使用方式砰蠢,保證ES集群能夠穩(wěn)定運(yùn)行蓖扑,支持PB級別的存儲。溯本回原台舱,如果我們能有一個方案使得客戶只需要把熱數(shù)據(jù)放在SSD盤上律杠,然后冷數(shù)據(jù)存儲到COS/S3上,但同時又使冷數(shù)據(jù)能夠支持按需隨時可查竞惋,那我們前面碰到的所有問題都迎刃而解了柜去。可以想象得到的好處有:
- 只需要更小規(guī)模的集群和非常廉價的COS/S3對象存儲就可以支持PB級別的數(shù)據(jù)量拆宛,客戶的資金成本非常低
- 小規(guī)模的集群只需要能夠支撐熱索引的寫入和查詢即可嗓奢,集群整體的分片數(shù)不會太多,從而避免了集群不穩(wěn)定現(xiàn)象的發(fā)生
而這正是目前es開源社區(qū)正在開發(fā)中的Searchable Snapshots功能胰挑,從Searchable Snapshots API的官方文檔上可以看到蔓罚,我們可以創(chuàng)建一個索引,將其掛載到一個指定的快照中瞻颂,這個新的索引是可查詢的豺谈,雖然查詢時間可能會慢點(diǎn),但是在日志場景中贡这,對一些較老的索引進(jìn)行查詢時茬末,延遲大點(diǎn)一般都是可以接受的。
所以我認(rèn)為,Searchable Snapshots解決了很多痛點(diǎn)丽惭,將會給ES帶了新的繁榮击奶!
總結(jié)
經(jīng)歷過上述運(yùn)維和優(yōu)化ES集群的實(shí)踐,我們總結(jié)到的經(jīng)驗(yàn)有:
- 新集群上線前務(wù)必做好集群規(guī)模和節(jié)點(diǎn)規(guī)格的評估
- 集群整體的分片數(shù)量不能太多责掏,可以通過調(diào)整使用方式并且借助ES本身的能力不斷進(jìn)行優(yōu)化柜砾,使得集群總體的分片數(shù)維持在一個較低的水位,保證集群的穩(wěn)定性
- Searchable Snapshots利器會給ES帶來新的生命力换衬,需要重點(diǎn)關(guān)注并研究其實(shí)現(xiàn)原理
從一開始和客戶進(jìn)行接觸痰驱,了解客戶訴求,逐步解決ES集群的問題瞳浦,最終使得ES集群能夠保持穩(wěn)定担映,這中間的經(jīng)歷讓我真真正正的領(lǐng)悟到"實(shí)踐出真知",只有不斷實(shí)踐叫潦,才能對異常情況迅速做出反應(yīng)蝇完,以及對客戶提的優(yōu)化需求迅速反饋。