01.15 ​PromQL:拆解CPU利用率

PromQL是Prometheus內置的數據查詢語言,其提供對時間序列數據豐富的查詢,聚合以及邏輯運算能力的支持。並且被廣泛應用在Prometheus的日常應用當中,包括對數據查詢、可視化、告警處理當中,下面我們講簡單瞭解一下PromQL語言,並且從簡單的例子入手。

數據類型

在Prometheus的表達式語言中,任何表達式或者子表達式都可以歸為四種類型:

1.instant vector 瞬時向量 :一組時間序列,包含每個時間序列的單個樣本,所有時間序列都共享相同的時間戳。

2.range vector 範圍向量 :一組時間序列,包含每個時間序列隨時間變化的一系列數據點。
3.scalar 標量 :一個簡單的浮點值
4.string 字符串 :一個當前沒有被使用的簡單字符串


​PromQL:拆解CPU利用率


操作符

使用PromQL除了能夠方便的按照查詢和過濾時間序列以外,PromQL還支持豐富的操作符,用戶可以使用這些操作符對進一步的對事件序列進行二次加工。這些操作符包括:數學運算符,邏輯運算符,布爾運算符等等。

PromQL支持的所有數學運算符如下所示:

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

舉例說明↓↓↓

例如我們查詢主機的內存大小,返回的是Bytes,如果我要把他轉換成G可以使用一下表達式:

node_memory_MemTotal_bytes / 1024 /1024 /1024

返回的結果是一個瞬時向量。兩個瞬時向量之間的數學計算例如:

node_disk_written_bytes_total + node_disk_read_bytes_total

那麼我們會發現是根據表達式的標籤進行數學運算,分別算出vda、vdb的磁盤io。

Prometheus支持以下布爾運算符如下:

== (相等)

!= (不相等)

> (大於)

< (小於)

>= (大於等於)

<= (小於等於)

使用bool修飾符、返回匹配的查詢結果↓↓↓

例如:通過數學運算符我們可以很方便的計算出,當前所有主機節點的內存使用率:

(node_memory_bytes_total - node_memory_free_bytes_total) / node_memory_bytes_total

而在我們寫告警規則的時候我們需要篩選出,內存使用率超過百分之95的主機、則可以使用布爾運算表達式:

(node_memory_bytes_total - node_memory_free_bytes_total) / node_memory_bytes_total > 0.95

Prometheus 支持以下集合運算符:

and (並且)
or (或者)
unless (排除)

通過集合運算,可以在兩個瞬時向量與瞬時向量之間進行相應的集合操作↓↓↓

vector1 and vector2 會產生一個由 vector1 的元素組成的新的向量。該向量包含 vector1 中完全匹配 vector2 中的元素組成。

vector1 or vector2 會產生一個新的向量,該向量包含 vector1 中所有的樣本數據,以及 vector2 中沒有與 vector1 匹配到的樣本數據。

vector1 unless vector2 會產生一個新的向量,新向量中的元素由 vector1 中沒有與 vector2 匹配的元素組成。

優先級

在 Prometheus 系統中,二元運算符優先級從高到低的順序為:

^

*, /, %

+, -

==, !=, <=, =, >

and, unless

or

具有相同優先級的運算符是滿足結合律的(左結合)。例如,2 3 % 2 等價於 (2 3) % 2。運算符 ^ 例外,^ 滿足的是右結合,例如,2 ^ 3 ^ 2 等價於 2 ^ (3 ^ 2)。

聚合運算

Prometheus還提供了下列內置的聚合操作符,這些操作符作用於瞬時向量。可以將瞬時表達式返回的樣本數據進行聚合,形成一個新的時間序列。

sum (求和)

min (最小值)

max (最大值)

avg (平均值)

stddev (標準差)

stdvar (標準差異)

count (計數)

count_values (對value進行計數)

bottomk (後n條時序)

topk (前n條時序)

quantile (分佈統計)


​PromQL:拆解CPU利用率


使用聚合操作的語法如下:

<aggr-op>([parameter,] <vector>) [without|by (<label>)]/<label>/<vector>/<aggr-op>

其中只有count_values, quantile, topk, bottomk支持參數(parameter)。

without用於從計算結果中移除列舉的標籤,而保留其它標籤。by則正好相反,結果向量中只保留列出的標籤,其餘標籤則移除。通過without和by可以按照樣本的問題對數據進行聚合。

<code>sum(http_requests_total) without (instance)等於:sum(http_requests_t;otal) by (code,handler,job,method)/<code>

如果只需要計算整個應用的HTTP請求總量,可以直接使用表達式:sum(http_requests_total)

<code>查詢數據的平均值:avg(http_requests_total)查詢最靠前的3個值:topk(3, http_requests_total)/<code>

常用函數

Prometheus為不同的數據類型提供了非常多的計算函數,有個小技巧就是遇到counter數據類型,在做任何操作之前,先套上一個rate()或者increase()函數。下面介紹一些比較常用的函數幫助理解:

increase()函數:

該函數配合counter數據類型使用,獲取區間向量中的第一個和最後一個樣本並返回其增長量。如果除以一定時間就可以獲取該時間內的平均增長率:


<code>increase(node_cpu_seconds_total[2m]) / 120 #主機節點最近兩分鐘內的平均CPU使用率/<code>

rate()函數:

該函數配合counter類型數據使用,取counter在這個時間段中的平均每秒增量。

<code>rate(node_cpu_seconds_total[2m]) #直接計算區間向量在時間窗口內平均增長速率/<code>


​PromQL:拆解CPU利用率


sum()函數:

在實際工作中CPU大多是多核的,而node_cpu會將每個核的數據都單獨顯示出來,我們其實不會關注每個核的單獨情況,而是關心總的CPU情況。使用sum()函數進行求和後可以得出一條總的數據,但sum()是將所有機器的數據都進行了求和,所以還要再使用by (instance)或者by (cluster_name)就可以取出單個服務器或者一組服務器的CPU數據。上面的公式可以進化為:

<code>sum( increase(node_cpu_seconds_total[1m]) ) #先找出每一個,然後再合併/<code>

Topk()函數:

該函數可以從大量數據中取出排行前N的數值,N可以自定義。比如監控了100臺服務器的320個CPU,用這個函數就可以查看當前負載較高的那幾個,用於報警:

<code>topk(3, http_requests_total) #統計最靠前的3個值/<code>

predict_linear()函數:對曲線變化速率進行計算,起到一定的預測作用。比如當前這1個小時的磁盤可用率急劇下降,這種情況可能導致磁盤很快被寫滿,這時可以使用該函數,用當前1小時的數據去預測未來幾個小時的狀態,實現提前告警:


<code>predict_linear( node_filesystem_free_bytes{mountpoint="/"}[1h],4*3600 ) < 0 #如果未來4小時後磁盤使用率為負數就會報警/<code>

CPU利用率表達式拆解


1.先把key找出來,比如是為了查看CPU的使用率,那麼就應該使用:



<code>node_cpu這個key/<code>


2.在node_cpu這個key的基礎上把idle的CPU時間和全部CPU時間過濾出來,使用{}做過濾:


<code>node_cpu_seconds_total{ mode='idle' }  #找出空閒CPU的值node_cpu_seconds_total  #不寫其他參數代表ALL/<code>


3.使用increase()函數把1分鐘的數據抓取出來,這個時候取出來的是每個CPU的數據:


<code>increase(node_cpu_seconds_totalmode='idle'}[1m])/<code>
​PromQL:拆解CPU利用率

<code>increase(node_cpu_seconds_totalmode='idle'}[1m])/<code>


4.使用sum()函數求和每個CPU的數據,得到單獨一個數據:


<code>sum( increase(node_cpu_seconds_total{mode='idle'}[1m]) )/<code>


5.sum()函數雖然把每個CPU的數據進行了求和,但是還把每臺服務器也進行了求和,所有服務器的CPU數據都相同了,還需要進行一次處理。這裡又引出了一個新函數 by (instance)。它會把sum求和到一起的數值按照指定方式進行拆分,instance代表的是機器名。如果不寫by (instance)的話就需要在{}中寫明需要哪個實例的數據。

<code>sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by(instance) /sum(increase(node_cpu_seconds_total[1m])) by(instance)/<code>


​PromQL:拆解CPU利用率


6.獲取CPU空閒時間佔比:

<code>sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by(instance) /sum(increase(node_cpu_seconds_total[1m])) by(instance)/<code>

7.CPU的利用率:

<code>1-(sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by(instance) /sum(increase(node_cpu_seconds_total[1m])) by(instance)) * 100/<code>


最終計算可能為負數,可能好多granafa模板都這樣,當cpu處於多核、低負載的情況下,值的差異會被放大,從而導致出現負數的情況。


分享到:


相關文章: