相关文章推荐
刚毅的馒头  ·  语雀搭配Web ...·  1 月前    · 

PromQL

PromQL 是 Prometheus 内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。并被广泛应用在 Prometheus 的日常数据查询、可视化、告警处理当中。

可以这么说,PromQL 是 Prometheus 所有应用场景的基础,理解和掌握 PromQL 是 Prometheus 入门的第一课。

如果把 Prometheus 和常见的关系型数据库 MySQL 对比着看。每个指标就相当于 MySQL 中的表,PromQL 就类似于 MySQL 中的 Select 语句,标签筛选就类似于 Select 中加入了 Where 条件限制。

用户可以直接通过指标名称获取到 exporter 采集到的样本数据:

prometheus_http_requests_total

如图所示:

这样采集的数据可能会包含多个拥有不同标签的指标,为了更精确的获取到想要的指标数据,就需要进行标签筛选:

prometheus_http_requests_total{handler=~"/api.*|/metrics", code="200"}

如图所示:

PromQL 支持两种对于标签的匹配模式:

  • 完全匹配:= 和 !=
  • 正则匹配:=~ 和 !~
  • PromQL 合法性

    所有的 PromQL 表达式都必须至少包含一个指标名称,或一个不会匹配到空字符串的标签过滤器。

    合法的表达式

    # 只有指标名称
    prometheus_http_requests_total
    # 标签为空为指标
    prometheus_http_requests_total{}
    # 普通的标签筛选
    prometheus_http_requests_total{code="200"}
    # 正则的标签筛选
    prometheus_http_requests_total{handler=~"/api.*|/metrics", code="200"}
    # 只有标签的筛选
    {code="200"}
    # 使用名称的筛选
    {__name__="prometheus_http_requests_total"}
    

    不合法的表达式

    # 都是能匹配到空,所以不合法
    {job=~".*"}
    {job=""}
    

    瞬时向量和区间向量

    通过 PromQL 查询到的数据可以分为两类:

  • 瞬时向量:最新的值,常用于监控告警。
  • 区间向量:某一时间段范围的值,常用于数据分析。
  • 如果 PromQL 表达式中如果没有使用 时间范围选择器 [] 的,那么这个向量就是瞬时向量。

    node_memory_MemFree_bytes
    

    如图所示:

    node_memory_MemFree_bytes[1m]
    

    如图所示:

    显示画图:

    特别说明:

  • 由于采集间隔为 15s,所以获取 1 分钟的数据会返回 4 个值。
  • 绘图只能使用瞬时向量,因为一般的绘图都会自带一个时间选择器,如果使用区间向量则时间范围重复,出现报错。
  • 时间范围选择器支持 s(秒),m(分钟),h(小时),d(天),w(周),y(年)等单位,但是不能使用 1.5h,只能是 1h30m。
  • 前面查询到的数据都是从当前时间作为截止时间获取的,有时候也需要获取历史某个时间的值,此时就需要使用到时间偏移。

    # 获取 5 分钟前 1 分钟内的数据
    node_memory_MemFree_bytes[1m] offset 5m
    

    如图所示:

    数学运算符 / 算术运算符

    某些监控指标获取到的值有些时候是不好直接拿来使用的,需要进行一定的计算,于是便需要使用到算术运算符。

    常见的数学运算符包含以下:

  • +:加法
  • -:减法
  • *:乘法
  • /:除法
  • %:求余
  • ^:幂运算
  • 使用示例:

    # 获取可用内存为多少 M
    (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / 1024 / 1024
    

    如图所示:

    布尔运算符 / 比较运算符

    在监控告警的时候,需要对采集到的值进行判断,此时就需要使用到布尔运算符。

    常见的布尔运算符包含以下:

  • ==:相等
  • !=:不相等
  • >=:大于等于
  • <=:小于等于
  • 使用实例:

    # 获取内存使用率大于 10% 的数据
    (1 - ((node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / node_memory_MemTotal_bytes)) * 100 > 10
    

    如图所示:

    在某些场景下并不是为了筛选,而是想要获取布尔值(true 为 1,false 为 0):

    # 获取 bool 值
    (1 - ((node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / node_memory_MemTotal_bytes)) * 100 > bool 10
    

    如图所示:

    集合运算符

    集合运算符更多的还是用于数据的筛选。

    常见的集合运算符包含以下:

  • and:并且
  • or:或者
  • unless:排除
  • 使用示例:

    # 多条件筛选,并列条件,取交集
    prometheus_http_requests_total{code!="400"} and prometheus_http_requests_total{handler!~"/api.*"}
    # 多条件筛选,并列条件,取并集
    prometheus_http_requests_total{code="400"} or prometheus_http_requests_total{handler!~"/api.*"}
    # 多条件筛选,从结果中排除
    prometheus_http_requests_total unless prometheus_http_requests_total{code="200"}
    

    如图所示:

    操作符优先级

    对于复杂的 PromQL 表达式一般都会包含多种运算符,同种运算符,不同运算符之间都是由执行优先级的,具体先后顺序如下:

  • *, /, %
  • ==, !=, <=, <, >=, >
  • and, unless
  • 内置函数 / absent(取布尔值)

    以瞬时向量做参数,判断该瞬时向量是否有值:

  • 如果有值,则返回空向量。
  • 如果没有值,则返回不带标签的名称时间序列,值为 1。
  • 使用示例:

    # 有值返回空
    absent(prometheus_http_requests_total)
    # 没值返回 1
    absent(prometheus_http_requests_totalxxx)
    

    该方法可以用于判断给定的 PromQL 不存在。

    内置函数 / abs(绝对值)

    用于返回绝对值,将负数转换成正数。

    abs(node_memory_MemFree_bytes - node_memory_MemTotal_bytes)
    

    如图所示:

    内置函数 / round(四舍五入取整)

    返回结果四舍五入取整:

    round((node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100)
    

    如图所示:

    内置函数 / clamp_max(对比取值)

    clamp_max 用于比较瞬时向量和传入的值,谁小返回谁。clamp_min 反之。

    clamp_max(node_memory_MemFree_bytes / node_memory_MemTotal_bytes, 1)
    

    如图所示:

    内置函数 / label_join(新增标签)

    新增标签,但值来源于原来的标签的值的组合。

    label_join(prometheus_http_requests_total{handler="/metrics"}, "url", "==", "instance", "handler")
    

    结果如图:

    新标签名称第一个参数为分隔符,后面的就是各种需要组合的标签,每个组合的值中间都有一个分隔带。

    内置函数 / label_replace(标签替换)

    将筛选的标签值进行替换:

    label_replace(prometheus_http_requests_total{code="200"}, "code", "$1", "instance", "(.*):.*")
    

    如图所示:

    内置函数 / predict_linear(预测)

    该方法常用于监控告警中,用于预测未来发生故障的时间。

    # 根据 10 分钟的磁盘空闲情况推测 12 小时后磁盘空闲情况
    predict_linear(node_filesystem_free_bytes{mountpoint="/"}[10m], 12*3600) / 1024 / 1024 / 1024
    

    如图所示:

    内置函数 / rate(区间增长率)

    通过区间向量计算平均增长速率,该函数返回结果不带度量指标。

    # 以 5 分钟为基准计算请求的每秒增长率
    rate(prometheus_http_requests_total{handler="/metrics"}[1m])
    

    如图所示:

    内置函数 / irate(瞬时增长率)

    通 rate 类似,用于计算区间向量的增长率,但与 rate 不同的是他是用于计算瞬时向量的增长率,通过区间向量中最后两个样本数据来计算区间向量的增长速率。

    irate(prometheus_http_requests_total{handler="/metrics"}[1m])
    

    如图所示:

    特别说明:

    irate 只能用于绘制快速变化的计数器,在长期趋势分析或者告警中更推荐使用 rate 函数。因为使用 irate 函数时,速率的简短变化会重置 FOR 语句,形成的图形有很多波峰,难以阅读。

    内置函数 / sort(升序排序)

    将多组数据按照升序排序,相反则使用 sort_desc。

    sort(prometheus_http_requests_total{handler=~"/api/.*"})
    

    如图所示:

    内置函数 / delta(计算差值)

    用于计算一段时间的变化值。

    # 计算过去 1 小时磁盘使用了多少 M
    abs(delta(node_filesystem_free_bytes{mountpoint="/"}[1h]) / 1024 / 1024)
    

    如图所示:

    通过获取一个即时向量并聚合它的元素,从而得到一个新的瞬时向量。

    常用的聚合函数包含以下:

  • sum:求和
  • min:最小值
  • max:最大值
  • avg:平均值
  • count:元素个数
  • count_values:等于某值的元素个数
  • bottomk:最小的 k 个元素
  • topk:最大的 k 个元素
  • 语法格式如下:

    <聚合函数>([parameter,] <指标查询语句>) [without|by (<label list>)]
    

    其中参数 without ⽤于从计算结果中移除列举的标签,by 则相反,结果向量中只保留列出的标签。

    只有 count_values , quantile , topk , bottomk ⽀持参数 parameter。

    聚合函数 / sum(求和)

    使用实例:

    # 计算所有请求数量
    sum(prometheus_http_requests_total)
    

    如图所示:

    结合 by 使用:

    # by 通过指定的标签分组
    sum(prometheus_http_requests_total) by (code)
    

    如图所示:

    结合 without 使用:

    # without 剔除指定的标签后分组
    sum(prometheus_http_requests_total) without (handler)
    

    如图所示:

    聚合函数 / max、min(最大最小值)

    使用示例:

    # 最大值
    max(prometheus_http_requests_total)
    # 最小值
    min(prometheus_http_requests_total)
    

    如图所示:

    聚合函数 / avg(平均值)

    使用示例:

    # 计算 CPU 平均信息
    avg(node_cpu_seconds_total{instance="192.168.200.101:9100"}) by (mode)
    

    如图所示:

    聚合函数 / count(数量统计)

    使用实例:

    # 统计指标下时间序列的数量
    count(prometheus_http_requests_total)
    

    如图所示:

    聚合函数 / count_values(值出现次数)

    # 根据值进行分类统计并自定义标签,标签的值为指标的值
    count_values("request_times", prometheus_http_requests_total)
    

    如图所示:

    聚合函数 / topk、bottomk(前后 k 个)

    使用示例:

    # 请求前 3
    topk(3, prometheus_http_requests_total)
    # 请求后 3
    bottomk(3, prometheus_http_requests_total)
    

    如图所示:

    常用的子查询有以下四个:

  • avg_over_time:指定间隔内所有样本数据的平均值。
  • min_over_time:指定间隔内所有样本数据的最小值。
  • max_over_time:指定间隔内所有样本数据的最大值。
  • sum_over_time:指定间隔内所有样本数据的总和。
  • 使用示例:

    avg_over_time(node_memory_MemFree_bytes[1d]) / 1024 / 1024 / 1024
    min_over_time(node_memory_MemFree_bytes[1d]) / 1024 / 1024 / 1024
    max_over_time(node_memory_MemFree_bytes[1d]) / 1024 / 1024 / 1024
    sum_over_time(node_memory_MemFree_bytes[1d]) / 1024 / 1024 / 1024
    

    综合查询示例

    查看 CPU 平均使用率

    # CPU 平均使用率
    (1 - avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) * 100
    

    查看 CPU 负载

    # 1 分钟
    node_load1
    # 5 分钟
    node_load5
    # 15 分钟
    node_load15
    

    计算 CPU 数量

    count(node_cpu_seconds_total{mode="idle"}) by (instance)
    

    查询负载是 CPU 值的两倍的数据用于告警

    node_load1 > on (instance) count(node_cpu_seconds_total{mode="idle"}) by (instance) * 2
    

    内存使用率

    (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100
    

    磁盘使用率

    (1 - node_filesystem_free_bytes{mountpoint!~"/boot|/run.*"} / node_filesystem_size_bytes{mountpoint!~"/boot|/run.*"}) * 100
    

    特别说明:df 看到的值比监控的值一般会大 1%,原因在于 Linux 本身会预留一部分给 root,避免磁盘写满 root 无法操作。而 exporter 获取到的不包含那部分。

    磁盘空间预测

    # 预测 12 小时后磁盘使用率
    (1 - (predict_linear(node_filesystem_free_bytes{mountpoint="/"}[1h], 3600 * 12) / node_filesystem_size_bytes)) * 100
    

    节点状态查询

    up{job="node-exporter"}