Prometheus 和 它的監控需求朋友們 ;)

這篇博文的面向羣體是 還不太瞭解 Prometheus 和 想要開始使用 Prometheus 的人羣.html

本文想作的事是 想盡力講清楚 Prometheus 是如何看待監控這件事情 以及 Prometheus 是如何實現這些需求的.golang

本文中不會出現的內容: 跟 Prometheus 實現細節有太多相關的東西 等數據庫

當想看監控的時候, 咱們到底想要什麼?

咱們想要看的東西也就是咱們對監控的需求.數組

需求

在實際的生產過程當中, 產生的和須要收集的監控數據分爲不少種, 例如如下這些, 除此以外, 還有不少不少. 但從實現方式上來講, 大多都大同小異.微信

  • 瞬時狀態的 CPU 和 MEM 使用率讀數併發

  • 硬盤使用量的增加率函數

  • 對 集羣節點 狀態 進行篩選 , 記錄節點位於什麼時刻不可用, 這就要求有 Tag 支持優化

  • 瞬時狀態的 網卡流量, 例如 100 Mbps,url

  • 服務請求量, 服務的 QPS, 服務的 錯誤率和錯誤次數spa

  • 所有請求的平均時耗

  • 一段時間內, 全部請求的 時耗中, 50% 的請求時耗小於多少毫秒, 95% 的請求時耗小於多少毫秒? 以此評估總體的接口狀況

  • 一段時間內, 全部請求的 時耗中, 多少請求時耗大於 1000ms, 多少請求時耗位於 200-500 區間內, 用於瞭解 請求時耗的具體分佈, 以評估接口狀況

  • ……

那麼咱們就須要一個 監控系統 來完成 上述需求, 這個監控系統 僅僅能收集上面的這些數據還不夠, 若是不能展現 和 查詢, 這些數據的保存將毫無心義. 另外, 既然是監控, 那麼必然要有告警的功能.

那麼總結一下, 咱們須要 可以 將 監控數據 收集 和 查詢 的監控系統 來完成咱們的需求, 除此以外, 咱們還須要 告警 和 展現 的功能

而 Prometheus 就是完成了 咱們上述需求的一個 監控系統 的 實現.

從 Prometheus 的視角看這些需求

數據的保存

Prometheus 使用一個 TSDB 來保存 這些監控數據,

TSDB 的全稱是 Time series Database (時序數據庫), 是爲了解決 時序性數據的保存問題, 而誕生的 數據庫類型.

一開始的話, 其實這些保存 時序型數據 的需求均可以使用 關係型 數據庫 來解決. 但 若是直接基於 關係型數據庫 來直接作需求的話, 各類寫入 和 讀出的 適配都得得按照 關係型數據庫的規則來作, 比較麻煩, 雖然確實有一些 TSDB 是基於 關係型數據庫實現的.

另外 因爲時序型數據 的一些特色, 例如 大部分都是寫入操做, 極少修改, 大部分讀都是順序讀, 對於一個指標的分析. 那麼基於這些特色, 又有一些 技巧, 來作不少的優化, 因此便有了 單獨的 TSDB 實現. 例如 InfluxDB, FaceBook 的 Gorilla 等, Prometheus 的 TSDB 即是 參考了 FB 的 Gorilla 以後, 自行實現的.

TSDB 中的 保存的數據, 一般以 數據點(Point) 做爲基本單位, 多個 Point 構成 Series (序列), 全部關於同一個主題的數據點 構成 Metrics(指標), 而每一個數據點會帶有一個 TimeStemp , 也就是這個點所關聯的時間, 而後每一個數據點會帶一些 Tag, 也能夠叫 Label , 下文中一概稱爲 Tag

而後咱們能夠經過對 TSDB 經過 指定 一些相關的指標和 Tag 來進行查詢, TSDB 的 數據的層級結構以下所示.

TSDB 和 關係型數據庫系統 相似, 一般分爲三層, 讀者能夠按這種對應關係來理解, 不過細節的意義上仍是有所不一樣的.

  1. Database 對應 RDBMS 的 Schema , 概念相似 , 而在 Prometheus 中則沒有 這一層, 全部的 Metrics 都在一個 Database 中

  2. Metrics 對應 RDBMS 的 Table , 在 InfluxDB 中這一層叫作 measurement ,概念相似 .

  3. Point 對應 RDBMS 的 一條數據 Row , 這個是 TSDB 中的最小單位, 每一個 Point 帶有一些 Tag , 能夠根據不一樣的條件篩選出來

收集? 如何收集?

監控的收集無非就是上報, 而上報須要 服務端 和 客戶端配合. 這裏先介紹服務端.

對 Prometheus 來說, 他的上報採用的是 pull 模型, 也就是拉取模型, 服務端根據客戶端的位置, 按時去固定接口拉取. PULL 模型 相較於 PUSH 模型在 客戶端較多的狀況下較爲明顯, 很好了緩解了 服務端的併發壓力, 但也帶來了一些問題, 例如 每一個客戶端的 位置都要註冊給服務端, 會很麻煩, 這個問題一般使用 服務發現和註冊 來解決.

整個過程上報流程能夠描述爲 客戶端按照協議, 準備好數據, 而後服務端的抓取器 定時訪問, 來抓取.

相較於 服務端, 客戶端就要複雜的多, 咱們先來聊聊 Prometheus 上報的協議

協議

Prometheus 服務端 要求 客戶端 準備一個 訪問 endpoint, 在 Prometheus 訪問 endpoint 的時候, 客戶端程序須要按照 相似於下面這種格式 準備好 上報數據,

# 這裏每一條 由 三個部分組成, 
# metricName : metrics 的名字 // [a-zA-Z_:][a-zA-Z0-9_:]*
# labels : 也能夠不填 // [a-zA-Z0-9_]*
# value : float64 類型的值
#

<--- metricName ---> <- labels -> <--value-->
go_gc_duration_seconds{quantile="0"} 1.1883e-05
go_gc_duration_seconds{quantile="0.25"} 2.2286e-05
go_gc_duration_seconds{quantile="0.5"} 4.734e-05
go_gc_duration_seconds{quantile="0.75"} 7.4898e-05
go_gc_duration_seconds{quantile="1"} 0.000809044
go_gc_duration_seconds_sum 0.683513876
go_gc_duration_seconds_count 10304
go_goroutines 36
go_info{version="go1.13.12"} 1
go_memstats_alloc_bytes 1.2614712e+07
go_memstats_alloc_bytes_total 8.483245152e+10
go_memstats_buck_hash_sys_bytes 1.648549e+06
go_memstats_frees_total 1.199358391e+09

而後 Prometheus 服務端 在抓取到這些數據, 會將一條記錄作成一個 point , 存入 TSDB 中.

這就是所有協議的內容, 很簡單. 另外上面的 endpoint 一般狀況下 是一個 相似於 http://ip:port/metrics這樣,而後以 metrics 結尾的 url, 不過也能夠根據自身需求來擬定 url 格式.

抽象

協議在 描述 服務端 如何與 客戶端 交互, 但若全部監控項 都由 客戶端這麼生成其實會有些繁瑣 和 混亂, 因此 Prometheus 的客戶端除了對 生成結構進行封裝外, 還提出了 四種 Metrics 規則類型 供 用戶使用. 這些 Metrics 規則類型僅僅和 Client 端有關, 與 Server 無關.

不少教程 一上來就給你講 四種 Metrics 規則類型, 讓人覺得 這四個東西是 Prometheus 裏很是重要的 東西, 甚至筆者在 寫這篇 文章的前幾版 以前, 也是這樣認爲. 但事實上, 這僅僅表明 Prometheus 看待 監控這件事的想法和態度 , 而後抽象出來一方面方便用戶使用, 另外一方面, 在社區發展第三方內容(例如 第三方 Client SDK 和 Exportor )的時候, 能夠規範你們的實現, 以方便討論.

即使 用戶不使用這四種 Metrics 規則類型, 也能夠徹底自定義本身的指標數據, 甚至定義本身的 指標類型 , 只要知足 上述協議便可. 這四種 Metrics 類型, 更像 Prometheus 團隊 在作監控這件事上 ,提出的一個行之有效的方法論(maybe 是 Google 團隊 XD).

除此以外, 因爲 這些規則被放在 Client 放在客戶端運行, 全部的 規則都會在算好以後, 被抓取到服務端, 這樣也進一步 降低了服務端的壓力, 和上面使用 Pull 的方式 的想法相似, 將壓力下推和分散到 Client 端.

只加不減的類型 Counter

顧名思義 Counter, 累加者, 這個是最經常使用的類型, 經常用於 記錄 HTTP Request Total 這種數據, 這個 類型 一般會一直上升, 基於這個特性, 咱們又可使用一些函數來獲取到另外一些咱們比較常關心的 指標 , 例如 單日訪問量 和 QPS

  • 因爲只增不減 的特性, 因此咱們使用 當前時刻的值 減去 當前時刻一日以前的值, 便可得到單日的訪問量, 例以下圖, 用 2020-01-03 11:00 時刻的數據, 減去 2020-01-02 11:00 時刻的數據, 便可算出單日請求量, 事實上, 任意時段的 請求量, 咱們均可以使用相似的方法算出.

  • 有了 時間段內請求量, 咱們要算出 QPS 那就很簡單了, 只須要 使用 時間段內請求量 / 秒數 便可, 例如 想展現一天時段內的 QPS, 咱們以 5m 爲一格, 那麼 5m 內的 QPS 就是 5m 內的請求總數 / 60*5, 而後把每一個 5m 的 QPS 算出來, 展現在 時間軸上, 便可看到一天 全部時段的 QPS 數據

瞬時指標 Gauge

這個指標就比較直白, 用於表示一些瞬時指標, 例如 上面需求提到的 瞬時 CPU 讀數, 瞬時 Disk Used Space, 等 , 這個指標不會累加, 只是按當前值爲主.

直方圖 Histogram 和 分位值 Summary

這兩個指標類型在使用上, 一般會一塊兒使用. 但也根據需求, 有時候會單獨使用. 在聊這兩種指標以前, 我想先聊聊 平均值, 用來引出 這兩種指標.

平均值的缺陷

假設 以下圖的一個瞬間, 系統 處理 100 條併發請求的 時耗以下左邊所示, 這 100 條請求裏面 時耗分爲 五個段, 0 ~ 100 ms,101~200 ms,201 ~ 300 ms,301 ~ 400 ms,401 ~ 500 ms, 接着 很快就能算出 這一秒請求的時耗 平均值 是 300 ms, 在 3s 或者更長的時間段裏, 咱們都能看到時間段內的全部請求的平均值 在 300ms.

假設請求時延 超過 400 ms 被認爲 不可接受, 那麼很明顯, 若是咱們只關注 平均值(avg) , 咱們就極可能認爲這一時段, 系統正處於正常狀態, 但實際上, 系統此時已經發生異常, 有 20% 的 請求處於不可接受的狀態. 平均值 過分的 屏蔽了 請求時耗分佈 的 具體細節.

分位值 Summary

而爲了解決 平均值的這個缺陷, 這個時候就須要引入 分位值(Top Percentile) 的概念.

顧名思義, 分位值 就是 分位 分位上的值, 例如 中位數, 其實也就是 50% 分位數. 一般計算分位值的 分位數 是在將 樣本集 的數據排序後, 取出指定位置的數據 做爲對應位置的分位值.例如 一個已經排好序的數組 裏面有 100 個數, 那麼 這個數組的 25% 分位值 就是第 25 個數. 例以下面的 圖, 就分別展現了 25% 分位值, 50% 分位值, 75% 分位值 , 95% 分位值 和 99% 分位值

那麼利用分位數,咱們就能夠明確知道 這個時段內的請求分佈狀況, 相比剛纔計算 平均數的方式, 分位值 的優點就展現的比較明顯.

分位值除了能夠比較好的展現時段內請求的細節外, 分位值還能夠做爲服務異常的基準指標, 以搜索接口爲例, 假設 規定搜索接口 95% 的請求都須要保證在 1s 內完成, 若是超過就告警. 用分位值來作就很好作, 直接 95% 的分位值 不容許大於 1s 便可.

在 Prometheus 中, 客戶端根據 協議 提交給 服務端 的 分位數 指標數據大體會像下面這個樣子

# quantile 能夠由用戶自行指定
go_gc_duration_seconds{quantile="0"} 1.1883e-05
go_gc_duration_seconds{quantile="0.25"} 2.2286e-05
go_gc_duration_seconds{quantile="0.5"} 4.734e-05
go_gc_duration_seconds{quantile="0.75"} 7.4898e-05
go_gc_duration_seconds{quantile="0.99"} 0.000809044
go_gc_duration_seconds_sum 0.683513876
go_gc_duration_seconds_count 10304

直方圖 Histogram

但僅僅只有 Summary , 仍是不太夠, 雖然知道了分位值 , 但有時候咱們想知道, 到底有多少請求 大於 400ms , 多少請求在 100ms 內, 這個僅僅經過 Summary 是沒有辦法告訴咱們的, 咱們須要藉助 Histogram 來表示.

Histogram 也就是直方圖, 沒錯, 就是 小學課本上那種. 在客戶端 使用直方圖 進行計數, 咱們就能夠很清晰的看到 請求時耗 在 咱們劃定的 區間中的分佈.

在 Prometheus 中, 客戶端根據 協議 生成好後, 提交給 服務端 的 指標數據大體會像下面這樣.

# le 能夠由用戶自行指定
prometheus_tsdb_compaction_chunk_range_bucket{le="100"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1000"} 100
prometheus_tsdb_compaction_chunk_range_bucket{le="1600"} 100
prometheus_tsdb_compaction_chunk_range_bucket{le="409600"} 100
prometheus_tsdb_compaction_chunk_range_bucket{le="1.6384e+06"} 260
prometheus_tsdb_compaction_chunk_range_bucket{le="2.62144e+07"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="+Inf"} 780
prometheus_tsdb_compaction_chunk_range_sum 1.1540798e+09
prometheus_tsdb_compaction_chunk_range_count 780

關於直方圖的實現, Prometheus 客戶端 生成的 並非像上面圖這樣錯落有致, 而是用這個公式這樣算出來的 當前的區間的值 = 當前區間內的實際值 + 上一個區間的值, 初看可能比較繞. 不過這樣作的好處有不少,

  • 咱們若是要計算 任意區間之間的 實際值 的話, 就只須要使用後面區間減去 前面區間的值便可, 在計算多個區間的值的時候尤爲明顯.

  • 當區間在減小的時候, 數據依舊不失真

  • 數據存儲時比較好優化

  • ……

至此, 咱們基本講完了 Prometheus 的大多數 使用細節. 接着咱們來看看 Prometheus 的結構.

Prometheus

上面這個是 Prometheus 官網的 Prometheus 結構圖, 咱們能夠看到 Prometheus 的結構分爲三塊,

最左邊的 是 監控數據的 source 區, 裏面有 Prometheus 使用它的 抓取器 來抓取 咱們的 應用程序的 監控數據, 而後保存到 TSDB 上,

接着 最右邊的 部分分爲兩塊, 一塊是 AlertManager , Prometheus 會按期檢查一些告警規則, 若是這些規則 被知足, 將會 推送 給 AlertManager 表示這些數據須要告警. 另外一塊是 展現的部分 , Grafana 或者 Prometheus 的 WebUI 經過 PromQL 查詢 Prometheus 的 TSDB 來獲取 結果並展現.

告警

告警的部分, 在 Prometheus 推送消息給 AlertManager 以後, AlertManager 會自行判斷, 這條告警是否須要推送出去, AlertManger 中有一些 沉默 和 告警閾值的規則, 當 一條告警觸發 多少次, 或者 多久以內觸發一次, 就會告警到 設置好的 Channel , 這樣能夠避免 由 告警風暴 帶來的麻痹 和 狼來了的故事.

PromQL

PromQL 是 Prometheus 設計出來的一種 DSL , 使用起來的感受向使用 函數. 關於 PromQL 的內容, 能夠參考 另外一篇博文 PromQL 指南

基於指標的監控系統 和 基於日誌的監控系統的區別

在 接觸 Prometheus + Grafana 指標監控系統 以前, 筆者也接觸過 ElasticSearch + Logstash/Fluentd 的 日誌監控系統,

筆者認爲兩者各有各的優點, Prometheus 方案的重點在於輕和迅速, 沒有太多的基礎設施, 甚至能夠不依賴 服務註冊中心, 只要把 Prometheus 拉起來, 而後 服務接入一下 Prometheus Client , 就能夠開始使用監控. 但 Prometheus 因爲基於 TSDB 的緣故, 因此 Prometheus 沒有辦法支持太太高維度的指標 或者 枚舉值太多的 Tag, 而這點對於 基於日誌監控系統 來說則還好.

而基於日誌的監控系統的問題在於過重了, 光是搭建和維護 一個 ES 集羣加上 Logstash 以及 Beats 收集器 和 Kibana, 就已經有些費力 . 另外 過多的 東西須要在 Logstash 這一層配置, 每次 業務方新的需求寫到日誌中, 須要添加一些 Logstash 的 配置, 來解析日誌以方便 Kibana 的視圖查詢.固然咱們也能夠用 通用解析的方案來實現, 那麼接着有時候就要添加一些 Index 規則 , 筆者以爲太高的自由度帶來了更多問題. 固然這只是筆者的想法, 若是有別的見解, 歡迎交流.

因此總結來說, 筆者認爲, 基於指標的監控系統 和 基於日誌的監控系統 更像是一種互補的方案, 雖然一般狀況下, 監控需求方面, 若是須要關注 太高維度的指標或者太高枚舉值的 狀況, 一般都是 這個需求自己就不合理. 但仍然有些狀況, 咱們必須實現這種需求, 那麼就能夠考慮 基於日誌的監控系統

Ref

  • https://developer.aliyun.com/article/174535

  • https://www.cnblogs.com/jimbo17/p/8337535.html

  • https://fabxc.org/tsdb/

  • https://www.jianshu.com/p/31afb8492eff


本文分享自微信公衆號 - GoCN(golangchina)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索