打造雲原生大型分佈式監控系統(二): Thanos 架構詳解

概述

之前在 大規模場景下 Prometheus 的優化手段 中,我們想盡 "千方百計" 才好不容易把 Prometheus 優化到適配大規模場景,部署和後期維護麻煩且複雜不說,還有很多不完美的地方,並且還無法滿足一些更高級的訴求,比如查看時間久遠的監控數據,對於一些時間久遠不常用的 "冷數據",最理想的方式就是存到廉價的對象存儲中,等需要查詢的時候能夠自動加載出來。

Thanos (沒錯,就是滅霸) 可以幫我們簡化分佈式 Prometheus 的部署與管理,並提供了一些的高級特性:全局視圖長期存儲高可用。下面我們來詳細講解一下。

Thanos 架構

這是官方給出的架構圖:

這張圖中包含了 Thanos 的幾個核心組件,但並不包括所有組件,為了便於理解,我們先不細講,簡單介紹下圖中這幾個組件的作用:

  • Thanos Query: 實現了 Prometheus API,將來自下游組件提供的數據進行聚合最終返回給查詢數據的 client (如 grafana),類似數據庫中間件。
  • Thanos Sidecar: 連接 Prometheus,將其數據提供給 Thanos Query 查詢,並且/或者將其上傳到對象存儲,以供長期存儲。
  • Thanos Store Gateway: 將對象存儲的數據暴露給 Thanos Query 去查詢。
  • Thanos Ruler: 對監控數據進行評估和告警,還可以計算出新的監控數據,將這些新數據提供給 Thanos Query 查詢並且/或者上傳到對象存儲,以供長期存儲。
  • Thanos Compact: 將對象存儲中的數據進行壓縮和降低採樣率,加速大時間區間監控數據查詢的速度。

架構設計剖析

如何理解 Thanos 的架構設計的?我們可以自己先 YY 一下,要是自己來設計一個分佈式 Prometheus 管理應用,會怎麼做?

Query 與 Sidecar

首先,監控數據的查詢肯定不能直接查 Prometheus 了,因為會存在許多個 Prometheus 實例,每個 Prometheus 實例只能感知它自己所採集的數據。我們可以比較容易聯想到數據庫中間件,每個數據庫都只存了一部分數據,中間件能感知到所有數據庫,數據查詢都經過數據庫中間件來查,這個中間件收到查詢請求再去查下游各個數據庫中的數據,最後將這些數據聚合匯總返回給查詢的客戶端,這樣就實現了將分佈式存儲的數據集中查詢。

實際上,Thanos 也是使用了類似的設計思想,Thanos Query 就是這個 "中間價" 的關鍵入口。它實現了 Prometheus 的 HTTP API,能夠 "看懂" PromQL。這樣,查詢 Prometheus 監控數據的 client 就不直接查詢 Prometheus 本身了,而是去查詢 Thanos Query,Thanos Query 再去下游多個存儲了數據的地方查數據,最後將這些數據聚合去重後返回給 client,也就實現了分佈式 Prometheus 的數據查詢。

那麼 Thanos Query 又如何去查下游分散的數據呢?Thanos 為此抽象了一套叫 Store API 的內部 gRPC 接口,其它一些組件通過這個接口來暴露數據給 Thanos Query,它自身也就可以做到完全無狀態部署,實現高可用與動態擴展。

這些分散的數據可能來自哪些地方呢?首先,Prometheus 會將採集的數據存到本機磁盤上,如果我們直接用這些分散在各個磁盤上的數據,可以給每個 Prometheus 附帶部署一個 Sidecar,這個 Sidecar 實現 Thanos Store API,當 Thanos Query 對其發起查詢時,Sidecar 就讀取跟它綁定部署的 Prometheus 實例上的監控數據返回給 Thanos Query。

由於 Thanos Query 可以對數據進行聚合與去重,所以可以很輕鬆實現高可用:相同的 Prometheus 部署多個副本(都附帶 Sidecar),然後 Thanos Query 去所有 Sidecar 查數據,即便有一個 Prometheus 實例掛掉過一段時間,數據聚合與去重後仍然能得到完整數據。

這種高可用做法還彌補了我們上篇文章中用負載均衡去實現 Prometheus 高可用方法的缺陷:如果其中一個 Prometheus 實例掛了一段時間然後又恢復了,它的數據就不完整,當負載均衡轉發到它上面去查數據時,返回的結果就可能會有部分缺失。

不過因為磁盤空間有限,所以 Prometheus 存儲監控數據的能力也是有限的,通常會給 Prometheus 設置一個數據過期時間 (默認15天) 或者最大數據量大小,不斷清理舊數據以保證磁盤不被撐爆。因此,我們無法看到時間比較久遠的監控數據,有時候這也給我們的問題排查和數據統計造成一些困難。

對於需要長期存儲的數據,並且使用頻率不那麼高,最理想的方式是存進對象存儲,各大雲廠商都有對象存儲服務,特點是不限制容量,價格非常便宜。

Thanos 有幾個組件都支持將數據上傳到各種對象存儲以供長期保存 (Prometheus TSDB 數據格式),比如我們剛剛說的 Sidecar:

Store Gateway

那麼這些被上傳到了對象存儲里的監控數據該如何查詢呢?理論上 Thanos Query 也可以直接去對象存儲查,但會讓 Thanos Query 的邏輯變的很重。我們剛才也看到了,Thanos 抽象出了 Store API,只要實現了該接口的組件都可以作為 Thanos Query 查詢的數據源,Thanos Store Gateway 這個組件也實現了 Store API,向 Thanos Query 暴露對象存儲的數據。Thanos Store Gateway 內部還做了一些加速數據獲取的優化邏輯,一是緩存了 TSDB 索引,二是優化了對象存儲的請求 (用儘可能少的請求量拿到所有需要的數據)。

這樣就實現了監控數據的長期儲存,由於對象存儲容量無限,所以理論上我們可以存任意時長的數據,監控歷史數據也就變得可追溯查詢,便於問題排查與統計分析。

Ruler

有一個問題,Prometheus 不僅僅只支持將採集的數據進行存儲和查詢的功能,還可以配置一些 rules:

  1. 根據配置不斷計算出新指標數據並存儲,後續查詢時直接使用計算好的新指標,這樣可以減輕查詢時的計算壓力,加快查詢速度。
  2. 不斷計算和評估是否達到告警閥值,當達到閥值時就通知 AlertManager 來觸發告警。

由於我們將 Prometheus 進行分佈式部署,每個 Prometheus 實例本地並沒有完整數據,有些有關聯的數據可能存在多個 Prometheus 實例中,單機 Prometheus 看不到數據的全局視圖,這種情況我們就不能依賴 Prometheus 來做這些工作,Thanos Ruler 應運而生,它通過查詢 Thanos Query 獲取全局數據,然後根據 rules 配置計算新指標並存儲,同時也通過 Store API 將數據暴露給 Thanos Query,同樣還可以將數據上傳到對象存儲以供長期保存 (這裡上傳到對象存儲中的數據一樣也是通過 Thanos Store Gateway 暴露給 Thanos Query)。

看起來 Thanos Query 跟 Thanos Ruler 之間會相互查詢,不過這個不衝突,Thanos Ruler 為 Thanos Query 提供計算出的新指標數據,而 Thanos Query 為 Thanos Ruler 提供計算新指標所需要的全局原始指標數據。

至此,Thanos 的核心能力基本實現了,完全兼容 Prometheus 的情況下提供數據查詢的全局視圖,高可用以及數據的長期保存。

看下還可以怎麼進一步做下優化呢?

Compact

由於我們有數據長期存儲的能力,也就可以實現查詢較大時間範圍的監控數據,當時間範圍很大時,查詢的數據量也會很大,這會導致查詢速度非常慢。通常在查看較大時間範圍的監控數據時,我們並不需要那麼詳細的數據,只需要看到大致就行。Thanos Compact 這個組件應運而生,它讀取對象存儲的數據,對其進行壓縮以及降採樣再上傳到對象存儲,這樣在查詢大時間範圍數據時就可以只讀取壓縮和降採樣後的數據,極大地減少了查詢的數據量,從而加速查詢。

再看架構圖

上面我們剖析了官方架構圖中各個組件的設計,現在再來回味一下這張圖:

理解是否更加深刻了?

另外還有 Thanos Bucket 和 Thanos Checker 兩個輔助性的工具組件沒畫出來,它們不是核心組件,這裡也就不再贅述。

Sidecar 模式與 Receiver 模式

前面我們理解了官方的架構圖,但其中還缺失一個核心組件 Thanos Receiver,因為它是一個還未完全發佈的組件。這是它的設計文檔: https://thanos.io/proposals/201812_thanos-remote-receive.md/

這個組件可以完全消除 Sidecar,所以 Thanos 實際有兩種架構圖,只是因為沒有完全發佈,官方的架構圖只給的 Sidecar 模式。

Receiver 是做什麼的呢?為什麼需要 Receiver?它跟 Sidecar 有什麼區別?

它們都可以將數據上傳到對象存儲以供長期保存,區別在於最新數據的存儲。

由於數據上傳不可能實時,Sidecar 模式將最新的監控數據存到 Prometheus 本機,Query 通過調所有 Sidecar 的 Store API 來獲取最新數據,這就成一個問題:如果 Sidecar 數量非常多或者 Sidecar 跟 Query 離的比較遠,每次查詢 Query 都調所有 Sidecar 會消耗很多資源,並且速度很慢,而我們查看監控大多數情況都是看的最新數據。

為了解決這個問題,Thanos Receiver 組件被提出,它適配了 Prometheus 的 remote write API,也就是所有 Prometheus 實例可以實時將數據 push 到 Thanos Receiver,最新數據也得以集中起來,然後 Thanos Query 也不用去所有 Sidecar 查最新數據了,直接查 Thanos Receiver 即可。另外,Thanos Receiver 也將數據上傳到對象存儲以供長期保存,當然,對象存儲中的數據同樣由 Thanos Store Gateway 暴露給 Thanos Query。

有同學可能會問:如果規模很大,Receiver 壓力會不會很大,成為性能瓶頸?當然設計這個組件時肯定會考慮這個問題,Receiver 實現了一致性哈希,支持集群部署,所以即使規模很大也不會成為性能瓶頸。

總結

本文詳細講解了 Thanos 的架構設計,各個組件的作用以及為什麼要這麼設計。如果仔細看完,我相信你已經 get 到了 Thanos 的精髓,不過我們還沒開始講如何部署與實踐,實際上在騰訊雲容器服務的多個產品的內部監控已經在使用 Thanos 了,比如 TKE (公有雲 k8s)、TKEStack (私有雲 k8s)、EKS (Serverless k8s)。 下一篇我們將介紹 Thanos 的部署與最佳實踐,敬請期待。