容器監控實踐—PromQL查詢解析

一. 概述

Prometheus除了存儲數據外,還提供了一種強大的功能表達式語言 PromQL,容許用戶實時選擇和匯聚時間序列數據。node

表達式的結果能夠在瀏覽器中顯示爲圖形,也能夠顯示爲表格數據,或者由外部系統經過 HTTP API 調用。經過PromQL用戶能夠很是方便地查詢監控數據,或者利用表達式進行告警配置git

如:k8s中的node在線率:sum(kube_node_status_condition{condition="Ready", status="true"}) / sum(kube_node_info) *100 正則表達式

Metric類型

關於時間序列存儲,能夠參考:https://www.infoq.cn/article/...sql

Prometheus會將全部採集到的樣本數據以時間序列(time-series)的方式保存在內存數據庫TSDB中,而且定時保存到硬盤上。time-series是按照時間戳和值的序列順序存放的,咱們稱之爲向量(vector)。每條time-series經過指標名稱(metrics name)和一組標籤集(labelset)命名。數據庫

在time-series中的每個點稱爲一個樣本(sample),樣本由如下三部分組成:express

  • 指標(metric):metric name和描述當前樣本特徵的labelsets;
  • 時間戳(timestamp):一個精確到毫秒的時間戳;
  • 樣本值(value): 一個folat64的浮點型數據表示當前樣本的值。

如某一時刻的node_cpu指標爲459.71json

node_cpu{app="node-exporter",cpu="cpu0",instance="192.168.0.4:9100",job="kubernetes-service-endpoints",kubernetes_name="node-exporter",kubernetes_namespace="kube-system",mode="guest"}     459.71

Prometheus定義了4中不一樣的指標類型(metric type):api

  • Counter 計數器
計數器,只增不減,如http_requests_total請求總數

例如,經過rate()函數獲取HTTP請求量的增加率:
rate(http_requests_total[5m])
  • Gauge 儀表盤
當前狀態,可增可減。如kube_pod_status_ready當前pod可用數
能夠獲取樣本在一段時間返回內的變化狀況,如:
delta(kube_pod_status_ready[2h])
  • Histogram 直方圖
Histogram 由 <basename>_bucket{le="<upper inclusive bound>"},<basename>_bucket{le="+Inf"}, <basename>_sum,<basename>_count 組成,主要用於表示一段時間範圍內對數據進行採樣(一般是請求持續時間或響應大小),並可以對其指定區間以及總數進行統計,一般它採集的數據展現爲直方圖。

例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted, 表示 Prometheus 中每一個時序須要存儲的 chunks 數量,咱們能夠用它計算待持久化的數據的分位數。
  • Summary 摘要
Summary 和 Histogram 相似,由 <basename>{quantile="<φ>"},<basename>_sum,<basename>_count 組成,主要用於表示一段時間內數據採樣結果(一般是請求持續時間或響應大小),它直接存儲了 quantile 數據,而不是根據統計區間計算出來的。

例如 Prometheus server 中 prometheus_target_interval_length_seconds。

Histogram 須要經過 <basename>_bucket 計算 quantile, 而 Summary 直接存儲了 quantile 的值。

基礎查詢

PromQL是Prometheus內置的數據查詢語言,其提供對時間序列數據豐富的查詢,聚合以及邏輯運算能力的支持。瀏覽器

如http_requests_total指標bash

你能夠經過附加一組標籤,並用{}括起來,來進一步篩選這些時間序列。下面這個例子只選擇有http_requests_total名稱的、有prometheus工做標籤的、有canary組標籤的時間序列:

http_requests_total{job="prometheus",group="canary"}

若是條件爲空,能夠寫爲:http_requests_total{}

另外,也能夠也能夠將標籤值反向匹配,或者對正則表達式匹配標籤值。如操做符:

=:選擇正好相等的字符串標籤
  !=:選擇不相等的字符串標籤
  =~:選擇匹配正則表達式的標籤(或子標籤)
  !=:選擇不匹配正則表達式的標籤(或子標籤)

範圍查詢

相似http_requests_total{job="prometheus",group="canary"}的方式,獲得的是瞬時值,若是想獲得必定範圍內的值,可使用範圍查詢

時間範圍經過時間範圍選擇器[]進行定義。例如,經過如下表達式能夠選擇最近5分鐘內的全部樣本數據,如:http_request_total{}[5m]

除了分鐘,支持的單位有:

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

偏移查詢

如:查詢http_requests_total在當前時刻的一週的速率:

rate(http_requests_total{} offset 1w)

偏移修飾符容許更改查詢中單個即時向量和範圍向量的時間偏移量,例如,如下表達式返回相對於當前查詢時間5分鐘前的http_requests_total值:

http_requests_total offset 5m

等價於

http_requests_total{job="prometheus"}[5m]

請注意,偏移量修飾符始終須要跟隨選擇器,即如下是正確的:

sum(http_requests_total{method="GET"} offset 5m) // GOOD.

下面是錯誤的:

sum(http_requests_total{method="GET"}) offset 5m // INVALID.

操做符

Prometheus 的查詢語言支持基本的邏輯運算和算術運算

二元算術運算:
    • 加法
    • 減法
    • 乘法
  • / 除法
  • % 模
  • ^ 冪等

運算中用到的基礎數據類型:

  • 瞬時向量(Instant vector) - 一組時間序列,每一個時間序列包含單個樣本,它們共享相同的時間戳。也就是說,表達式的返回值中只會包含該時間序列中的最新的一個樣本值。而相應的這樣的表達式稱之爲瞬時向量表達式。
  • 區間向量(Range vector) - 一組時間序列,每一個時間序列包含一段時間範圍內的樣本數據。
  • 標量(Scalar) - 一個浮點型的數據值。
  • 字符串(String) - 一個簡單的字符串值。

二元運算操做符支持 scalar/scalar(標量/標量)、vector/scalar(向量/標量)、和 vector/vector(向量/向量) 之間的操做。

在兩個標量之間進行數學運算,獲得的結果也是標量。

例如,若是咱們想根據 node_disk_bytes_written 和 node_disk_bytes_read 獲取主機磁盤IO的總量,可使用以下表達式:

`node_disk_bytes_written + node_disk_bytes_read
`

或者node的內存數GB

node_memory_free_bytes_total / (1024 * 1024)
布爾運算
  • == (相等)
  • != (不相等)
  • (大於)
  • < (小於)
  • = (大於等於)
  • <= (小於等於)

如:獲取http_requests_total請求總數是否超過10000,返回0和1,1則報警

http_requests_total > 10000 # 結果爲 true 或 false
http_requests_total > bool 10000 # 結果爲 1 或 0
集合運算
  • and (而且)
  • or (或者)
  • unless (排除)
優先級

四則運算有優先級,promql的複雜運算也有優先級

例如,查詢主機的CPU使用率,可使用表達式:

100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )

其中irate是PromQL中的內置函數,用於計算區間向量中時間序列每秒的即時增加率
在PromQL操做符中優先級由高到低依次爲:

  1. ^
  2. *, /, %
  3. +, -
  4. ==, !=, <=, <, >=, >
  5. and, unless
  6. or
匹配模式(聯合查詢)

與數據庫中的join相似,promsql有兩種典型的匹配查詢:

  • 一對一(one-to-one)
  • 多對一(many-to-one)或一對多(one-to-many)

例如當存在樣本:

method_code:http_errors:rate5m{method="get", code="500"}  24
method_code:http_errors:rate5m{method="get", code="404"}  30
method_code:http_errors:rate5m{method="put", code="501"}  3
method_code:http_errors:rate5m{method="post", code="500"} 6
method_code:http_errors:rate5m{method="post", code="404"} 21

method:http_requests:rate5m{method="get"}  600
method:http_requests:rate5m{method="del"}  34
method:http_requests:rate5m{method="post"} 120

使用 PromQL 表達式:

method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m

該表達式會返回在過去 5 分鐘內,HTTP 請求狀態碼爲 500 的在全部請求中的比例。若是沒有使用 ignoring(code),操做符兩邊表達式返回的瞬時向量中將找不到任何一個標籤徹底相同的匹配項。

所以結果以下:

{method="get"} 0.04 // 24 / 600
{method="post"} 0.05 // 6 / 120

同時因爲 method 爲 put 和 del 的樣本找不到匹配項,所以不會出如今結果當中。

多對一模式

例如,使用表達式:

method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m

該表達式中,左向量 method_code:http_errors:rate5m 包含兩個標籤 method 和 code。而右向量 method:http_requests:rate5m 中只包含一個標籤 method,所以匹配時須要使用 ignoring 限定匹配的標籤爲 code。

在限定匹配標籤後,右向量中的元素可能匹配到多個左向量中的元素 所以該表達式的匹配模式爲多對一,須要使用 group 修飾符 group_left 指定左向量具備更好的基數。

最終的運算結果以下:

{method="get", code="500"} 0.04 // 24 / 600
{method="get", code="404"} 0.05 // 30 / 600
{method="post", code="500"} 0.05 // 6 / 120
{method="post", code="404"} 0.175 // 21 / 120

提醒:group 修飾符只能在比較和數學運算符中使用。在邏輯運算 and,unless 和 or 操做中默認與右向量中的全部元素進行匹配。

聚合操做

Prometheus 還提供了下列內置的聚合操做符,這些操做符做用域瞬時向量。能夠將瞬時表達式返回的樣本數據進行聚合,造成一個具備較少樣本值的新的時間序列。

  • sum (求和)
  • min (最小值)
  • max (最大值)
  • avg (平均值)
  • stddev (標準差)
  • stdvar (標準差別)
  • count (計數)
  • count_values (對 value 進行計數)
  • bottomk (樣本值最小的 k 個元素)
  • topk (樣本值最大的k個元素)
  • quantile (分佈統計)

這些操做符被用於聚合全部標籤維度,或者經過 without 或者 by 子語句來保留不一樣的維度。

  • without 用於從計算結果中移除列舉的標籤,而保留其它標籤。
  • by 則正好相反,結果向量中只保留列出的標籤,其他標籤則移除。

經過 without 和 by 能夠按照樣本的問題對數據進行聚合。

例如:

若是指標 http_requests_total 的時間序列的標籤集爲 application, instance, 和 group,咱們能夠經過如下方式計算全部 instance 中每一個 application 和 group 的請求總量:

sum(http_requests_total) without (instance)

等價於

`
sum(http_requests_total) by (application, group)
`

若是隻須要計算整個應用的 HTTP 請求總量,能夠直接使用表達式:

`sum(http_requests_total)
`

count_values 用於時間序列中每個樣本值出現的次數。count_values 會爲每個惟一的樣本值輸出一個時間序列,而且每個時間序列包含一個額外的標籤。

這個標籤的名字由聚合參數指定,同時這個標籤值是惟一的樣本值。

例如要計算運行每一個構建版本的二進制文件的數量:

count_values("version", build_version)
返回結果以下:

{count="641"}   1
{count="3226"}  2
{count="644"}   4

topk 和 bottomk

則用於對樣本值進行排序,返回當前樣本值前 n 位,或者後 n 位的時間序列。

獲取 HTTP 請求數前 5 位的時序樣本數據,可使用表達式:

topk(5, http_requests_total)

quantile 用於計算當前樣本數據值的分佈狀況 quantile(φ, express) ,其中 0 ≤ φ ≤ 1

例如,當 φ 爲 0.5 時,即表示找到當前樣本數據中的中位數:

quantile(0.5, http_requests_total)
返回結果以下:

{}   656

內置函數

Prometheus 提供了其它大量的內置函數,能夠對時序數據進行豐富的處理。如上文提到的irate

100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )

經常使用的有:

兩分鐘內的平均CPU使用率:

rate(node_cpu[2m])

irate(node_cpu[2m])

須要注意的是使用rate或者increase函數去計算樣本的平均增加速率,容易陷入「長尾問題」當中,

其沒法反應在時間窗口內樣本數據的突發變化。 

例如,對於主機而言在2分鐘的時間窗口內,可能在某一個因爲訪問量或者其它問題致使CPU佔用100%的狀況,

可是經過計算在時間窗口內的平均增加率卻沒法反應出該問題。

爲了解決該問題,PromQL提供了另一個靈敏度更高的函數irate(v range-vector)。

irate一樣用於計算區間向量的計算率,可是其反應出的是瞬時增加率。

irate函數是經過區間向量中最後兩個兩本數據來計算區間向量的增加速率。

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

irate函數相比於rate函數提供了更高的靈敏度,不過當須要分析長期趨勢或者在告警規則中,irate的這種靈敏度反而容易形成干擾。

所以在長期趨勢分析或者告警中更推薦使用rate函數。

完整的函數列表爲:

  • abs()
  • absent()
  • ceil()
  • changes()
  • clamp_max()
  • clamp_min()
  • day_of_month()
  • day_of_week()
  • days_in_month()
  • delta()
  • deriv()
  • exp()
  • floor()
  • histogram_quantile()
  • holt_winters()
  • hour()
  • idelta()
  • increase()
  • irate()
  • label_join()
  • label_replace()
  • ln()
  • log2()
  • log10()
  • minute()
  • month()
  • predict_linear()
  • rate()
  • resets()
  • round()
  • scalar()
  • sort()
  • sort_desc()
  • sqrt()
  • time()
  • timestamp()
  • vector()
  • year()
  • <aggregation>_over_time()

API訪問

Prometheus當前穩定的HTTP API能夠經過/api/v1訪問

錯誤狀態碼:

  • 404 Bad Request:當參數錯誤或者缺失時。
  • 422 Unprocessable Entity 當表達式沒法執行時。
  • 503 Service Unavailiable 當請求超時或者被中斷時。

全部的API請求均使用如下的JSON格式:

{
  "status": "success" | "error",
  "data": <data>,

  // 爲error時,有以下報錯信息
  "errorType": "<string>",
  "error": "<string>"
}

經過HTTP API咱們能夠分別經過/api/v1/query和/api/v1/query_range查詢PromQL表達式當前或者必定時間範圍內的計算結果。

瞬時數據查詢

URL請求參數:

  • query=:PromQL表達式。
  • time=:用於指定用於計算PromQL的時間戳。可選參數,默認狀況下使用當前系統時間。
  • timeout=:超時設置。可選參數,默認狀況下使用-query,timeout的全局設置。
$ curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'

返回:

{
   "status" : "success",
   "data" : {
      "resultType" : "vector",
      "result" : [
         {
            "metric" : {
               "__name__" : "up",
               "job" : "prometheus",
               "instance" : "localhost:9090"
            },
            "value": [ 1435781451.781, "1" ]
         },
         {
            "metric" : {
               "__name__" : "up",
               "job" : "node",
               "instance" : "localhost:9100"
            },
            "value" : [ 1435781451.781, "0" ]
         }
      ]
   }
}
區間查詢

URL請求參數:

  • query=: PromQL表達式。
  • start=: 起始時間。
  • end=: 結束時間。
  • step=: 查詢步長。
  • timeout=: 超時設置。可選參數,默認狀況下使用-query,timeout的全局設置。
$ curl 'http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s'

返回:

{
   "status" : "success",
   "data" : {
      "resultType" : "matrix",
      "result" : [
         {
            "metric" : {
               "__name__" : "up",
               "job" : "prometheus",
               "instance" : "localhost:9090"
            },
            "values" : [
               [ 1435781430.781, "1" ],
               [ 1435781445.781, "1" ],
               [ 1435781460.781, "1" ]
            ]
         },
         {
            "metric" : {
               "__name__" : "up",
               "job" : "node",
               "instance" : "localhost:9091"
            },
            "values" : [
               [ 1435781430.781, "0" ],
               [ 1435781445.781, "0" ],
               [ 1435781460.781, "1" ]
            ]
         }
      ]
   }
}

本文爲容器監控實踐系列文章,完整內容見:container-monitor-book

相關文章
相關標籤/搜索