一、背景 在《SRE: Google運維解密》一書中作者指出,監控系統需要能夠有效的支持白盒監控和黑盒監控。黑盒監控只在某個問題目前正在發生,並且造成了某個現象時才會發出緊急警報。“白盒監控則大量依賴對系統內部信息的檢測,如系統日誌、抓取提供指標信息的 HTTP 節點等。白盒監控系統因此可以檢測到即 ...
一、背景
在《SRE: Google運維解密》一書中作者指出,監控系統需要能夠有效的支持白盒監控和黑盒監控。黑盒監控只在某個問題目前正在發生,並且造成了某個現象時才會發出緊急警報。“白盒監控則大量依賴對系統內部信息的檢測,如系統日誌、抓取提供指標信息的 HTTP 節點等。白盒監控系統因此可以檢測到即將發生的問題及那些重試所掩蓋的問題等”。為了完善系統的白盒監控,會員團隊基於 Prometheus + Grafana 開源組件構建了監控告警平臺。最近一段時間在查詢監控指標時遇到了性能瓶頸,表現為一些監控頁面的圖表載入特別慢,查詢近7天的監控數據就會失敗,極大的降低了開發人員的工作效率。
二、排查
選取其中一個載入失敗的監控頁面,查詢近7天的監控數據,通過瀏覽器的開發者工具觀察到的指標數據查詢介面響應耗時如下圖所示:
分析指標數據查詢介面和監控圖表的對應關係後發現,監控圖表載入失敗是查詢介面超時所導致的。使用超時的指標查詢語句直接查詢 Prometheus,即便將採樣步長調高到40分鐘,查詢響應耗時依然有48秒之多。說明查詢的主要耗時都用在 Prometheus 的查詢處理上。
想要繼續弄清楚 Prometheus 的查詢處理為什麼需要耗時這麼久,我們需要簡單瞭解一下 Prometheus 的查詢處理流程。Prometheus 使用了一個基於標簽(label)、值和時間戳的簡單數據模型,這些標簽和樣本一起構成了數據序列(series),每個樣本都是由時間戳和值組成。
Prometheus 將這些數據存儲在其內部的時間序列資料庫中(Prometheus 也支持外部存儲系統)。Prometheus 的資料庫被劃分為基本的存儲單元,稱為 block,其中包含一定時間範圍(預設2小時)的數據。block 的結構如下圖所示:
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 的查詢處理通常包含以下五個基本步驟:
-
通過查詢的時間範圍確定對應的 block。
-
通過 postings index 確定與標簽匹配的序列。
-
通過 series index 確定這些序列對應的 chunk。
-
從這些 chunk 中檢索樣本數據。
-
如果查詢中包含操作符、聚合操作或內置函數,還需要基於樣本數據進行二次計算。
瞭解了 Prometheus 的查詢處理流程後,我們可以得出以下結論:
1)查詢的時間範圍越大則耗時也就會越多,因為查詢時間範圍越大則涉及的 block 也會越多。
2)標簽的值越多則查詢耗時也就會越多,因為標簽每增加一個值就會生成一個新的序列。
3)查詢中使用了操作符、聚合操作或內置函數也會增加耗時,因為需要基於樣本數據進行二次計算。
為了後面描述方便,我們先引入一個定義:基數(cardinality)。基數的基本定義是指一個給定集合中的元素的數量。以logback_events_total 這個指標為例,我們來理解下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))
該指標的標簽基數彙總如下:
可以看到,instance 和uri 這兩個標簽的基數都很高。執行如下 PromQL 查詢該指標的基數,發現該指標的基數達到了147610。
count({__name__="http_server_requests_seconds_count"})
用同樣的方式分析了下其他問題指標,指標基數同樣達到了10萬以上。作為對比,我們抽樣分析了幾個載入比較快的指標,指標基數大都在1萬以下。因此可以確認,查詢耗時高主要是指標基數過高引起的。實際監控圖表配置的查詢語句中還使用了一些聚合操作(例如 sum)和內置函數(例如 rate),也在一定程度上增加了查詢耗時。
三、優化
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:acuexpr: 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 指標基數過高時,就會出現監控圖表載入很慢甚至載入失敗。通過 Prometheus 提供的記錄規則,我們可以對查詢語句進行優化從而減少查詢耗時。除了記錄規則外,還有一些技巧可以優化查詢性能,例如增加 Prometheus 指標採集間隔,刪除不用的指標序列等。實際上,在監控指標設計階段就應該對指標基數進行評估,必要時對標簽取值進行取捨。例如,一個標簽對應 HTTP 響應碼,可以將它的取值定義為 1XX、2XX、3XX、4XX、5XX,相比詳細的響應碼可以大大降低指標基數。
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Prometheus-monitoring-metrics-query-performance-tuning.html