天天看點

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

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

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

日志是服務觀察的主要方式,我們依賴日志去感覺服務的運作狀态、曆史狀況;當發生錯誤時,我們又依賴日志去了解現場,定位問題。日志對研發工程師來說異常關鍵,同時随着微服務的流行,服務部署越來越分散化,是以我們需要一套日志服務來采集、傳輸、檢索日志

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

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

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

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

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

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

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

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

同樣的我們需要對日志進行采集,但在處理日志時我們不對日志原文進行解析和索引,而是通過日志時間、日志所屬執行個體、日志類型、日志級别等日志中繼資料對日志進行分塊。這樣檢索系統可以不對日志格式做任何要求,并且因為沒有解析和建立索引(這塊開銷很大)的步驟, 寫入速度也能夠達到極緻(隻取決于磁盤的 IO 速度)。

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

簡單來說, 我們可以将一個執行個體産生的同一類日志按時間順序寫入到一個檔案中, 并按時間次元對檔案拆分. 不同的日志塊會分散在多台機器上(我們一般會按照執行個體和類型等次元對日志塊的存儲機器進行分片), 這樣我們就可以在多台機器上對這些日志塊并發地進行處理, 這種方式是支援橫向擴充的. 如果一台機器的處理性能不夠, 橫向再擴充就行。

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

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

首先我們當日志塊建立時, 我們會基于日志塊的中繼資料資訊搭建索引, 像服務名稱、日志時間, 日志所屬執行個體, 日志類型等資訊, 并将日志塊的存儲位置做為 value 一起存儲。通過索引日志塊的中繼資料,當我們需要對某個服務在某段時間内的某類日志發起檢索時,就可以快速地找到需要檢索的日志塊位置,并發處理。

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

索引的結構可以按需建構, 你可以将你關心的中繼資料資訊放入到索引中, 進而友善快速圈定需要的日志塊。因為我們隻對日志塊的中繼資料做了索引, 相比于對全部日志建立索引, 這個成本可以說降到了極低, 鎖定日志塊的速度也足夠理想。

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

對于這種情況,如果将生命周期内的所有日志塊都儲存在本地磁盤上, 無疑是對我們的機器容量提了很大的需求。對于這種日志存儲上的需求,我們可以采用壓縮和沉降的手段來解決。

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

簡單來說,我們将日志塊存儲分為本地存儲(磁盤)、遠端存儲(對象存儲)、歸檔存儲三個級别; 本地存儲負責提供實時和短期的日志查詢(一天或幾個小時), 遠端存儲負責一定時期内的日志查詢需求(一周或者幾周), 歸檔存儲負責日志整個生命周期裡的查詢需求。

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

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

價格參考:

存儲媒體

參考連結

本地盤

https://buy.cloud.tencent.com/price/cvm?regionId=8&zoneId=800002

對象存儲

https://buy.cloud.tencent.com/price/cos

歸檔存儲

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

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

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

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

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

日志檢索服務分為以下幾個子產品:

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 已經能夠滿足我們索引日志塊的需求, 并且基于記憶體的查詢速度也能夠滿足我們快速鎖定日志塊的需求。

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

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

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

檢視最新的服務日志。

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

檢視某類日志, 像通路 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 日志資料索引膨脹)。能有兩個量級的差距。

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆号,及時擷取更多幹貨!!
TKE 使用者故事 - 作業幫 PB 級低成本日志檢索服務

繼續閱讀