滴滴 Elasticsearch 多集羣架構實踐

出品 | 滴滴技術
作者 |魏子珺

圖片描述

Elasticsearch 是基於 Lucene 實現的分佈式搜索引擎,提供了海量數據實時檢索和分析能力。Elastic 公司開源的一系列產品組成的Elastic Stack,可以爲日誌服務、搜索引擎、系統監控等提供簡單、易用的解決方案。

滴滴 Elasticsearch 簡介
滴滴2016年初開始構建Elasticsearch平臺,如今已經發展到超過3500+Elasticsearch實例,超過5PB的數據存儲,峯值寫入tps超過了2000w/s的超大規模。

Elasticsearch在滴滴有着非常豐富的使用場景,例如線上核心的打車地圖搜索,客服、運營的多維度查詢,滴滴日誌服務等近千個平臺用戶。

超大的規模和豐富的場景給滴滴Elasticsearch平臺帶來了極大的挑戰,我們在這期間積累了豐富經驗,也取得了一些成果。本文給大家分享下滴滴在Elasticsearch多集羣架構的實踐。

單集羣架構瓶頸
介紹單集羣架構瓶頸前,先來看下滴滴Elasticsearch單集羣的架構。

滴滴Elasticsearch單集羣架構

圖片描述

滴滴在單集羣架構的時候,寫入和查詢就已經通過Sink服務和Gateway服務管控起來。

| Sink服務
滴滴幾乎所有寫入Elasticsearch的數據都是經由kafka消費入到Elasticsearch。kafka的數據包括業務log數據、mysql binlog數據和業務自主上報的數據,Sink服務將這些數據實時消費入到Elasticsearch。

最初設計Sink服務是想對寫入Elasticsearch集羣進行管控,保護Elasticsearch集羣,防止海量的數據寫入拖垮Elasticsearch,之後我們也一直沿用了Sink服務,並將該服務從Elasticsearch平臺分離出去,成立滴滴Sink數據投遞平臺,可以從kafka或者MQ實時同步數據到Elasticsearch、HDFS、Ceph等多個存儲服務。

有了多集羣架構後,Elasticsearch平臺可以消費一份MQ數據寫入多個Elasticsearch集羣,做到集羣級別的容災,還能通過MQ回溯數據進行故障恢復。

| Gateway服務
所有業務的查詢都是經過Gateway服務,Gateway服務實現了Elasticsearch的http restful和tcp協議,業務方可以通過Elasticsearch各語言版本的sdk直接訪問Gateway服務,Gateway服務還實現了SQL接口,業務方可以直接使用SQL訪問Elasticsearch平臺。

Gateway服務最初提供了應用權限的管控,訪問記錄,限流、降級等基本能力,後面隨着平臺演進,Gateway服務還提供了索引存儲分離、DSL級別的限流、多集羣災備等能力。

| Admin服務
整個Elasticsearch平臺由Admin服務統一管控起來。Admin服務提供了索引的生命週期管理,索引容量自動規劃,索引健康分,集羣監控等豐富的平臺能力,以及爲Sink、Gateway服務提供索引、權限等元數據信息。

Elasticsearch單集羣瓶頸
隨着滴滴Elasticsearch平臺規模的快速發展,Elasticsearch集羣越來越大,最大的時候,是由幾百臺物理機組成集羣,當時集羣共 3000+ 的索引,超過了 50000 個 shard,集羣總容量達到了PB級別。超大的Elasticsearch集羣面臨了很大的穩定性風險,這些風險主要來自於以下三個方面:

  • Elasticsearch架構瓶頸
  • 索引資源共享風險
  • 業務場景差異大

Elasticsearch架構瓶頸
Elasticsearch架構在集羣變大到一定的規模會遇到瓶頸,瓶頸主要跟Elasticsearch任務處理模型有關。

Elasticsearch看起來是p2p架構,但實際上,仍然是中心化的分佈式架構。整個集羣只有一個active master。master負責整個集羣的元數據管理。集羣的所有元數據保存在ClusterState對象中,主要包括全局的配置信息、索引信息和節點信息。只要元數據發生修改,都得由master完成。

Elasticsearchmaster的任務處理是單線程完成的,每次處理任務,涉及到ClusterState的改動,都會將最新的ClusterState對象publish給集羣的全部節點,並阻塞等待全部節點接受到變更消息,處理完變更任務後,才完成本次任務。

這樣的架構模型導致在集羣規模變大的時候出現很嚴重的穩定性風險。

  • 如果有節點假死,比如jvm內存被打滿,進程還存活着,響應master任務時間會很長,影響單個任務的完成時間。
  • 有大量恢復任務的時候,由於master是單線程處理的,所有任務需要排隊處理,產生大量的pending_tasks。恢復時間變得很長。
  • Elasticsearch的任務分了優先級,例如put-mapping任務優先級低於創建、恢復索引,如果一些業務上低優先級索引在恢復,正常索引有新字段寫入時會被阻塞。
  • master任務處理模型,在任務執行完成後,會回調大量listener處理元數據變更。其中有些回調邏輯在索引、shard膨脹後,會出現處理緩慢的問題,當shard膨脹到5-6w時,一些任務處理需要8-9s的時間,嚴重影響了集羣的恢復能力。

針對這些問題,Elasticsearch也在不斷優化,針對相同類型的任務,比如put-mapping任務,master會一次性處理所有堆積在隊列裏的相同任務。ClusterState對象只傳遞diff內容,優化回調listener模塊的處理耗時環節等等。

但是由於整個集羣的任務都集中在一個master的一個線程中處理,在線程中需要同步元數據變更給集羣的每個節點,並阻塞等待全部節點同步完成。這個模型在集羣規模不斷膨脹時,穩定性會不斷下降。

| 索引資源共享風險
Elasticsearch索引是由多個shard組成,master會動態給這些shard分配節點資源。不同的索引會存在資源混部的情況。
圖片描述

Elasticsearch通過Shard Allocation Awareness的設計,可以將集羣的節點按集合劃分成不同的rack。在分配索引時可以指定rack列表,這樣索引就只會分配在指定rack對應的節點列表中,從而做到物理資源的隔離。

但是實際使用中,很多容量小的索引由於佔用資源有限,會混部在一些節點中。這種情況下,會因爲個別索引的查詢、寫入量飆升,而影響到其他索引的穩定性。如果出現了節點故障,就會影響到整個集羣的穩定性。

整個集羣master、clientnode資源是共享的,master風險前面已經單獨提及,clientnode共享帶來的gc、抖動、異常問題都會影響到集羣內的全部索引。

| 業務場景差異大
Elasticsearch適用的業務場景差異特別大。

  • 針對線上核心的入口搜索,一般按城市劃分索引後,索引容量不大,數據沒有實時寫入或者實時寫入tps很小,比如地圖poi數據採用離線更新的方式,外賣商家、菜品寫入量也很小。但是查詢的qps很高,查詢對rt的平均時間和抖動情況要求很高。
  • 針對日誌檢索的的場景,實時寫入量特別大,有些索引甚至超過了100w/s的tps,該場景對吞吐量要求很高,但對查詢qps和查詢rt要求不高。
  • 針對binlog數據的檢索,寫入量相比日誌會小很多,但是對查詢的複雜度、qps和rt有一定的要求。
  • 針對監控、分析類的場景,聚合查詢需求會比較多,對Elasticsearch內存壓力較大,容易引起節點的抖動和gc。

這些場景各異,穩定性、性能要求各不相同的場景,一個Elasticsearch集羣即使使用各種優化手段,很難全部滿足需求,最好的方式還是按業務場景劃分Elasticsearch集羣。

多集羣挑戰
正是單集羣面臨了非常大的穩定性風險,我們開始規劃多集羣的架構。我們在設計多集羣方案的時候,期望對業務方是零感知的。

寫入還是經過kafka,Sink服務可以將不同topic的數據入到不同的Elasticsearch集羣。查詢繼續通過Gateway服務,而且業務方仍然像之前一樣傳遞索引名稱,而無需感知到平臺內部的索引分佈。所有的索引在不同集羣的分佈細節,均由Gateway服務屏蔽。

整個改造最大的挑戰在於查詢方式的兼容。Elasticsearch查詢索引的方式非常靈活,可以支持*號作爲通配符匹配。這樣一個索引query可能查詢的是多個索引,比如有如下3個索引:

  • index_a
  • index_b
  • index_c

使用index*查詢的時候,可以同時查詢到index_a、index_b、index_c三個索引。 Elasticsearch這種實現方式非常簡單,由於一次query最終查詢的是多個shard的數據,所以無論對於具體的索引,還是模糊的索引,都是先根據索引名稱得到shard列表,再將多個shard的query結果merge到一起返回。

這樣的使用方式,對於多集羣方案就會遇到問題,比如index_a在A集羣,index_b在B集羣、index_c在C集羣,對於index*的query,就無法在一個集羣上完成。

tribenode介紹

經過調研,我們發現Elasticsearchtribenode特性可以很好的滿足多集羣查詢的特性。tribenode的實現非常巧妙。org.elasticsearch.tribe包下只有三個文件,核心類是TribeService。tribenode的核心原理就是merge每個集羣的ClusterState對象成一個公共的ClusterState對象,ClusterState包含了索引、shard和節點數據分佈表。而Elasticsearch的工作邏輯都是基於ClusterState元數據驅動的,所以對外看起來就是一個包含全部索引的的clientnode。

圖片描述

tribenode通過配置多個Elasticsearch集羣地址,然後以clientnode角色分別連接每個集羣,每個集羣看起來會多了一個clientnode。tribenode通過該clientnode角色獲取到集羣的ClusterState信息,並綁定listener監聽ClusterState變化。tribenode將獲取的所有集羣的ClusterState信息merge到一起,形成一個對外部訪問使用的ClusterState對象,對外提供服務。tribenode除了註冊listener和merge ClusterState,其他的所有邏輯都是複用了clientnode的代碼。

可以看到tribenode的優點:

  • 能夠滿足多集羣訪問的需求,對外使用是透明的。
  • 實現的簡單、優雅,可靠性有保證。

同時tribenode有些不足的地方:

  • tribenode必須以clientnode加入到每個Elasticsearch集羣,master的變更任務必須等待tribenode的迴應才能繼續,可能影響到原集羣的穩定性。
  • tribenode不會持久化ClusterState對象,重啓時需要從每個Elasticsearch集羣獲取元數據。而在獲取元數據期間,tribenode就已經能夠提供訪問,會導致查詢到還在初始化中的集羣索引訪問失敗。
  • tribenode連接的集羣多了,初始化會變得很慢。針對該缺陷,我們平臺在重啓某個tribenode集羣時,將Gateway訪問該集羣的全部流量切到備份tribenode集羣解決。

如果多個集羣有相同的索引名稱,tribenode只能設置一種perfer規則:隨機、丟棄、prefer指定集羣。這可能帶來查到不符合預期的異常。滴滴Elasticsearch平臺通過統一管控索引,避免了同一個索引名稱出現在tribenode連接的多個集羣中。

正是tribenode有了這些瑕疵,Elasticsearch在高版本引入了Cross ClusterSearch的設計,Cross Cluster不會以節點的形式連接到其他集羣,只是將請求代理。目前我們還在評估Cross Cluster的方案,這裏不展開介紹。

多集羣架構拓撲
最終改造後,我們的集羣架構拓撲如下:

圖片描述

按照不同的應用場景,平臺將Elasticsearch集羣劃分成四種類型,Log集羣、Binlog集羣、文檔數據集羣、獨立集羣。公共集羣一般最多100臺datanode爲基準組成一個集羣。我們利用滴滴雲實現了集羣的自動化部署和彈性擴縮容,可以很方便的水平擴展集羣。

Elasticsearch集羣前面是多組tribenode集羣,主要是爲了解決tribenode的穩定性問題。

Gateway會同時連接tribenode集羣和Elasticsearch集羣,根據應用訪問的索引列表,配置應用訪問的集羣名稱,Gateway根據集羣名稱,將請求代理到指定集羣訪問,如果訪問的是tribenode集羣,則該應用可以訪問到多個集羣的索引。

Admin服務則管控了所有的Elasticsearch集羣,以及索引和集羣的對應關係。一系列功能都針對多集羣做了改造。

Sink服務已經從Elasticsearch平臺分離出去,成立DSink數據投遞平臺,DSink Manager負責管理DSink節點,DSink Manager從Elasticsearch Admin服務獲取索引的元數據信息,下發給對應的DSink節點。

多集羣架構實踐總結

| 多集羣架構收益
Elasticsearch多集羣架構改造給Elasticsearch平臺帶來了如下收益:

  • Elasticsearch平臺的隔離性可以從物理節點級別上升到Elasticsearch集羣級別。對於核心的線上應用,可以使用獨立的Elasticsearch集羣支持。
  • 不同類型的數據按集羣劃分,避免相互影響,減小了故障的影響面,對平臺穩定性帶來極大的提升。
  • Elasticsearch平臺的擴展能力進一步提升,通過新增集羣可以很好的做到水平擴展。
  • 多集羣架構最終做到了對業務方無感知,業務看起來,Elasticsearch平臺就像一個無限大的Elasticsearch集羣,而無需感知索引真實的集羣分佈。

| 多集羣架構實踐經驗
滴滴Elasticsearch平臺多集羣的架構已經演進了一年半時間,這期間也遇到一些多集羣架構帶來的挑戰。
tribenode穩定性挑戰:

  • 隨着集羣數量越來越多,前面提到的tribenode不足越來越明顯,比如初始化的時間越來越長等等。我們採取的應對策略是部署多組tribenode集羣,有幾組連接全量的集羣,互爲災備,有幾組只連接核心的一些集羣,用作更爲重要的跨集羣訪問場景。
  • tribenode的ClusterState元數據包含了太多的索引和shard,Elasticsearch的search邏輯在有些case處理下容易出現耗時過長的情況。Elasticsearch在client接收到search請求時,是在netty的io線程中完成請求轉發給每個shard的,低版本的Elasticsearch還沒有限制一次query的shard數量,在一些複雜的模糊索引匹配shard的邏輯中,以及給每個shard發送query請求時,會出現較高的耗時,可能有超過1-2s的case,這會影響到該netty worker上的其他的請求,造成部分響應飆高的情況。我們優化了tribenode search流程中一些索引、shard膨脹之後的耗時邏輯,解決了該問題。

多集羣配置、版本統一的挑戰:

  • 在只有一個集羣的時候,平臺只用維護一份集羣的配置和版本。當集羣數量增多後,不同集羣間的_cluster
    settings信息會出現部分差異,這些差異,可能會導致集羣間的負載不均,恢復速度過快或者過慢等問題,每個集羣還有一份基礎的索引模板配置,這裏面也出現了部分差異。這個問題目前我們還在解決中,我們計劃將Admin服務分離成索引管理服務和集羣管理服務,集羣管理會專注於集羣版本、配置、部署、擴容、監控等方面對Elasticsearch集羣進行更全面的管控。
  • 我們做的一些Elasticsearch源碼優化,會先後在部分集羣上線,這樣導致了集羣間的版本混亂的問題。我們的解決方案是在Elasticsearch和Lucene內增加內部的版本號,通過公司內部的發佈系統,發佈Elasticsearch的更新,後續集羣管理服務會將集羣的版本管理起來。

多集羣間容量均衡的挑戰:

  • 我們主要從跨集羣索引遷移和容量規劃解決集羣間容量均衡的挑戰,在單Elasticsearch集羣的時候,數據遷移可以依賴Elasticsearch的rebalance能力完成。在使用多集羣架構後,平臺內部的Elasticsearch集羣會出現資源分配不均的問題,例如有些索引容量增長的很快,導致所在集羣的資源緊張,有些索引數據減少,不需要佔用太多資源,導致集羣資源空閒。於是產生了索引跨集羣遷移的需求。針對這個需求,我們通過給索引添加版本號,解決了索引跨集羣遷移問題。之後我們有文章會詳細的介紹該方案。
  • 滴滴Elasticsearch平臺實現了索引容量的自動規劃,解決了集羣間的容量均衡。Elasticsearch平臺可以動態的規劃索引的容量。當一個集羣容量規劃不足時,平臺可以動態的遷移一部分索引到空閒的集羣中。新的索引接入需求會優先接入在空閒的集羣資源中。滴滴Elasticsearch平臺是如何實現索引容量的自動規劃,也請期待後續的分享。

總結
滴滴的多集羣架構,最初是爲了解決Elasticsearch單集羣架構的瓶頸。爲了支持多集羣架構,後面的很多組件都需要考慮連接多個集羣的場景,給平臺架構帶來了一定的複雜性。但是多Elasticsearch集羣帶來的穩定性和隔離性的提升,它所帶來的收益遠遠大於架構的複雜性。改造成多集羣架構後,我們扛住了Elasticsearch平臺規模爆炸式增長,Elasticsearch平臺的規模翻了5倍多,多集羣架構很好的支撐了業務的快速發展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章