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处于多核、低负载的情况下,值的差异会被放大,从而导致出现负数的情况。


分享到:


相關文章: