1. 程式人生 > 實用技巧 >樹義帶你學 Prometheus(四):PromQL 快速入門

樹義帶你學 Prometheus(四):PromQL 快速入門

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg

我們在 Prometheus入門教程(三):Grafana 圖表配置快速入門 中提到可以針對業務指標做自定義監控,其中有一個設定屬性為 Metrics,即:

這個 Metrics 屬性的值遵守了 PromQL 規則。我們只要學會了 PromQL 表示式,就知道了怎麼設定這個屬性了。

什麼是 PromQL?

PromQL(Prometheus Query Language)是 Prometheus 內建的資料查詢語言,它能實現對事件序列資料的查詢、聚合、邏輯運算等。它並且被廣泛應用在 Prometheus 的日常應用當中,包括對資料查詢、視覺化、告警處理當中。

簡單地說,PromQL 廣泛存在於以 Prometheus 為核心的監控體系中。所以需要用到資料篩選的地方,就會用到 PromQL。例如:監控指標的設定、報警指標的設定等等。

PromQL 基礎用法

當Prometheus通過Exporter採集到相應的監控指標樣本資料後,我們就可以通過PromQL對監控樣本資料進行查詢。

當我們直接使用監控指標名稱查詢時,可以查詢該指標下的所有時間序列。我們這裡啟動 Prometheus 伺服器,並開啟 http://localhost:9090/graph 地址。在查詢框中,我們輸入:prometheus_http_requests_total 並點選執行。

可以看到我們查詢出了所有指標名稱為 prometheus_http_requests_total

的資料。

PromQL 支援戶根據時間序列的標籤匹配模式來對時間序列進行過濾,目前主要支援兩種匹配模式:完全匹配和正則匹配。

完全匹配

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg

PromQL 支援使用 = 和 != 兩種完全匹配模式。

  • 等於。通過使用 label=value 可以選擇那些標籤滿足表示式定義的時間序列。
  • 不等於。通過使用 label!=value 則可以根據標籤匹配排除時間序列。

例如我們上面查詢出了所有指標名稱為 prometheus_http_requests_total 的資料。這時候我們希望只檢視錯誤的請求,即過濾掉所有 code 標籤不是 200 的資料。那麼我們的 PromQL 表示式可以修改為:prometheus_http_requests_total{code!="200"}

從上圖可以看到,查詢出的結果已經過濾掉了所有 code 不為 200 的資料。

正則匹配

PromQL 還可以使用正則表示式作為匹配條件,並且可以使用多個匹配條件。

  • 正向匹配。使用 label=~regx 表示選擇那些標籤符合正則表示式定義的時間序列。
  • 反向匹配。使用 label!~regx 進行排除。

例如我想查詢指標 prometheus_http_requests_total 中,所有 handler 標籤以 /api/v1 開頭的記錄,那麼我的表示式為:prometheus_http_requests_total{handler=~"/api/v1/.*"}

從上面的查詢結果可以看出,查詢的結果已經只保留了handler 標籤以 /api/v1 開頭的資料。

範圍查詢

我們上面直接通過類似 prometheus_http_requests_total 表示式查詢時間序列時,同一個指標同一標籤只會返回一條資料。這樣的表示式我們稱之為瞬間向量表示式,而返回的結果稱之為瞬間向量

而如果我們想查詢一段時間範圍內的樣本資料,那麼我們就需要用到區間向量表示式,其查詢出來的結果稱之為區間向量。時間範圍通過時間範圍選擇器 [] 進行定義。例如,通過以下表達式可以選擇最近5分鐘內的所有樣本資料:

prometheus_http_requests_total{}[5m]

通過查詢結果可以看到,此時我們查詢出了所有的樣本資料,而不再是一個樣本資料的統計值。

除了使用m表示分鐘以外,PromQL的時間範圍選擇器支援其它時間單位:

  • s - 秒
  • m - 分鐘
  • h - 小時
  • d - 天
  • w - 周
  • y - 年

時間位移操作

在瞬時向量表示式或者區間向量表示式中,都是以當前時間為基準:

# 瞬時向量表示式,選擇當前最新的資料
prometheus_http_requests_total{} 
# 區間向量表示式,選擇以當前時間為基準,5分鐘內的資料
prometheus_http_requests_total{}[5m] 

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg

如果我們想查詢 5 分鐘前的瞬時樣本資料,或昨天一天的區間內的樣本資料呢? 這個時候我們就可以使用位移操作,位移操作的關鍵字為 offset。

# 查詢 5 分鐘前的最新資料
http_request_total{} offset 5m
# 往前移動 1 天,查詢 1 天前的資料
# 例如現在是 2020-10-07 00:00:00
# 那麼這個表示式查詢的資料是:2020-10-05 至 2020-10-06 的資料
http_request_total{}[1d] offset 1d

聚合操作

一般情況下,我們通過 PromQL 查詢到的資料都是很多的。PromQL 提供的聚合操作可以用來對這些時間序列進行處理,形成一條新的時間序列。

以我們的 prometheus_http_requests_total 指標為例,不加任何條件我們查詢到的資料為:

從上圖查詢結果可以知道,一共有 8 條資料,這 8 條資料的 value 總和為 307。那麼我們使用下面兩個聚合操作表示式來查詢,看看結果對不對。

第一個表示式,計算一共有幾條資料:count(prometheus_http_requests_total)

第二個表示式,計算所有資料的 value 總和:sum(prometheus_http_requests_total)

可以看到 count 的數值是一致的,都是 8。但是 sum 的數值有誤差,這是因為我們兩次查詢的時間間隔內,某些記錄的數值發生了變化。

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg

標量

在 PromQL 中,標量是一個浮點型的數字值,沒有時序。例如:10

需要注意的是,當使用表示式count(http_requests_total),返回的資料型別,依然是瞬時向量。使用者可以通過內建函式scalar()將單個瞬時向量轉換為標量。

如上圖所示,我們將 sum 操作的值用 scalar 轉換了一下,最終的結果就是一個標量了。

字串

在 PromQL 中,字串是一個簡單的字串值。直接使用字串作為 PromQL 表示式,則會直接返回字串。

上圖中我使用字串 "this is a string" 直接作為 PromQL 查詢表示式,結果返回的是一個字串。

PromQL 操作符

PromQL 還支援豐富的操作符,使用者可以使用這些操作符對進一步的對事件序列進行二次加工。這些操作符包括:數學運算子,邏輯運算子,布林運算子等等。

數學運算子

數學運算子比較簡單,就是簡單的加減乘除等。

例如我們通過 prometheus_http_response_size_bytes_sum 可以查詢到 Prometheus 這個應用的 HTTP 響應位元組總和。但是這個單位是位元組,我們希望用 MB 顯示。那麼我們可以這麼設定:prometheus_http_response_size_bytes_sum/8/1024

最終顯示的資料就是以 MB 作為單位的數值。

PromQL支援的所有數學運算子如下所示:

  • + (加法)
  • - (減法)
  • * (乘法)
  • / (除法)
  • % (求餘)
  • ^ (冪運算)

布林運算子

布林運算子支援使用者根據時間序列中樣本的值,對時間序列進行過濾。例如我們可以通過 prometheus_http_requests_total 查詢出每個介面的請求次數,但是如果我們想篩選出請求次數超過 20 次的介面呢?

此時我們可以用下面的 PromQL 表示式:

prometheus_http_requests_total > 20

可以看到我們將所有 value 值超過 20 的資料都篩選了出來,從其指標名稱可以看出對應的介面名。

從上面的圖中我們可以看到,value 的值還是具體的數值。但如果我們希望對符合條件的資料,value 變為 1。不符合條件的資料,value 變為 0。那麼我們可以使用bool修飾符。

我們使用下面的 PromQL 表示式:

prometheus_http_requests_total > bool 20

從下面的執行結果可以看到,這時候並不過濾掉資料,而是將 value 的值變成了 1 或 0。

目前,Prometheus支援以下布林運算子如下:

  • * == (相等)
  • != (不相等)
  • > (大於)
  • < (小於)
  • >= (大於等於)
  • <= (小於等於)

集合運算子

通過集合運算,可以在兩個瞬時向量與瞬時向量之間進行相應的集合操作。目前,Prometheus支援以下集合運算子:

  • and 與操作
  • or 或操作
  • unless 排除操作

and 與操作

vector1 and vector2 進行一個與操作,會產生一個新的集合。該集合中的元素同時在 vector1 和 vector2 中都存在。

例如我們有 vector1 為 A B C,vector2 為 B C D,那麼 vector1 and vector2 的結果為:B C。

or 或操作

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg

vector1 and vector2 進行一個或操作,會產生一個新的集合。該集合中包含 vector1 和 vector2 中的所有元素。

例如我們有 vector1 為 A B C,vector2 為 B C D,那麼 vector1 or vector2 的結果為:A B C D。

unless 排除操作

vector1 and vector2 進行一個或操作,會產生一個新的集合。該集合首先取 vector1 集合的所有元素,然後排除掉所有在 vector2 中存在的元素。

例如我們有 vector1 為 A B C,vector2 為 B C D,那麼 vector1 unless vector2 的結果為:A。

操作符優先順序

在PromQL操作符中優先順序由高到低依次為:

  • ^
  • *, /, %
  • +, -
  • ==, !=, <=, <, >=, >
  • and, unless
  • or

PromQL 聚合操作

Prometheus 還提供了聚合操作符,這些操作符作用於瞬時向量。可以將瞬時表示式返回的樣本資料進行聚合,形成一個新的時間序列。目前支援的聚合函式有:

  • sum (求和)
  • min (最小值)
  • max (最大值)
  • avg (平均值)
  • stddev (標準差)
  • stdvar (標準方差)
  • count (計數)
  • count_values (對value進行計數)
  • bottomk (後n條時序)
  • topk (前n條時序)
  • quantile (分位數)

sum 求和

用於對記錄的 value 值進行求和。

例如:sum(prometheus_http_requests_total) 表示統計所有 HTTP 請求的次數。

min 最小值

返回所有記錄的最小值。

prometheus_http_requests_total 指標所有資料如下圖所示:

當我們執行如下 PromQL 時,會篩選出最小的記錄值。

min(prometheus_http_requests_total)

max 最大值

返回所有記錄的最大值。

當我們執行如下 PromQL 時,會篩選出最大的記錄值。

max(prometheus_http_requests_total)

avg 平均值

avg 函式返回所有記錄的平均值。

當我們執行如下 PromQL 時,會篩選出最大的記錄值。

avg(prometheus_http_requests_total)

stddev 標準差

標準差(Standard Deviation)常用來描述資料的波動大小。例如我們統計籃球隊員身高:

兩支隊伍平均身高都是 180,看起來似乎差不多。但如果畫圖的話,得到結果如下:

很顯然,藍色隊隊員身高更加整齊一些,橙色隊身高顯得參差不齊。為了反映一組資料,偏離平均值的程度,就有了「標準差 」這個概念。

如果資料量很大,比如幾萬人的身高,我們不容易從折線圖看出來,可以直接用公式計算。上圖的資料標準差計算結果為:

很明顯,橙色隊的標準差比藍色隊標準差大很多。這說明橙色隊的身高波動更大。

當我們執行如下 PromQL 時,會計算出不同 HTTP 請求的數量波動情況。

stddev(prometheus_http_requests_total)

從計算結果可以看到,標準差達到了 1100 多,這說明其資料波動非常大。

參考:樣本標準差的意義是什麼? - 李俊達的回答 - 知乎

count 計數

count 函式返回所有記錄的計數。

例如:count(prometheus_http_requests_total) 表示統計所有 HTTP 請求的次數。

bottomk 後幾條

bottomk 用於對樣本值進行排序,返回當前樣本值後 N 位的時間序列。

例如獲取 HTTP 請求量後 5 位的請求,可以使用表示式:

bottomk(5, prometheus_http_requests_total)

topk 前幾條

topk 用於對樣本值進行排序,返回當前樣本值前 N 位的時間序列。

例如獲取 HTTP 請求量前 5 位的請求,可以使用表示式:

topk(5, prometheus_http_requests_total)

PromQL 內建函式

PromQL 提供了大量的內建函式,可以對時序資料進行豐富的處理。例如 irate() 函式可以幫助我們計算監控指標的增長率,不需要我們去手動計算。

rate 增長率

我們知道 counter 型別指標的特點是隻增不減,在沒有發生重置的情況下,其樣本值是不斷增大的。為了能直觀地觀察期變化情況,需要計算樣本的增長率。

increase(v range-vector) 函式是 PromQL 中提供的眾多內建函式之一。其中引數 v 是一個區間向量,increase 函式獲取區間向量中的第一個後最後一個樣本並返回其增長量。因此,可以通過以下表達式Counter型別指標的增長率:

increase(node_cpu[2m]) / 120

這裡通過node_cpu[2m]獲取時間序列最近兩分鐘的所有樣本,increase計算出最近兩分鐘的增長量,最後除以時間120秒得到node_cpu樣本在最近兩分鐘的平均增長率。並且這個值也近似於主機節點最近兩分鐘內的平均CPU使用率。

除了使用increase函式以外,PromQL中還直接內建了rate(v range-vector)函式,rate函式可以直接計算區間向量v在時間視窗內平均增長速率。因此,通過以下表達式可以得到與increase函式相同的結果:

rate(node_cpu[2m])

需要注意的是使用rate或者increase函式去計算樣本的平均增長速率,容易陷入「長尾問題」當中,其無法反應在時間視窗內樣本資料的突發變化。

例如,對於主機而言在 2 分鐘的時間視窗內,可能在某一個由於訪問量或者其它問題導致 CPU 佔用 100% 的情況,但是通過計算在時間視窗內的平均增長率卻無法反應出該問題。

為了解決該問題,PromQL提供了另外一個靈敏度更高的函式 irate(v range-vector)。irate 同樣用於計算區間向量的計算率,但是其反應出的是瞬時增長率。irate 函式是通過區間向量中最後兩個樣本資料來計算區間向量的增長速率。

這種方式可以避免在時間視窗範圍內的「長尾問題」,並且體現出更好的靈敏度,通過 irate 函式繪製的圖示能夠更好的反應樣本資料的瞬時變化狀態。

irate(node_cpu[2m])

irate函式相比於rate函式提供了更高的靈敏度,不過當需要分析長期趨勢或者在告警規則中,irate的這種靈敏度反而容易造成干擾。因此在長期趨勢分析或者告警中更推薦使用rate函式。

predict_linear 增長預測

在一般情況下,系統管理員為了確保業務的持續可用執行,會針對伺服器的資源設定相應的告警閾值。例如,當磁碟空間只剩512MB時向相關人員傳送告警通知。 這種基於閾值的告警模式對於當資源用量是平滑增長的情況下是能夠有效的工作的。

但是如果資源不是平滑變化的呢? 比如有些某些業務增長,儲存空間的增長速率提升了高几倍。這時,如果基於原有閾值去觸發告警,當系統管理員接收到告警以後可能還沒來得及去處理問題,系統就已經不可用了。

因此閾值通常來說不是固定的,需要定期進行調整才能保證該告警閾值能夠發揮去作用。 那麼還有沒有更好的方法嗎?

PromQL 中內建的 predict_linear(v range-vector, t scalar) 函式可以幫助系統管理員更好的處理此類情況,predict_linear 函式可以預測時間序列v在t秒後的值。

它基於簡單線性迴歸的方式,對時間視窗內的樣本資料進行統計,從而可以對時間序列的變化趨勢做出預測。例如,基於2小時的樣本資料,來預測主機可用磁碟空間的是否在4個小時候被佔滿,可以使用如下表達式:

predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg

參考資料

文章首發於【陳樹義】公眾號,點選跳轉到原文:https://mp.weixin.qq.com/s/wnudWqfafzKUoDk4ke5Npg