目录
- 一、PromQL 的指标类型
- 1.1 Counter
- 1.2 Gauge
- 1.3 Histogram
- 1.4 Summary
- 1.5 Histogram 与 Summary 的异同
- 二、Prometheus 的聚合函数
- 三、PromQL 的聚合表达式
一、PromQL 的指标类型
PromQL 有四个指标类型:
-
Counter :计数器,用于保存单调递增型的数据;例如站点访问次数等。数据单调递增,不支持减少,不能为负值,重启进程后,会被重置回 0 ;
-
Gauge :仪表盘,用于存储有着起伏特征的指标数据,例如内存空闲大小等。数据可变大,可变小;重启进程后,会被重置;
-
Histogram :累积直方图,将时间范围内的数据划分成不同的时间段,并各自评估其样本个数及样本值之和,因而可计算出分位数;
可用于分析因异常值而引起的平均值过大的问题;
分位数计算要使用专用的 histogram_quantile 函数; -
Summary :类似于 Histogram,但会在客户端直接计算并上报分位数;
1.1 Counter
通常,Counter 的总数并没有直接作用,而是需要借助于 rate、topk、increase 和 irate 等函数来生成样本数据的变化状况(增长率):
topk(3, prometheus_http_requests_total)
获取该指标下 http 请求总数排名前 3 的时间序列
rate(prometheus_http_requests_total[1h])
获取 1 小内,该指标下各时间序列上的 http 总请求数的增长速率
irate(prometheus_http_requests_total[1h])
irate 为高灵敏度函数,用于计算指标的瞬时速率,基于样本范围内的最后两个样本进行计算,相较于 rate 函数来说,irate 更适用于短期时间范围内的变化速率分析。
1.2 Gauge
Gauge 用于存储其值可增可减的指标的样本数据,常用于进行求和、取平均值、最小值、最大值等聚合计算;也会经常结合 PromQL 的 delta 和 predict_linear 函数使用:
●delta 函数计算范围向量中每个时间序列元素的第一个值与最后一个值之差,从而展示不同时间点上的样本值的差值。
delta(cpu_temp_celsius{host="node01"}[2h])
返回该服务器上的CPU温度与2小时之前的差异
●predict_linear 函数可以预测时间序列 v 在 t 秒后的值,它通过线性回归的方式,对样本数据的变化趋势做出预测。
predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600)
基于 2 小时的样本数据,来预测主机可用磁盘空间在 4 个小时之后的剩余情况
1.3 Histogram
对于 Prometheus 来说,Histogram 会在一段时间范围内对数据进行采样(通常是请求持续时长或响应大小等),并将其计入可配置的 bucket(存储桶)中 ,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。
Prometheus 取值间隔的划分采用的是累积区间间隔机制,即每个 bucket 中的样本均包含了其前面所有 bucket 中的样本,因而也称为累积直方图。
Histogram 类型的每个指标有一个基础指标名称 ,它会提供多个时间序列:
●_sum :所有样本值的总和
●_count :总的采样次数,它自身本质上是一个 Counter 类型的指标
●_bucket{le=“<上边界>”} :观测桶的上边界,即样本统计区间,表示样本值小于等于上边界的所有样本数量
_bucket{le=“+Inf”} :最大区间(包含所有样本)的样本数量
使用 histogram
在大多数情况下人们一般倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统 API 调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些 Web 页面的响应时间落到中位数的情况,而这种现象被称为长尾问题。
为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10 ms 之间的请求数有多少,而 10~20 ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram和Summary都是为了能够解决这样问题的存在,通过 Histogram 和 Summary 类型的监控指标,我们可以快速了解监控样本的分布情况。
http 请求响应时间 <= 0.005 秒 的请求次数为 10
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.005"} 10
http 请求响应时间 <= 0.01 秒 的请求次数为 15
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.01"} 15
http 请求响应时间 <= 0.025 秒 的请求次数为 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.025"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.05"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.075"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.1"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.25"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.5"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.75"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="1.0"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="2.5"} 18
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="5.0"} 20
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="7.5"} 20
prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="+Inf"} 20
所有样本值的大小总和,命名为 _sum
prometheus_http_request_duration_seconds_sum{handler="/metrics"} 10.107670803000001
样本总数,命名为 _count ,效果与 _bucket{le=“+Inf”} 相同
prometheus_http_request_duration_seconds_count{handler="/metrics"} 20
注意:
bucket 可以理解为是对数据指标值域的一个划分,划分的依据应该基于数据值的分布。注意后面的样本是包含前面的样本,假设
prometheus_http_request_duration_seconds_bucket{…,le=“0.01”} 的值为 10,而 prometheus_http_request_duration_seconds_bucket{…,le=“0.05”} 的值为 30,那么意味着这 30 个样本中,有 10 个是小于 0.01s 的,其余 20 个采样点的响应时间是介于 0.01s 和 0.05s 之间的。
累积间隔机制生成的样本数据需要额外使用内置的 histogram_quantile 函数即可根据 Histogram 指标来计算相应的分位数(quantile),即某个 bucket 的样本数在所有样本数中占据的比例。
●histogram_quantile 函数在计算分位数时会假定每个区间内的样本满足线性分布状态,因而它的结果仅是一个预估值,并不完全准确
●预估的准确度取决于bucket区间划分的粒度;粒度越大,准确度越低
例如,假设 http 请求响应时间的样本的 9 分位数(quantile=0.9)的上边界为 0.01,即表示小于等于 0.01 的样本值的数量占总体样本值的 90%
histogram_quantile(prometheus_http_request_duration_seconds_bucket{handler="/metrics",le="0.01"}) 0.9
1.4 Summary
Histogram 在客户端仅是简单的桶划分和分桶计数,分位数计算由 Prometheus Server 基于样本数据进行估算,因而其结果未必准确,甚至不合理的 bucket 划分会导致较大的误差。
Summary 是一种类似于 Histogram 的指标类型,但它在客户端于一段时间内(默认为 10 分钟)的每个采样点进行统计,计算并存储了分位数数值,Server 端直接抓取相应值即可。
对于每个指标,Summary 以指标名称 为前缀,生成如下几个指标序列:
- _sum :统计所有样本值之和
- _count :统计所有样本总数
- {quantile=“x”} :统计样本值的分位数分布情况,分位数范围:0 ≤ x ≤ 1
示例:
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.5"} 0.012352463
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.9"} 0.014458005
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.99"} 0.017316173
prometheus_tsdb_wal_fsync_duration_seconds_sum 2.888716127000002
prometheus_tsdb_wal_fsync_duration_seconds_count 216
从上面的样本中可以得知当前Promtheus Server进行 wal_fsync 操作的总次数为 216 次,耗时 2.888716127000002s。 其中中位数(quantile=0.5)的耗时为 0.012352463s,9分位数(quantile=0.9)的耗时为0.014458005s。
1.5 Histogram 与 Summary 的异同
它们都包含了 _sum 和 _count 指标,Histogram 需要通过 _bucket 来计算分位数,而 Summary 则直接存储了分位数的值。
二、Prometheus 的聚合函数
一般说来,单个指标的价值不大,监控场景中往往需要联合并可视化一组指标,这种联合机制即是指“聚合”操作,例如,将计数、求和、平均值、分位数、标准差及方差等统计函数应用于时间序列的样本之上生成具有统计学意义的结果等。
对查询结果事先按照某种分类机制进行分组(group by)并将查询结果按组进行聚合计算也是较为常见的需求,例如分组统计、分组求平均值、分组求和等。
Prometheus 的聚合操作由聚合函数针对一组值进行计算并返回值作为结果。
Prometheus 内置提供的 11 个聚合函数,也称为聚合运算符:
函数 | 含义 |
---|---|
sum() | 对样本值求和 |
min() | 求取样本值中的最小者 |
max() | 求取样本值中的最大者 |
avg() | 对样本值求平均值 |
count() | 对分组内的时间序列进行数量统计 |
stddev() | 对样本值求标准差,以帮助用户了解数据的波动大小(或称之为波动程度) |
stdvar() | 对样本值求方差,它是求取标准差过程中的中间状态 |
topk() | 逆序返回分组内的样本值最大的前 k 个时间序列及其值,即最大的 k 个样本值 |
bottomk() | 顺序返回分组内的样本值最小的前 k 个时间序列及其值,即最小的 k 个样本值 |
quantile() | 分位数,用于评估数据的分布状态,该函数会返回分组内指定的分位数的值,即数值落在小于等于指定的分位区间的比例 |
count_values() | 对分组内的时间序列的样本值进行数量统计,即等于某值的样本个数 |
三、PromQL 的聚合表达式
PromQL 中的聚合操作语法格式可采用如下面两种格式之一:
- <聚合函数>(向量表达式) by|without (标签)
- <聚合函数> by|without (标签) (向量表达式)
分组聚合:先分组、后聚合
by : 仅使用by子句中指定的标签进行聚合,结果向量中出现但未被 by 指定的标签则会被忽略;
为了保留上下文信息,使用 by 子句时需要显式指定其结果中原本出现的 job、instance 等一类的标签。
without: 从结果向量中删除由 without 指定的标签,未指定的那部分标签则用作分组标准
示例:
(1)每台主机 CPU 在最近 5 分钟内的平均使用率
(1-avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) )* 100
(2)查询 1 分钟的 load average 的时间序列是否超过主机 CPU 数量 2 倍
node_load1 > on (instance) 2 * count (node_cpu_seconds_total{mode="idle"}) by (instance)
(3)计算主机内存使用率
可用内存空间:空闲内存、buffer、cache 指标之和
node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes
已用内存空间:总内存空间减去可用空间
node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
使用率:已用空间除以总空间
(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100
针对查看k8s的监控可使用以下表达式:
(4)计算所有 node 节点所有容器总计内存:
sum by (instance) (container_memory_usage_bytes{instance=~"node*"})/1024/1024/1024
(5)计算 node01 节点最近 1m 所有容器 cpu 使用率:
sum (rate(container_cpu_usage_seconds_total{instance="node01"}[1m])) / sum (machine_cpu_cores{instance =~"node01"}) * 100
(6)计算最近 1m 所有容器 cpu 使用率
sum by (id) (rate(container_cpu_usage_seconds_total{id!="/"}[1m]))
(7)查询 K8S 集群中最近 1m 每个 Pod 的 CPU 使用率
sum by (name) (rate(container_cpu_usage_seconds_total{image!="", name!=""}[1m]))