Prometheus 告警时为何无法获取现场值
Prometheus 生态已经成为新时代的监控标准,很多公司都用到了 Prometheus 生态的产品。在使用 Prometheus 过程中,经常有人困惑:为何在告警恢复时拿不到恢复时的值?

我们从原理来分析,帮大家解疑答惑。
Prometheus 告警原理
Prometheus 的整个告警流程,涉及两个进程:prometheus + alertmanager:
prometheus 进程负责周期性查询判定。用户在 prometheus.yaml 中配置告警规则,prometheus 进程根据这些 yaml 格式的 rules,周期性查询 TSDB,判定是否触发了阈值,如果查到了数据,会返回一堆 vectors,prometheus 就会把这堆 vectors 发给 alertmanager,一个 vector 就被看做一个告警事件。

以上例而言,告警规则所用的 promql 是:
返回了 6 条数据,就是 6 个 vector,也就是会生成 6 条事件,发给 alertmanager(这里故意没有讲解 for 关键字,是为了更容易理解整个原理,咱们先理解基本原理)。
一般来讲,Prometheus 的 eval interval 是 15s,也就是每 15s 查询判定一次,就上例而言,如果这 6 个分区的磁盘利用率一直大于 80,那每次判定时,这 6 条数据都会发给 alertmanager。
再说 alertmanager,
alertmanager 收到事件之后,会进行消息发送。当然了,alertmanager 会有各种 silent、inhibit、group 之类的逻辑,姑且不论,最终就是发送。
对于告警事件,prometheus 查询 promql 时,TSDB 会返回告警时候的值,所以,告警事件中会带有 trigger value,也就是模板中可以引用的那个 $value
变量。
Prometheus 恢复原理
当谈到告警恢复时,逻辑就会绕一些了。就上例而言,磁盘利用率小于或等于 80 的时候,告警就恢复了。一旦数值小于或等于 80,那 prometheus 进程拿着 promql 去查询 TSDB 时,TSDB 发现没有数据符合条件,就不返回数据了。
此后每个计算周期,prometheus 都不会推送事件给 alertmanager。alertmanager 发现长时间(根据 resolve_timeout 配置来的)收不到事件了,就认为告警恢复了,产生恢复事件。
注意,alertmanager 产生恢复事件时,并没有收到 prometheus 的事件,而是把内存里之前缓存的告警事件修改了一下状态为 恢复
,直接发出来了,所以,恢复时的 $value
并非是恢复告警的那一刻的即时值,而是之前告警触发时的值!
有办法获取恢复时的值吗
以笔者的理解,Prometheus 本身是做不到的。如果你有办法欢迎留言分享。
不过有个开源项目可以做到,就是开源夜莺:Nightingale。Nightingale 开源项目是一个告警引擎,既可以对 Prometheus、VictoriaMetrics 中的数据做告警,也可以对 ClickHouse、MySQL、ElasticSearch 中的数据做告警。
下面我们对夜莺项目做一个简介,同时介绍一些在 Nightingale 中如何配置即可获取恢复时的值。
夜莺项目简介
如果你已经了解过,可以跳过这部分内容。

夜莺监控(Nightingale)是一款侧重告警的监控类开源项目。类似 Grafana 的数据源集成方式,夜莺也是对接多种既有的数据源,不过 Grafana 侧重在可视化,夜莺是侧重在告警引擎、告警事件的处理和分发。
夜莺监控项目,最初由滴滴开发和开源,并于 2022 年 5 月 11 日,捐赠予中国计算机学会开源发展委员会(CCF ODC),为 CCF ODC 成立后接受捐赠的第一个开源项目。
其开源仓库地址:
夜莺如何获取告警恢复时的值
夜莺要拿到告警恢复时的值,需要做两步:
在告警规则配置时,在附加字段里配置 recovery_promql
在告警消息通知模板里,引用
AnnotationsJSON.recovery_value
字段
比如,我有这么一条告警规则:

告警规则里的 promql 没有什么特殊的,和 Prometheus rules 中的 expr 原理一样,额外的,我们要在规则的附加字段里额外配置一个 recovery_promql 字段:

recovery_promql 这个字段也是配置了一个 promql,和告警规则里的 promql 几乎一样,但是没有阈值,同时,引用了一个变量。要理解这个工作原理,我们先来看看 http_response_result_code 这个指标的数据长什么样子:

从上图可以看出,这个指标包含两个 series,其中 agent_hostname 和 method 字段相同,target 字段可以区分开这俩 series。告警规则 http_response_result_code != 0 如果触发,告警事件中一定会带有 target 标签,所以,如果告警事件恢复的时候,我们用告警时的那个 target 标签去查询,一定就可以准确查到恢复时的值了。所以 recovery_promql 的配置中引用了 target 标签,其值是变量,这个变量就是告警事件中的 target 标签值。
简单来讲,就是在告警恢复时,夜莺从新查询了一次 recovery_promql(把其中的变量替换为告警事件的标签值),拿到了现场值。
夜莺会把这个现场值放到告警事件的 Annotations.recovery_value 中,之后我们就可以在模板中引用,比如:
OK,今天的分享就到这里。各种开源项目都有各自专精的领域,玩开源,就是各种拼凑 🤣
评论