金山云:基於 JuiceFS 的 Elasticsearch 溫冷熱數據管理實踐

  • 2022 年 11 月 18 日
  • 筆記

01 Elasticsearch 廣泛使用帶來的成本問題

Elasticsearch(下文簡稱「ES」)是一個分散式的搜索引擎,還可作為分散式資料庫來使用,常用於日誌處理、分析和搜索等場景;在運維排障層面,ES 組成的 ELK(Elasticsearch+ Logstash+ Kibana)解決方案,簡單易用、響應速度快,並且提供了豐富的報表;高可用方面, ES 提供了分散式和橫向擴展;數據層面,支援分片和多副本。

ES 的使用便捷,生態完整,在企業之中得到了廣泛的應用。 隨之而來的是物理資源和費用的增加,如何降低 ES 場景的成本成為了大家普遍關心的話題。

如何降低 ES 的成本

ES 的主要的成本是主機成本,主機成本又分為計算資源和存儲資源。

計算資源簡單理解就是 CPU 和記憶體,如果降低 CPU 和主機的數量,意味著計算能力會下降,因此通常會採用冷熱節點;熱節點使用高配的機器,冷節點使用低配的機器;比如 CPU 記憶體比從 1:4 降低為 1:8,如8C32G->4C32G。但是沒有降低記憶體,因為 ES 對記憶體有更高的需求,來提高響應速度。或者採用低頻的 CPU,更老的硬體。

存儲成本是遠大於計算成本的,是要重點考慮的成本。現在的存儲介質通常是 SSD 和 HDD 這種兩種介質,在雲廠商 SSD 的成本是 0.8元/G,HDD 的成本是 0.35元/G,對象存儲的價格是 0.12元/G。但前兩種設備是塊設備,提供的是文件系統的協議,但對象存儲支援是 S3 協議的,彼此之間不兼容。

如何將對象存儲和 ES 結合起來,我們調研了兩種方案。

第一種方案,修改 ES 存儲引擎,適配對象存儲調用。這種方式需要修改 ES 的源碼,團隊要投入很多的人力來做開發設計調研以及最後的驗證,投入產出比是非常低的。

第二種方案是將對象存儲作為磁碟來使用,將其掛載到作業系統。把 ES 分為 hot 和 warm 節點。hot節點存儲熱數據,掛載的是塊設備。warm 節點使用對象存儲。

02 對象存儲文件系統選型

文件系統選型的時候主要考慮了三個方面。第一個是功能,首先要滿足最基本的功能需求,第二個是性能,第三個是可靠性。我們調研了 s3fs、 goofys 和 JuiceFS。

性能方面, s3fs 和 goofys 在 read 和 write 方面沒有本地快取,其性能是依靠 s3 的性能來支撐的,這兩個文件系統整體的性能相比JuiceFS 會低一些。

最明顯的是 mv,對象存儲沒有 rename 操作,在對象存儲中進行 rename 操作就是一個 copy 加 delete,性能代價是非常大的。

ls 方面,對象存儲的存儲類型是 kv 存儲,不具備目錄語義,所以 ls 整個目錄結構對於 s3 來說,其實是對整個元數據的遍歷,調用代價非常大。在大數據的場景下,性能是非常低的,並且有一些特性功能是不支援的。

元數據方面,s3fs 和 goofys 沒有自己獨立的元數據,所有的元數據都是依賴於s3的,JuiceFS 有自己的獨立的元數據存儲,

易用性方面,這幾個產品都是非常簡單易用,通過一個簡單的命令就可以實現 s3的掛載;從可維護性來說,JuiceFS 有自己的獨立的元數據引擎,我們需要對元數據服務進行運維;從社區的角度來說,JuiceFS 的社區活躍度是最高的。基於以上綜合考慮,金山雲選擇了JuiceFS。

基於 JuiceFS 的測試

JuiceFS 產品介紹中第一句話就是 「像本地盤一樣使用對象存儲」,恰恰是我們做 ES 時所需要的功能。JuiceFS 已經集成了很多種對象存儲,金山雲的 KS3 也已經完整兼容,只需要再選一個元資料庫即可。

第二功能驗證。元資料庫常用的有 Redis,關係型據庫、 KV 資料庫等。我們從這三個層面,元資料庫支撐的數據量、響應速度、可運維性來做判斷。

從數據量上來說,TiKV 無疑是最高的,但是我們的設計初衷是讓每套 ES 集群獨立一個元資料庫實例,因此不同集群之間的元數據是不進行共享的,為了高可用彼此間需要互相隔離。

其次在響應速度方面, ES 把 JuiceFS 作為冷節點存儲,冷節點存儲的數據 IO 要比元數據的調用性能損耗更大,所以我們認為元數據的調用性能不是核心考慮的點。

從運維的角度來說,關係型資料庫大家都比較熟,開發人員都可以很輕鬆的上手,其次公司是有公司有 RDS For MySQL 產品,有專業的 DBA 團隊來負責運維,所以最終是選擇了MySQL 作為的元數據引擎。

JuiceFS 可靠性測試

元數據選型完以後,我們對 JuiceFS 進行了可靠性測試,JuiceFS 掛載到主機上只需要三步:

第一步,創建文件系統,我們要指定 bucket 來確定它的 AK、SK 以及元資料庫;

第二步,把文件系統 mount 到磁碟上;

第三步,把 ES軟鏈到 JuiceFS 的 mount 目錄上。

雖然設計初衷是將 JuiceFS 作為冷節點,但是在測試的過程中,我們想用一種極限的方式來壓測 JuiceFS。我們設計了兩種極限壓測。

第一個:將 JuiceFS + KS3 掛載到 hot 節上,把數據實時寫到 JuiceFS 。

ES 的寫入流程是先寫到 buffer 中也就是記憶體裡面,當記憶體滿或者是到達索引設定的時間閾值以後,會刷新到磁碟上,這時候會生成 ES 的 segment。它是由一堆的數據和元數據文件來構成的,每次刷新就會生成一系列的 segment,這時候就會產生頻繁的 IO 調用。

我們通過這種壓測的方式來測試 JuiceFS 整體的可靠性,同時 ES 本身它會有一些segment merge。這些場景在 warm 節點是不具備的,所以我們是想用一種極限的方式來壓測。

第二種策略,通過生命周期管理來做熱數據到冷數據的遷移。

測試的時候 JuiceFS1.0 還沒有發布,測試的過程中確實發現了問題,在實時寫的過程中會出現了數據損壞的情況,跟社區溝通後可以通過修改快取的大小來避免:

–attr-cache=0.1 屬性快取時長,單位秒 (默認值: 1)

–entry-cache=0.1 文件項快取時長,單位秒 (默認值: 1)

–dir-entry-cache=0.1 目錄項快取時長,單位秒 (默認值: 1)

這三個參數的快取默認是 1,把時長改成 0.1,它確實解決了索引損壞的問題,但是會帶來一些新的問題,因為元數據的快取和數據快取的時間變短,會導致在執行系統命令的時候,比如 curl 一個系統命令,查看索引數量或者集群狀態,正常的情況下,調用可能在秒級,而這種變化可能導致需要數 10 秒才能夠完成。

第二個問題就是寫入的 QPS 有明顯下降。我們可以看到監控圖中 Write QPS 非常不穩定,這並不代表 ES 真實的 QPS,因為監控圖中的 QPS 是通過兩次得到的 documents 數量來做差得到的,由於舊版 JuiceFS 存在一些內核快取問題,導致 ES 讀到了一些舊數據。我們把該問題回饋給了社區, JuiceFS 1.0 正式發布後問題得到解決。

我們就進行了新一輪的測試,新一輪的測試確定了 hot 節點 3 台,8C16G 500G SSD, warm 節點 2 台,4C16G 200G SSD,測試時長 1 周,每天寫入數據量 1TB(1 副本),1 天后轉到 warm 節點 。沒有再出現索引數據損壞情況,通過這次壓測沒有再出現之前遇到的問題,這就給了我們信心,接下來我們把整個的 ES 逐漸的往這方面來做遷移。

JuiceFS 數據存儲和對象存儲的差異

JuiceFS 有自己的元數據,所以在對象存儲上和 JuiceFS 當中看到的目錄結構是不一樣的。

JuiceFS 分為三層結構,chunk、slice、block,因此我們在對象存儲上面看到的是 JuiceFS 對文件做拆分之後的數據塊。但是所有的數據是通過 ES 來管理,所以這一點用戶不需要關注,只需要通過 ES 來執行所有的文件系統操作即可。JuiceFS 會恰當管理對象存儲中的數據塊。

經過這一系列的測試後, 金山雲將 JuiceFS 應用在日誌服務( Klog)中,為企業用戶提供一站式日誌類數據服務,實現了雲上的數據可以不出雲,直接就完成數據採集,存儲分析以及告警的一站式服務;雲下的數據提供了 SDK 客戶端,通過採集工具來實現數據上雲的整個整條鏈路,最後可以把數據投遞到 KS3 和 KMR,來實現數據的加工計算。

03 Elasticsearch冷熱數據管理

ES 有幾個常用概念: Node Role 、Index Lifecycle Management 、 Data Stream。

Node Role,節點角色。每一個 ES 節點會分配不同的角色,比如 master、data、ingest。重點介紹一下 data 節點,老版本是分為三種,就是 hot、warm、cold 節點,在最新的版本裡面增加了 freeze ,冷凍節點。

Index Lifecycle Management(ILM)我們分為了 4 個階段:

  • hot: 索引正在被頻繁更新和查詢。
  • warm:索引不再被更新,但查詢量一般。
  • cold: 索引不再被更新,並且很少被查詢。這些資訊仍然需要可搜索,但如果查詢速度較慢也沒關係。
  • delete: 索引不再需要,可以安全地刪除。

ES 官方提供了一個生命周期的管理工具,我們可以基於索引的大小,docs 數量的大小以及時間策略,把一個大的索引拆分成成多個小索引。一個大索引從管理運維查詢,它的開銷的代價是非常大的。生命周期管理功能方便我們更靈活地管理索引。

Data Stream 是在 7.9 版本提出推出了一個新功能,它是基於索引生命周期管理來實現了一個數據流寫入,可以很方便地處理時間序列數據。

在查詢多個索引時,通常是把這些索引合併在一起來查詢,我們可以使用 Data Stream,他就像一個別名一樣,可以自行路由到不同的索引裡面。Data Stream 對時序數據的存儲管理和查詢來說更友好,這個是來對 ES 的冷熱管理上面是來更近了一步,方便整個的運維管理。

合理規劃冷節點大小

當我們把冷數據放到對象存儲上時,會涉及到冷節點的管理,主要是分為三個方面:

第一:記憶體和 CPU 以及存儲空間。 記憶體的大小決定了分片的數量。我們通常會在 hot 節點會把物理記憶體按照一半一半進行劃分:
一半給 ES 的 JVM,另外一半是給 Lucene。Lucene 是 ES 的檢索引擎,為其分配足夠的記憶體,能提升 ES 查詢表現。因此相應地,我們在冷數據節點可以適當的把 JVM 記憶體,然後減少 Lucene 記憶體,不過 JVM 的記憶體不要超過 31G。

第二: CPU/記憶體比從前面提到的 1:4 降到 1:8。在存儲空間上使用了 JuiceFS 和對象存儲可以認為存儲空間是無限的,但因為它是掛在冷節點上的,雖然有無限的空間可以使用,但是受限於記憶體大小,所以這就決定了無限存儲空間的只是理想狀態。如果再擴大,整個 ES 的在冷節點的穩定性上面就會有比較大的隱患。

第三:存儲空間。 以 32G 記憶體 為例,合理的存儲空間為 6.4 TB。 可以通過擴大分片的數量來擴大空間,但在 hot 節點,分片數量是要嚴格控制的,因為需要考慮到 hot 節點的穩定性,在冷節點適當放大這個比例是可以的。

這裡需要重點考慮的因素有兩個,就是一個是穩定性,第二個數據恢復時長。因為當節點掛掉,比如 JuiceFS 進程掛掉,或者冷節點掛掉,或者運維的時候需要重新掛載,這時候就需要把所有的數據重新載入到 ES 裡面,將會在 KS3 產生大量的頻繁讀數據請求,如果數據量越多,那麼整個的 ES 的分片時間恢復時長會越長。

常用的索引分片管理方法

管理方法主要考慮三個方面:

  • shard 過大: 導致集群故障後恢復緩慢;容易造成數據寫熱點,導致 bulk queue 打滿,拒絕率上升;
  • shard 過小: 造成更多的 shard,佔用更多的元數據,影響集群穩定性;降低集群吞吐;
  • shard 過多: 造成更多的 segment,IO 資源浪費嚴重,降低查詢速度;佔用更多記憶體,影響穩定性。

數據在寫入的時候,整個的數據大小是不確定的,通常會先創建模板,先確定固定的分片的大小,確定分片的數量,然後再創建 mapping 以及創建索引。

這時候就可能會出現兩個問題,第一個就是分片過多,因為在預期的時候不知道到底要寫入到多少數據,有可能我創建的分片多,但是沒有更多的數據進來。

第二個就是創建的分片數量過少,會導致索引過大,這時候會需要把小的分片進行合併,需要把採用更長的時間做數據的 rotate,再把一些小的 segment 合併成更大的 segment,避免佔用更多的 IO 和記憶體。同時還需要刪除一些空索引,空索引雖然沒有數據,但是它會佔用記憶體。建議合理的分片大小是控制在 20~50g。

04 JuiceFS 使用效果及注意事項

以某線上集群為例,數據規模:每天寫入 5TB,數據儲存 30 天,熱數據儲存一周,節點數量:5 個熱節點,15 個冷節點。

採用 JuiceFS 後,熱節點保持不變,冷節點從 15 個降到了 10 個,同時我們用了一個 1TB 的機械硬碟做給 JuiceFS 來做快取。

可以看到在凌晨的時候會有大量對象存儲調用,這因為我們把整個的生命周期的管理操作放到了低峰期來運行。

JuiceFS 記憶體佔用通常會在幾百 MB,它在高峰期調用的時候會在不到 1.5G 以及它的 CPU 的佔用,表現無異常。

以下是 JuiceFS 的使用注意事項:
第一:不共用文件系統。 因為我們把 JuiceFS 掛載到冷節點上,那麼每一台機器上所看到的是一個全量的數據,更友好的方式是採用多個文件系統,每一個 ES 節點採用一個文件系統,這樣能做到隔離,但是會帶來相應的管理問題。

我們最終選定的是一套 ES 對應一個文件系統的模式,這個實踐帶來的問題是:每一個節點都會看到全量數據,這時候就會容易有一些誤操作。如果用戶要在上面做一些 rm ,有可能會把其他機器上的數據刪掉了,但是綜合考慮我們是在不同集群之間不共享文件系統,而在同一個集群里,我們還是應該平衡管理和運維,所以採用了一套 ES 對應一個 JuiceFS 文件系統 的模式。

第二: 手動遷移數據到 warm 節點。 在索引生命周期管理,ES 會有一些策略,會把熱節點的數據遷到冷節點。策略在執行時,有可能是在業務高峰期,這時候會對熱節點產生 IO, 然後把數據 copy 到冷節點,再把熱節點的數據刪除,整個熱節點的系統的代價是比較大的,所以我們是採用的手動,來控制哪些索引什麼時間遷移到冷節點。

第三:低峰錯期進行索引遷移。

第四: 避免大索引。 在刪除大索引時,它的 CPU 以及 IO 性能要比熱節點要差一些,這時候會導致冷節點和 master 失聯,失聯以後就會出現了重新載入數據,然後重新恢複數據,整個就相當於 ES 故障了,節點故障了,這個代價是非常大的。

第五:合理的分片大小。

第六: 關閉回收站。在對象存儲上, JuiceFS 默認保存一天的數據,但在 ES 的場景下是不需要的。

還有一些其他涉及到一些大量 IO 的操作,要在 hot 節點完成。比如索引的合併、快照的恢復、以及分片的減少、索引以及數據的刪除等,這些操作如果發生在冷節點,會導致 master 節點失聯。 雖然對象存儲成本比較低,但是頻繁的 IO 調用成本會升高,對象存儲會要按照 put 和 get 的調用次數來收費,因此需要把這些大量的操作來放到熱節點上,只供業務側冷節點來做一些查詢。

如有幫助的話歡迎關注我們項目 Juicedata/JuiceFS 喲! (0ᴗ0✿)