天天看點

Prometheus監控名額查詢性能調優

作者:閃念基因

01

背景

在《SRE: Google運維解密》一書中作者指出,監控系統需要能夠有效的支援白盒監控和黑盒監控。黑盒監控隻在某個問題目前正在發生,并且造成了某個現象時才會發出緊急警報。“白盒監控則大量依賴對系統内部資訊的檢測,如系統日志、抓取提供名額資訊的 HTTP 節點等。白盒監控系統是以可以檢測到即将發生的問題及那些重試所掩蓋的問題等”。為了完善系統的白盒監控,會員團隊基于 Prometheus + Grafana 開源元件建構了監控告警平台。最近一段時間在查詢監控名額時遇到了性能瓶頸,表現為一些監控頁面的圖表加載特别慢,查詢近7天的監控資料就會失敗,極大的降低了開發人員的工作效率。

02

排查

初步排查

選取其中一個加載失敗的監控頁面,查詢近7天的監控資料,通過浏覽器的開發者工具觀察到的名額資料查詢接口響應耗時如下圖所示:

Prometheus監控名額查詢性能調優

分析名額資料查詢接口和監控圖表的對應關系後發現,監控圖表加載失敗是查詢接口逾時所導緻的。使用逾時的名額查詢語句直接查詢 Prometheus,即便将采樣步長調高到40分鐘,查詢響應耗時依然有48秒之多。說明查詢的主要耗時都用在 Prometheus 的查詢處理上。

Prometheus查詢處理流程分析

想要繼續弄清楚 Prometheus 的查詢處理為什麼需要耗時這麼久,我們需要簡單了解一下 Prometheus 的查詢處理流程。Prometheus 使用了一個基于标簽(label)、值和時間戳的簡單資料模型,這些标簽和樣本一起構成了資料序列(series),每個樣本都是由時間戳和值組成。

Prometheus監控名額查詢性能調優

Prometheus 将這些資料存儲在其内部的時間序列資料庫中(Prometheus 也支援外部存儲系統)。Prometheus 的資料庫被劃分為基本的存儲單元,稱為 block,其中包含一定時間範圍(預設2小時)的資料。block 的結構如下圖所示:

Prometheus監控名額查詢性能調優

block 中最重要的兩個組成部分是 chunks 和 index。chunks 中儲存的是特定序列在一定時間範圍内的采樣集合,一個 chunk 隻包含一個序列的資料。index 中儲存的是 Prometheus 通路資料使用的索引資訊。在 index 中儲存着兩種類的索引:postings index 和 series index。

  • postings index:儲存着标簽與包含該标簽的序列之間的對應關系。例如,标簽 __name__ ="logback_events_total" 可以被包含在兩個序列中,logback_events_total {job="app1", level="error"}和 logback_events_total {job="app2", level="error"}。
  • series index:儲存着序列和 chunk 之間的對應關系。例如,序列 {__name__=”logback_events_total”, job=”app1”, level="error"} 可能儲存在00001和00002兩個 chunk 裡。

block 中還包含了一個 meta.json 檔案(儲存 block 的中繼資料資訊)和 一個 tombstones 檔案(儲存已經删除的序列以及關于它們的資訊)。

Prometheus 的查詢處理通常包含以下五個基本步驟:

1、通過查詢的時間範圍确定對應的 block。

2、通過 postings index 确定與标簽比對的序列。

3、通過 series index 确定這些序列對應的 chunk。

4、從這些 chunk 中檢索樣本資料。

5、如果查詢中包含操作符、聚合操作或内置函數,還需要基于樣本資料進行二次計算。

詳細排查

了解了 Prometheus 的查詢處理流程後,我們可以得出以下結論:

1、查詢的時間範圍越大則耗時也就會越多,因為查詢時間範圍越大則涉及的 block 也會越多。

2、标簽的值越多則查詢耗時也就會越多,因為标簽每增加一個值就會生成一個新的序列。

3、查詢中使用了操作符、聚合操作或内置函數也會增加耗時,因為需要基于樣本資料進行二次計算。

為了後面描述友善,我們先引入一個定義:基數(cardinality)。基數的基本定義是指一個給定集合中的元素的數量。以logback_events_total 這個名額為例,我們來了解下Prometheus 中标簽基數和名額基數。

Prometheus監控名額查詢性能調優

logback_events_total 名額有兩個标簽,其中标簽 job 有兩個值,那麼它的基數為2,同理 level 标簽的基數為5,logback_events_total 名額的基數為10。名額基數越高查詢耗時也就會越多。

了解了基數的定義後,我們選取一個查詢逾時的監控名額來進行基數分析。先看下這個名額的一條标簽資料:

http_server_requests_seconds_count{application="app1", cluster="cluster1", exception="xxxException", instance="xxx", job="app1", method="GET", returnCode="A0000", status="200", uri="/xxx"}           

執行如下 PromQL(Prometheus自定義的資料查詢語言) 來查詢該名額每個标簽的基數(用實際标簽名替換下面語句中的 label_name):

count(count by (label_name) (http_server_requests_seconds_count))           

該名額的标簽基數彙總如下:

Prometheus監控名額查詢性能調優

可以看到,instance 和uri 這兩個标簽的基數都很高。執行如下 PromQL 查詢該名額的基數,發現該名額的基數達到了147610。

count({__name__="http_server_requests_seconds_count"})           

用同樣的方式分析了下其他問題名額,名額基數同樣達到了10萬以上。作為對比,我們抽樣分析了幾個加載比較快的名額,名額基數大都在1萬以下。是以可以确認,查詢耗時高主要是名額基數過高引起的。實際監控圖表配置的查詢語句中還使用了一些聚合操作(例如 sum)和内置函數(例如 rate),也在一定程度上增加了查詢耗時。

03

優化

Prometheus 提供了一種叫做記錄規則(Recording Rule)的方式來優化我們的查詢語句,記錄規則的基本思想是:預先計算經常要用或計算開銷較大的表達式,并将其結果儲存為一組新的時間序列。以上面提到的 http_server_requests_seconds_count 這個名額為例,優化前的一個監控圖表的查詢語句如下:

sum(rate(http_server_requests_seconds_count{application="$application", cluster=~"$cluster", uri!~"/actuator/.*|/\\*.*|root|/|/health/check"}[1m])) by (uri)           

從查詢語句可以看出,該監控圖表依賴于 application、cluster 和 uri 這三個标簽的聚合資料,是以可以建立如下的記錄規則(記錄規則的詳細建立方式可以參考 Prometheus 官方文檔):

record: http_server_requests_seconds_count:rate:1m:acu
expr: sum(rate(http_server_requests_seconds_count{uri !~ "/actuator/.*|/\\*.*|root|/|/health/check"}[1m])) by (application,cluster,uri)           

記錄規則建立後預設隻包含記錄規則建立時間之後的資料,并不包含建立之前的曆史資料。Prometheus從 v2.27 版本開始支援通過指令行指令來手工回溯曆史資料(對于 Prometheus v2.38及以下版本,需要在執行個體啟動時開啟--storage.tsdb.allow-overlapping-blocks 參數),通過 promtool tsdb create-blocks-from rules --help 可以了解該指令的使用,這裡給出了一個該指令的樣例:

promtool tsdb create-blocks-from rules \
--start 1680348042 \
--end 1682421642 \
--url http://mypromserver.com:9090 \
rules.yaml           

promtool tsdb create-blocks-from rules 指令的輸出是一個目錄(預設是 data/ ),其中包含了記錄規則檔案中所有規則曆史資料的 block。為了讓新生成的 block 生效,必須将它們手工移動到正在運作的 Prometheus 執行個體資料目錄下。移動後,新産生的 block 将在下一次壓縮運作時與現有 block 合并。

在執行完上面的操作後通過 PromQL 查詢這個記錄規則的基數,發現名額基數下降到了4878。原來監控圖表的查詢語句可以調整為:

sum(http_server_requests_seconds_count:rate:1m:acu{application="$application", cluster=~"$cluster"}) by (uri)           

對優化後的監控圖表進行測試,效果對比如下圖所示,可以看到查詢的時間範圍越長,效果提升越明顯。這主要得益于記錄規則帶來的名額基數大幅降低以及函數計算的預先處理。

Prometheus監控名額查詢性能調優

在實際場景中,如果有多個監控圖表都用到了同一個監控名額,可以整體評估一下記錄規則應該怎麼建立。因為一個記錄規則也是一組時間序列,在滿足查詢需求的前提下盡量避免建立過多的記錄規則。

04

小結

當 Prometheus 名額基數過高時,就會出現監控圖表加載很慢甚至加載失敗。通過 Prometheus 提供的記錄規則,我們可以對查詢語句進行優化進而減少查詢耗時。除了記錄規則外,還有一些技巧可以優化查詢性能,例如增加 Prometheus 名額采集間隔,删除不用的名額序列等。實際上,在監控名額設計階段就應該對名額基數進行評估,必要時對标簽取值進行取舍。例如,一個标簽對應 HTTP 響應碼,可以将它的取值定義為 1XX、2XX、3XX、4XX、5XX,相比詳細的響應碼可以大大降低名額基數。

作者:會員技術團隊

來源:微信公衆号:愛奇藝技術産品團隊

出處:https://mp.weixin.qq.com/s/aPi1qEaGm6WRyrDz3ml_IQ

繼續閱讀