Prometheus(三)——PromQL

通过PromQL用户可以非常方便地对监控样本数据进行 统计分析,PromQL支持常见的运算操作符,同时PromQL中还提供了大量的内置函数可以实现对数据的高级处理。PromQL作为Prometheus的核心能力除了实 现数据的对外查询和展现,同时告警监控也是依赖PromQL实现的。

1、Prometheus的数据模型

1.1、理解时间序列

通过Node Exporter暴露的HTTP服务,Prometheus可以采集到当前主机所有监控指标的样本数 据。例如:

# HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",mode="idle"} 362812.7890625

# HELP node_load1 1m load average.
# TYPE node_load1 gauge
node_load1 3.0703125

其中非#开头的每一行表示当前Node Exporter采集到的一个监控样本:node_cpu和node_load1表明了当前指标 的名称、大括号中的标签则反映了当前样本的一些特征和维度、浮点数则是该监控样本的具体值。

1.2、样本

Prometheus会将所有采集到的样本数据以时间序列(time-series)的方式保存在内存数据库中,并且定时保存到 硬盘上。time-series是按照时间戳和值的序列顺序存放的,我们称之为向量(vector). 每条time-series通过 指标名称(metrics name)和一组标签集(labelset)命名。如下所示,可以将time-series理解为一个以时间为Y 轴的数字矩阵:

Prometheus(三)——PromQL_第1张图片

 在time-series中的每一个点称为一个样本(sample),样本由以下三部分组成:

        指标(metric):metric name和描述当前样本特征的labelsets;

        时间戳(timestamp):一个精确到毫秒的时间戳;

        样本值(value): 一个folat64的浮点型数据表示当前样本的值。

Prometheus(三)——PromQL_第2张图片

 1.3、指标(Metric)

在形式上,所有的指标(Metric)都通过如下格式标示:

{

指标的名称(metric name)可以反映被监控样本的含义(比如, http_request_total - 表示当前系统接收到的 HTTP请求总量)。指标名称只能由ASCII字符、数字、下划线以及冒号组成并必须符合正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]* 。

标签(label)反映了当前样本的特征维度,通过这些维度Prometheus可以对样本数据进行过滤,聚合等。标签的名 称只能由ASCII字符、数字以及下划线组成并满足正则表达式 [a-zA-Z_][a-zA-Z0-9_]* 。

其中以 __ 作为前缀的标签,是系统保留的关键字,只能在系统内部使用。标签的值则可以包含任何Unicode编码的 字符。在Prometheus的底层实现中指标名称实际上是以 __name__= 的形式保存在数据库中的,因此以 下两种方式均表示的同一条time-series:

 api_http_requests_total{method="POST", handler="/messages"}

等同于:

{__name__="api_http_requests_total",method="POST", handler="/messages"}

在Prometheus源码中指标(Metric)对应的数据结构,如下所示:

type Metric LabelSet

type LabelSet map[LabelName]LabelValue

type LabelName string

type LabelValue string

2、Promthues中监控指标的类型

为了能够帮助用户理解和区分这些不同监控指标之间的差异,Prometheus定义了4中不同的指标类型(metric type):Counter(计数器)、Gauge(仪表盘)、Histogram(直方图)、Summary(摘要)。

2.1、Counter:只增不减的计数器

Counter类型的指标其工作方式和计数器一样,只增不减(除非系统发生重置)。常见的监控指标,如 http_requests_total,node_cpu都是Counter类型的监控指标。 一般在定义Counter类型指标的名称时推荐 使用_total作为后缀。

Counter是一个简单但有强大的工具,例如我们可以在应用程序中记录某些事件发生的次数,通过以时序的形式存储 这些数据,我们可以轻松的了解该事件产生速率的变化。PromQL内置的聚合操作和函数可以用户对这些数据进行进一 步的分析:

例如,通过rate()函数获取HTTP请求量的增长率:

 rate(http_requests_total[5m])

查询当前系统中,访问量前10的HTTP地址:

topk(10, http_requests_total)

2.2、Gauge:可增可减的仪表盘

Gauge类型的指标侧重于反应系统的当前状态。因此这类指标的样本数据可增可减。常见指标如: node_memory_MemFree(主机当前空闲的内容大小)、node_memory_MemAvailable(可用内存大小)都是 Gauge类型的监控指标。

通过Gauge指标,用户可以直接查看系统的当前状态:

node_memory_MemFree

通过PromQL内置函数delta()可以获取样本在一段时间返回内的变化情况。例如,计算 CPU温度在两个小时内的差异:

 delta(cpu_temp_celsius{host="zeus"}[2h])

使用deriv()计算样本的线性回归模型,甚至是直接使用predict_linear()对数据的变化趋势进行预测。例 如,预测系统磁盘空间在4个小时之后的剩余情况:

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

2.3、使用Histogram和Summary分析数据分布情况

Histogram和Summary主用用于统计和分析样本的分布情况。

在大多数情况下人们都倾向于使用某些量化指标的平均值,例如CPU的平均使用率、页面的平均响应时间。这种方式的 问题很明显,以系统API调用的平均响应时间为例:如果大多数API请求都维持在100ms的响应时间范围内,而个别请 求的响应时间需要5s,那么就会导致某些WEB页面的响应时间落到中位数的情况,而这种现象被称为长尾问题。

为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在0~10ms之间 的请求数有多少而10~20ms之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram和 Summary都是为了能够解决这样问题的存在,通过Histogram和Summary类型的监控指标,我们可以快速了解监控样 本的分布情况。

例如,指标prometheus_tsdb_wal_fsync_duration_seconds的指标类型为Summary。 它记录了 Prometheus Server中wal_fsync处理的处理时间,通过访问Prometheus Server的/metrics地址,可以获取 到以下监控样本数据:

# HELP prometheus_tsdb_wal_fsync_duration_seconds Duration of WAL fsync.
# TYPE prometheus_tsdb_wal_fsync_duration_seconds summary
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

从上面的样本中可以得知当前Prometheus Server进行wal_fsync操作的总次数为216次,耗时 2.888716127000002s。其中中位数(quantile=0.5)的耗时为0.012352463,9分位数(quantile=0.9)的 耗时为0.014458005s。

在Prometheus Server自身返回的样本数据中,我们还能找到类型为Histogram的监控指标 prometheus_tsdb_compaction_chunk_range_bucket。

 # HELP prometheus_tsdb_compaction_chunk_range Final time range of chunks on their first compaction
# TYPE prometheus_tsdb_compaction_chunk_range histogram
prometheus_tsdb_compaction_chunk_range_bucket{le="100"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="6400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="25600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="102400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="409600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1.6384e+06"} 260
prometheus_tsdb_compaction_chunk_range_bucket{le="6.5536e+06"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="2.62144e+07"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="+Inf"} 780
prometheus_tsdb_compaction_chunk_range_sum 1.1540798e+09
prometheus_tsdb_compaction_chunk_range_count 780

与Summary类型的指标相似之处在于Histogram类型的样本同样会反应当前指标的记录的总数(以_count作为后缀) 以及其值的总量(以_sum作为后缀)。不同在于Histogram指标直接反应了在不同区间内样本的个数,区间通过标签 len进行定义。

3、初识PromQL

PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。并 且被广泛应用在Prometheus的日常应用当中,包括对数据查询、可视化、告警处理当中。可以这么说,PromQL是 Prometheus所有应用场景的基础,理解和掌握PromQL是Prometheus入门的第一课。

3.1、查询时间序列

当Prometheus通过Exporter采集到相应的监控指标样本数据后,我们就可以通过PromQL对监控样本数据进行查 询。

当我们直接使用监控指标名称查询时,可以查询该指标下的所有时间序列。如:

 http_requests_total

等同于:

http_requests_total{}

该表达式会返回指标名称为http_requests_total的所有时间序列:

 http_requests_total{code="200",handler="alerts",instance="localhost:9090",job="prometheus",method="get"}=([email protected])
http_requests_total{code="200",handler="graph",instance="localhost:9090",job="prometheus",method="get"}=([email protected])

PromQL还支持用户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式:完全匹配和正 则匹配。

PromQL支持使用 = 和 != 两种完全匹配模式:

        通过使用 label=value 可以选择那些标签满足表达式定义的时间序列;

        反之使用 label!=value 则可以根据标签匹配排除时间序列;

http_requests_total{instance="localhost:9090"}
http_requests_total{instance!="localhost:9090"}

除了使用完全匹配的方式对时间序列进行过滤以外,PromQL还可以支持使用正则表达式作为匹配条件,多个表达式之 间使用 | 进行分离:

        使用 label=~regx 表示选择那些标签符合正则表达式定义的时间序列;

        反之使用 label !~regx 进行排除;

例如,如果我们只需要查询所有http_requests_total时间序列中满足标签instance为localhost:9090的时间 序列,则可以使用如下表达式:

http_requests_total{environment=~"staging|testing|development",method!="GET"}

3.2、范围查询

直接通过类似于 PromQL 表达式http_requests_total 查询时间序列时,返回值中只会包含该时间序列中的最新的一个样本值,这样的返回结果我们称之为瞬时向量。而相应的这样的表达式称之为__ 瞬时向量表达式。
如果我们想过去一段时间范围内的样本数据时,我们则需要使用区间向量表达式。区间向量表达式和瞬时向量表达式之间的差异在于在区间向量表达式中我们需要定义时间选择的范围,时间范围通过时间范围选择器 [] 进行定义。
例如,通过以下表达式可以选择最近 5 分钟内的所有样本数据:
http_request_total{}[5m]
该表达式将会返回查询到的时间序列中最近 5 分钟的所有样本数据

Prometheus(三)——PromQL_第3张图片

 通过区间向量表达式查询到的结果我们称为区间向量。

除了使用 m 表示分钟以外, PromQL 的时间范围选择器支持其它时间单位:
        s - 秒
        m - 分钟
        h - 小时
        d - 天
        w - 周
        y - 年

3.3、时间位移操作

在瞬时向量表达式或者区间向量表达式中,都是以当前时间为基准:
http_request_total{} # 瞬时向量表达式,选择当前最新的数据
http_request_total{}[5m] # 区间向量表达式,选择以当前时间为基准,5分钟内的数据
而如果我们想查询, 5 分钟前的瞬时样本数据,或昨天一天的区间内的样本数据呢 ? 这个时候我们就可以使用位移操 作,位移操作的关键字为offset
http_request_total{} offset 5m
http_request_total{}[1d] offset 1d

3.4、使用聚合操作

一般来说,如果描述样本特征的标签 (label) 在并非唯一的情况下,通过 PromQL 查询数据,会返回多条满足这些特征维度的时间序列。而PromQL 提供的聚合操作可以用来对这些时间序列进行处理,形成一条新的时间序列:
# 查询系统所有http请求的总量
sum(http_request_total) 
# 按照mode计算主机CPU的平均使用时间
avg(node_cpu) by (mode) 
# 按照主机查询各个主机的CPU使用率 
sum(sum(irate(node_cpu{mode!='idle'}[5m])) / sum(irate(node_cpu[5m]))) by (instance)

3.5、标量和字符串

除了使用瞬时向量表达式和区间向量表达式以外, PromQL 还直接支持用户使用标量 (Scalar) 和字符串 (String)
标量( Scalar ):一个浮点型的数字值
标量只有一个数字,没有时序。
字符串( String ):一个简单的字符串值
直接使用字符串,作为 PromQL 表达式,则会直接返回字符串。
"this is a string" 
'these are unescaped: \n \\ \t' 
`these are not unescaped: \n ' " \t`

3.6、合法的PromQL表达式

所有的 PromQL 表达式都必须至少包含一个指标名称 ( 例如 http_request_total) ,或者一个不会匹配到空字符串的 标签过滤器( 例如 {code=”200”})
因此以下两种方式,均为合法的表达式:
http_request_total # 合法 
http_request_total{} # 合法 
{method="get"} # 合法


{job=~".*"} # 不合法
同时,除了使用 {label=value} 的形式以外,我们还可以使用内置的 __name__ 标签来指定监控指标名 称:
{__name__=~"http_request_total"} # 合法 
{__name__=~"node_disk_bytes_read|node_disk_bytes_written"} # 合法

4、PromQL操作符

PromQL 支持丰富的操作符,用户可以使用这些操作符 对进一步的对事件序列进行二次加工。这些操作符包括:数学运算符,逻辑运算符,布尔运算符等等。

4.1、数学运算

PromQL 支持的所有数学运算符如下所示:
        +          ( 加法 )
        -          ( 减法 )
        *          ( 乘法 )
        /          ( 除法 )
        %         ( 求余 )
        ^          ( 幂运算 )
例如,我们可以通过指标 node_memory_free_bytes_total 获取当前主机可用的内存空间大小,其样本单位为 Bytes。这是如果客户端要求使用 MB 作为单位响应数据,那只需要将查询到的时间序列的样本值进行单位换算即可:
node_memory_free_bytes_total / (1024 * 1024)
node_memory_free_bytes_total 表达式会查询出所有满足表达式条件的时间序列,在上一小节中我们称该表达式 为瞬时向量表达式,而返回的结果成为瞬时向量。
当瞬时向量与标量之间进行数学运算时,数学运算符会依次作用域瞬时向量中的每一个样本值,从而得到一组新的时 间序列。
而如果是瞬时向量与瞬时向量之间进行数学运算时,过程会相对复杂一点。 例如,如果我们想根据node_disk_bytes_written和 node_disk_bytes_read 获取主机磁盘 IO 的总量,可以使用如下表达式:
node_disk_bytes_written + node_disk_bytes_read
那这个表达式是如何工作的呢?依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找 到匹配元素,则直接丢弃。同时新的时间序列将不会包含指标名称。

4.2、使用布尔运算过滤时间序列

Prometheus 支持以下布尔运算符:
        ==          ( 相等 )
        !=           ( 不相等 )
        >            ( 大于 )
        <            ( 小于 )
        >=         ( 大于等于 )
        <=          ( 小于等于 )
布尔运算则支持用户根据时间序列 中样本的值,对时间序列进行过滤

例如,系统管理员在排查问题的时候可能只想知道当前内存使用率超过95%的主机

(node_memory_bytes_total - node_memory_free_bytes_total) / node_memory_bytes_total > 0.95
布尔运算符的默认行为是对时序数据进行过滤。而在其它的情况下我们可能需要的是真正的布尔结果。

4.3、使用bool修饰符改变布尔运算符的行为

布尔运算符的默认行为是对时序数据进行过滤。而在其它的情况下我们可能需要的是真正的布尔结果。
例如,只需要知道当前模块的HTTP 请求量是否 >=1000 ,如果大于等于 1000 则返回 1 true )否则返回 0 false )。这时可以使用bool 修饰符改变布尔运算的默认行为。 例如:
http_requests_total > bool 1000
使用 bool 修改符后,布尔运算不会对时间序列进行过滤,而是直接依次返回瞬时向量中的各个样本数据与标量的比较结果 0或者 1 。从而形成一条新的时间序列。
http_requests_total{code="200",handler="query",instance="localhost:9090",job="prometheus",method="get"} 1
http_requests_total{code="200",handler="query_range",instance="localhost:9090",job="prometheus",method="get"} 0

4.4、使用集合运算符

Prometheus 支持以下集合运算符:
        and         ( 并且 )
        or            ( 或者 )
        unless      ( 排除 )
使用瞬时向量表达式能够获取到一个包含多个时间序列的集合,我们称为瞬时向量。 通过集合运算,可以在两个瞬时 向量与瞬时向量之间进行相应的集合操作。
vector1 and vector2 会产生一个由 vector1 的元素组成的新的向量。该向量包含 vector1 中完全匹配 vector2 中的元素组成。
vector1 or vector2 会产生一个新的向量,该向量包含 vector1 中所有的样本数据,以及 vector2 中没有与 vector1匹配到的样本数据。
vector1 unless vector2 会产生一个新的向量,新向量中的元素由 vector1 中没有与 vector2 匹配的元素组成。

4.5、操作符优先级

PromQL 操作符中优先级由高到低依次为:
1. ^ 
2. *, /, % 
3. +, - 
4. ==, !=, <=, <, >=, > 
5. and, unless 
6. or

4.6、匹配模式详解

向量与向量之间进行运算操作时会基于默认的匹配规则:依次找到与左边向量元素匹配(标签完全一致)的右边向量 元素进行运算,如果没找到匹配元素,则直接丢弃。
两种典型的匹配模式:一对一( one-to-one , 多对一( many-to-one )或一对多 (one-to-many )。
一对一匹配 : 一对一匹配模式会从操作符两边表达式获取的瞬时向量依次比较并找到唯一匹配 ( 标签完全一致 )的样本值。默认情况 下,使用表达式:
vector1  vector2
在操作符两边表达式标签不一致的情况下,可以使用 on(label list) 或者 ignoring(label list )来修改标签的 匹配行为。
使用 ignoreing 可以在匹配时忽略某些标签。而 on 则用于将匹配行为限定在某些标签之内。
  ignoring(
多对一和一对多 多对一和一对多两种匹配模式指的是 侧的每一个向量元素可以与 侧的多个元素匹配的情况。在这种情况下, 必须使用 group 修饰符: group_left 或者 group_right 来确定哪一个向量具有更高的基数(充当 的角色)。
  ignoring(
多对一和一对多两种模式一定是出现在操作符两侧表达式返回的向量标签不一致的情况。因此需要使用 ignoring和 on 修饰符来排除或者限定匹配的标签列表。

5、PromQL聚合函数

        sum ( 求和 )
        min ( 最小值 )
        max ( 最大值 )
        avg ( 平均值 )
        stddev ( 标准差 )
        stdvar ( 标准差异 )
        count ( 计数 )
        count_values ( value 进行计数 )
        bottomk ( n 条时序 )
        topk ( n 条时序 )
        quantile ( 分布统计 )
其中只有 count_values , quantile , topk , bottomk 支持参数 (parameter)
使用聚合操作的语法如下:
 ([parameter,] ) [without|by (
without 用于从计算结果中移除列举的标签,而保留其它标签。
by 则正好相反,结果向量中只保留列出的标签,其余 标签则移除。
例如:
 sum(http_requests_total) without (instance)
等价于
 sum(http_requests_total) by (code,handler,job,method)
如果只需要计算整个应用的 HTTP 请求总量,可以直接使用表达式:
sum(http_requests_total)
count_values 用于时间序列中每一个样本值出现的次数。 count_values 会为每一个唯一的样本值输出一个时间序 列,并且每一个时间序列包含一个额外的标签。
 count_values("count", http_requests_total)
topk bottomk 则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。
获取 HTTP 请求数前 5 位的时序样本数据,可以使用表达式:
topk(5, http_requests_total)
quantile 用于计算当前样本数据值的分布情况 quantile(φ, express) 其中 0 ≤ φ ≤ 1

例如,找到当前样本数据中的中位数:

quantile(0.5, http_requests_total)

6、PromQL内置函数

计算 Counter 指标增长率
increase(v range-vector) 函数是 PromQL 中提供的众多内置函数之一。其中参数 v 是一个区间向量, increase 函数获取区间向量中的第一个后最后一个样本并返回其增长量。因此,可以通过以下表达式Counter 类型指标的增长 率:
increase(node_cpu[2m]) / 120
这里通过 node_cpu[2m] 获取时间序列最近两分钟的所有样本, increase 计算出最近两分钟的增长量,最后除以时 间120 秒得到 node_cpu 样本在最近两分钟的平均增长率。并且这个值也近似于主机节点最近两分钟内的平均 CPU 使用 率。
PromQL 中还直接内置了 rate(v range-vector) 函数, rate 函数可以直接计算区间 向量v 在时间窗口内平均增长速率。因此,通过以下表达式可以得到与 increase 函数相同的结果:
rate(node_cpu[2m])
PromQL 提供了另外一个灵敏度更高的函数 irate(v range-vector) irate 同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个两本数据来计算区间向量的增 长速率。这种方式可以避免在时间窗口范围内的 长尾问题 ,并且体现出更好的灵敏度,通过 irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。
irate(node_cpu[2m])
irate 函数相比于 rate 函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中, irate 的这种灵敏度 反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用rate 函数。
预测 Gauge 指标变化趋势
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
统计 Histogram 指标的分位数
Histogram 的分位数计算 需要通过histogram_quantile(φ float, b instant-vector) 函数进行计算。其中 φ 0<φ<1 )表示需要计算 的分位数,如果需要计算中位数φ 取值为 0.5 ,以此类推即可。
当计算 9 分位数时,使用如下表达式:
histogram_quantile(0.5, http_request_duration_seconds_bucket)

你可能感兴趣的:(Prometheus,运维,云原生,kubernetes)