TKE 使用者故事 - 作業幫 PB 級低成本日誌檢索服務

騰訊雲原生發表於2021-12-29

作者

呂亞霖,2019年加入作業幫,作業幫架構研發負責人,在作業幫期間主導了雲原生架構演進、推動實施容器化改造、服務治理、GO微服務框架、DevOps的落地實踐。

莫仁鵬,2020年加入作業幫,作業幫高階架構師,在作業幫期間,推動了作業幫雲原生架構演進,負責作業幫服務治理體系的設計和落地、服務感知體系建設以及自研mesh、MQproxy研發工作。

摘要

日誌是服務觀察的主要方式,我們依賴日誌去感知服務的執行狀態、歷史狀況;當發生錯誤時,我們又依賴日誌去了解現場,定位問題。日誌對研發工程師來說異常關鍵,同時隨著微服務的流行,服務部署越來越分散化,所以我們需要一套日誌服務來採集、傳輸、檢索日誌

基於這個情況,誕生了以 ELK 為代表的開源的日誌服務。

需求場景

在我們的場景下,高峰日誌寫入壓力大(每秒千萬級日誌條數);實時要求高:日誌處理從採集到可以被檢索的時間正常 1s 以內(高峰時期 3s);成本壓力巨大,要求儲存半年的日誌且可以回溯查詢(百 PB 規模)。

ElasticSearch 的不足

ELK 方案裡最為核心的就是 ElasticSearch, 它負責儲存和索引日誌, 對外提供查詢能力。Elasticsearch 是一個搜尋引擎, 底層依賴了 Lucene 的倒排索引技術來實現檢索, 並且通過 **shard **的設計拆分資料分片, 從而突破單機在儲存空間和處理效能上的限制

寫入效能

​ ElasticSearch 寫入資料需要對日誌索引欄位的倒排索引做更新,從而能夠檢索到最新的日誌。為了提升寫入效能,可以做聚合提交、延遲索引、減少 refersh 等等,但是始終要建立索引, 在日誌流量巨大的情況下(每秒 20GB 資料、千萬級日誌條數), 瓶頸明顯。離理想差距過大,我們期望寫入近乎準實時。

執行成本

​ ElasticSearch 需要定期維護索引、資料分片以及檢索快取, 這會佔用大量的 CPU 和記憶體,日誌資料是儲存在機器磁碟上,在需要儲存大量日誌且儲存很長時間時, 機器磁碟使用量巨大,同時索引後會帶來資料膨脹,進一步帶來成本提升。

對非格式化的日誌支援不好

​ ELK需要解析日誌以便為日誌項建立索引, 非格式化的日誌需要增加額外的處理邏輯來適配。存在很多業務日誌並不規範,且有收斂難度。

總結:日誌檢索場景是一個寫多讀少的場景, 在這樣的場景下去維護一個龐大且複雜的索引, 在我們看來其實是一個價效比很低的事情。如果採用 ElasticSearch 方案,經測算我們需要幾萬核規模叢集,仍然保證不了寫入資料和檢索效率,且資源浪費嚴重。

日誌檢索設計

面對這種情況, 我們不妨從一個不同的角度去看待日誌檢索的場景, 用一個更適合的設計來解決日誌檢索的需求, 新的設計具體有以下三個點:

日誌分塊

同樣的我們需要對日誌進行採集,但在處理日誌時我們不對日誌原文進行解析和索引,而是通過日誌時間、日誌所屬例項、日誌型別、日誌級別等日誌後設資料對日誌進行分塊。這樣檢索系統可以不對日誌格式做任何要求,並且因為沒有解析和建立索引(這塊開銷很大)的步驟, 寫入速度也能夠達到極致(只取決於磁碟的 IO 速度)。

簡單來說, 我們可以將一個例項產生的同一類日誌按時間順序寫入到一個檔案中, 並按時間維度對檔案拆分. 不同的日誌塊會分散在多臺機器上(我們一般會按照例項和型別等維度對日誌塊的儲存機器進行分片), 這樣我們就可以在多臺機器上對這些日誌塊併發地進行處理, 這種方式是支援橫向擴充套件的. 如果一臺機器的處理效能不夠, 橫向再擴充套件就行。

那如何對入日誌塊內的資料進行檢索呢?這個很簡單, 因為儲存的是日誌原文,可以直接使用 grep 相關的命令直接對日誌塊進行檢索處理。對開發人員來說, grep 是最為熟悉的命令, 並且使用上也很靈活, 可以滿足開發對日誌檢索的各種需求。因為我們是直接對日誌塊做追加寫入,不需要等待索引建立生效,在日誌刷入到日誌塊上時就可以被立刻檢索到, 保證了檢索結果的實時性

後設資料索引

接下來我們看看要如何對這麼一大批的日誌塊進行檢索。

首先我們當日志塊建立時, 我們會基於日誌塊的後設資料資訊搭建索引, 像服務名稱、日誌時間, 日誌所屬例項, 日誌型別等資訊, 並將日誌塊的儲存位置做為 value 一起儲存。通過索引日誌塊的後設資料,當我們需要對某個服務在某段時間內的某類日誌發起檢索時,就可以快速地找到需要檢索的日誌塊位置,併發處理。

索引的結構可以按需構建, 你可以將你關心的後設資料資訊放入到索引中, 從而方便快速圈定需要的日誌塊。因為我們只對日誌塊的後設資料做了索引, 相比於對全部日誌建立索引, 這個成本可以說降到了極低, 鎖定日誌塊的速度也足夠理想。

日誌生命週期與資料沉降

日誌資料以時間維度的方向可以理解為一種時序資料, 離當前時間越近的日誌會越有價值, 被查詢的可能性也會越高, 呈現一種冷熱分離的情況。而且冷資料也並非是毫無價值,開發人員要求回溯幾個月前的日誌資料也是存在的場景, 即我們的日誌需要在其生命週期裡都能夠對外提供查詢能力。

對於這種情況,如果將生命週期內的所有日誌塊都儲存在本地磁碟上, 無疑是對我們的機器容量提了很大的需求。對於這種日誌儲存上的需求,我們可以採用壓縮和沉降的手段來解決。

簡單來說,我們將日誌塊儲存分為本地儲存(磁碟)、遠端儲存(物件儲存)、歸檔儲存三個級別; 本地儲存負責提供實時和短期的日誌查詢(一天或幾個小時), 遠端儲存負責一定時期內的日誌查詢需求(一週或者幾周), 歸檔儲存負責日誌整個生命週期裡的查詢需求。

現在我們看看日誌塊在其生命週期裡是如何在多級儲存間流轉的, 首先日誌塊會在本地磁碟建立並寫入對應的日誌資料, 完成後會在本地磁碟保留一定時間(保留的時間取決於磁碟儲存壓力), 在儲存一定時間後, 它首先會被壓縮然後被上傳至遠端儲存(一般是物件儲存中的標準儲存型別), 再經過一段時間後日志塊會被遷移到歸檔儲存中儲存(一般是物件儲存中的歸檔儲存型別).

這樣的儲存設計有什麼好處呢? 如下面的多級儲存示意圖所示, 越往下儲存的資料量越大, 儲存介質的成本也越低, 每層大概為上一層的 1/3 左右, 並且資料是在壓縮後儲存的, 日誌的資料壓縮率一般可以達到10:1, 由此看歸檔儲存日誌的成本能在本地儲存的1%的左右, 如果使用了 SSD 硬碟作為本地儲存, 這個差距還會更大。

價格參考:

儲存介質 參考連結
本地盤 https://buy.cloud.tencent.com/price/cvm?regionId=8&zoneId=800002
物件儲存 https://buy.cloud.tencent.com/price/cos
歸檔儲存 https://buy.cloud.tencent.com/price/cos

那在多級儲存間又是如何檢索的呢? 這個很簡單, 對於本地儲存上的檢索, 直接在本地磁碟上進行即可。

如果檢索涉及到遠端儲存上的日誌塊, 檢索服務會將涉及到的日誌塊下載到本地儲存, 然後在本地完成解壓和檢索。因為日誌分塊的設計,日誌塊的下載同檢索一樣,我們可以在多臺機器上並行操作; 下載回本地的資料複製支援在本地快取後一定的時間後再刪除, 這樣有效期內對同一日誌塊的檢索需求就可以在本地完成而不需要再重複拉取一遍(日誌檢索場景裡多次檢索同樣的日誌資料還是很常見).

對於歸檔儲存, 在發起檢索請求前, 需要對歸檔儲存中的日誌塊發起取回操作, 取回操作一般耗時在幾分鐘左右, 完成取回操作後日志塊被取回到遠端儲存上,再之後的資料流轉就跟之前一致了。即開發人員如果想要檢索冷資料, 需要提前對日誌塊做歸檔取回的申請,等待取回完成後就可以按照熱資料速度來進行日誌檢索了。

檢索服務架構

在瞭解上面的設計思路後, 我們看看基於這套設計的日誌檢索服務是怎麼落地的.

日誌檢索服務分為以下幾個模組:

  • GD-Search

​ 查詢排程器, 負責接受查詢請求, 對查詢命令做解析和優化, 並從 Chunk Index 中獲取查詢範圍內日誌塊的地址, 最終生成分散式的查詢計劃

GD-Search 本身是無狀態的, 可以部署多個例項,通過負載均衡對外提供統一的接入地址。

  • Local-Search

​ 本地儲存查詢器, 負責處理 GD-Search 分配過來的本地日誌塊的查詢請求。

  • Remote-Search

​ 遠端儲存查詢器, 負責處理 GD-Search 分配過來的遠端日誌塊的查詢請求。

Remote-Search 會將需要的日誌塊從遠端儲存拉取到本地並解壓, 之後同 Local-Search 一樣在本地儲存上進行查詢。同時 Remote-Search 會將日誌塊的本地儲存地址更新到 Chunk Index 中,以便將後續同樣日誌塊的查詢請求路由到本地儲存上。

  • Log-Manager

​ 本地儲存管理器,負責維護本地儲存上日誌塊的生命週期。

Log-Manager 會定期掃描本地儲存上的日誌塊, 如果日誌塊超過本地儲存期限或者磁碟使用率到達瓶頸,則會按照策略將部分日誌塊淘汰(壓縮後上傳到遠端儲存, 壓縮演算法採用了 ZSTD), 並更新日誌塊在 Chunk Index 中的儲存資訊。

  • Log-Ingester

​ 日誌攝取器模組, 負責從日誌 kafka 訂閱日誌資料, 然後將日誌資料按時間維度和後設資料維度拆分, 寫入到對應的日誌塊中。在生成新的日誌塊同時, Log-Ingester 會將日誌塊的後設資料寫入 Chunk Index 中, 從而保證最新的日誌塊能夠被實時檢索到。

  • Chunk Index

​ 日誌塊後設資料儲存, 負責儲存日誌塊的後設資料和儲存資訊。當前我們選擇了 Redis 作為儲存介質, 在後設資料索引並不複雜的情況下, redis 已經能夠滿足我們索引日誌塊的需求, 並且基於記憶體的查詢速度也能夠滿足我們快速鎖定日誌塊的需求。

檢索策略

在檢索策略設計上, 我們認為檢索的返回速度是追求更快, 同時避免巨大的查詢請求進入系統。

我們認為日誌檢索一般有以下三種場景:

  1. 檢視最新的服務日誌。

  2. 檢視某個請求的日誌, 依據 logid 來查詢。

  3. 檢視某類日誌, 像訪問 mysql 的錯誤日誌, 請求下游服務的日誌等等。

在大部分場景下, 使用者是不需要所有匹配到的日誌, 拿一部分日誌足以處理問題。所以在查詢時使用者可以設定 limit 數量, 整個檢索服務在查詢結果滿足 limit設定的日誌數量時, 終止當前的查詢請求並將結果返回給前端。

另外 GD-Search 元件在發起日誌塊檢索時, 也會提前判斷檢索的日誌塊大小總和, 對於超限的大範圍檢索請求會做拒絕。(使用者可以調整檢索的時間範圍多試幾次或者調整檢索語句使其更有選擇性)

效能一覽

使用 1KB 每條的日誌進行測試, 總的日誌塊數量在10000左右, 本地儲存使用 NVME SSD 硬碟, 遠端儲存使用 S3 協議標準儲存.

• 寫入

​ 單核可支援 2W條/S的寫入速度, 1W 條/S的寫入速度約佔用 1~2G 左右的記憶體,可分散式擴充套件,無上限

• 查詢(全文檢索)

​ 基於本地儲存的 1TB 日誌資料查詢速度可在 3S 以內完成

​ 基於遠端儲存的 1TB 日誌資料查詢耗時在 10S 間。

成本優勢

在每秒千萬級寫入,百 PB 儲存上,我們使用十幾臺物理伺服器就可以保證日誌寫入和查詢。熱點資料在本地 nvme 磁碟上,次熱資料在物件存裡,大量日誌資料儲存在歸檔儲存服務上。

計算對比

​ 因為不需要建立索引,我們只需要千核級別就可以保證寫入,同時日誌索引是個寫多讀少的服務,千核可以保證百級別 QPS 查詢。

​ ES 在這個量級上需要投入幾萬核規模。來應對寫入效能和查詢瓶頸,但是仍不能保證寫入和查詢效率。

儲存對比

​ 核心是在保證業務需求下,使用更便宜的儲存介質(歸檔儲存 VS 本地磁碟)和更少的儲存資料(壓縮率 1/10vs 日誌資料索引膨脹)。能有兩個量級的差距。

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多幹貨!!

相關文章