Prometheus 告警恢复时,怎么获取恢复时的值?

Prometheus 告警事件中的 $value 表示当前告警触发时的值,但是在告警恢复时,Resolved 事件中的 $value 仍然是最新告警时的值,并非是恢复时的值,这是什么原因和原理?是否有办法来解决呢?

不废话,先说原理。

原理

告警规则是配置在 prometheus.yaml 中的,由 Prometheus 负责做规则判定。Prometheus 规则判定的逻辑也很简单,就是周期性的,拿着 promql 去查询数据,如果查到了数据,并且连续多次满足 for 指定的时长,就触发告警事件,如果查不到数据,就认为指标是正常健康的状态。比如:

复制代码
cpu_usage_idle < 5

如上例,告警规则的 promql 中是带有阈值(< 5)的,所以只要查到了数据,就说明当前的值小于 5,只要没查到数据,就说明当前的值大于等于 5,即当前数据是健康的状态。注意,查不到数据的时候,时序库不返回数据,换句话说,数据正常的时候,因为时序库不返回数据,上层就拿不到正常状态时的值,既然拿不到正常状态时的值,也就没法在恢复时展示当前最新的值啦。

实际上,恢复时的事件,是 Alertmanager 根据 resolve_timeout 生成的,而不是 Prometheus 生成的。Alertmanager 生成恢复事件时,会把上次告警的标签和注解带过去,值呢?就是上次告警时的值,Alertmanager 不会再去查询 Prometheus 拿到最新的值。

Alertmanager 可以拿到恢复时的值么?

坦白讲,很难。Alertmanager 需要根据上次告警的标签和注解去查询 Prometheus 拿到上次告警时的值,Alertmanager 不会这么干的,核心是:

  • 从职能上,Alertmanager 去查询 Prometheus,就反向依赖了,Alertmanager 是告警的分发中心,不止是接收 Prometheus 推送过来的事件,还会接收其他告警源推送过来的事件,如果 Alertmanager 要去查询 Prometheus,那就耦合太过严重。
  • Prometheus 的告警规则中可以附加标签,和监控指标的标签一起,作为事件的标签集发给 Alertmanager,Alertmanager 需要根据这些标签去查询 Prometheus,拿到原始数据,这在某些场景下是不可行的。一个是要把标签中的附加标签剔除,只留数据标签,Alertmanager 没有办法做到;其次,有些 promql 查询结果压根就没有标签,根本没法查;再次,Alertmanager 需要解析 promql,把阈值的部分拿掉,而有些 promql 压根就不是数字阈值。

如果你想通过修改 Alertmanager 达成所愿,放弃吧。

有没有办法解决?

有。通常的解决办法有两种:

  • 告警规则里,顺便配置恢复时的 promql
  • 把阈值从 promql 中拿掉,promql 只用于查询原始数据,然后在上层做阈值判定,不管监控指标当前是否健康,都会查到原始数据

下面我以一些监控产品为例,具体来说明。

告警规则里配置恢复时的 promql

使用这个方法的产品,以夜莺(Nightingale)监控为例,来讲解具体做法。核心是配置两个地方,一个是在告警规则里配置恢复时的 promql,另一个是在告警模板里配置恢复时的值的渲染。

比如我有一个告警规则用来侦测 HTTP 地址探测失败:

需要在告警规则最下面的自定义字段里,增加 recovery_promql 的配置,如下:

要理解这个工作逻辑,我们先来看看 http_response_result_code 这个指标的数据长什么样子:

从上图可以看出,这个指标包含两个 series,其中 agent_hostname 和 method 字段相同,target 字段可以区分开这俩 series。告警规则 http_response_result_code != 0 如果触发,告警事件中一定会带有 target 标签,所以,如果告警事件恢复的时候,我们用高警时的那个 target 标签去查询,一定就可以准确查到恢复时的值了。所以 recovery_promql 的配置中引用了 target 标签,其值是变量,这个变量就是告警事件中的 target 标签值。

然后,我们在告警模板里,增加一个恢复时的值的渲染,以钉钉模板为例:

复制代码
#### {{if .IsRecovered}}<font color="#008800">💚{{.RuleName}}</font>{{else}}<font color="#FF0000">💔{{.RuleName}}</font>{{end}}

---
{{$time_duration := sub now.Unix .FirstTriggerTime }}{{if .IsRecovered}}{{$time_duration = sub .LastEvalTime .FirstTriggerTime }}{{end}}
- **告警级别**: {{.Severity}}级
{{- if .RuleNote}}
- **规则备注**: {{.RuleNote}}
{{- end}}
{{- if not .IsRecovered}}
- **当次触发时值**: {{.TriggerValue}}
- **当次触发时间**: {{timeformat .TriggerTime}}
- **告警持续时长**: {{humanizeDurationInterface $time_duration}}
{{- else}}
{{- if .AnnotationsJSON.recovery_value}}
- **恢复时值**: {{formatDecimal .AnnotationsJSON.recovery_value 4}}
{{- end}}
- **恢复时间**: {{timeformat .LastEvalTime}}
- **告警持续时长**: {{humanizeDurationInterface $time_duration}}
{{- end}}
- **告警事件标签**:
{{- range $key, $val := .TagsMap}}
{{- if ne $key "rulename" }}
  - `{{$key}}`: `{{$val}}`
{{- end}}
{{- end}}

这里最为关键的逻辑是判断 .AnnotationsJSON.recovery_value 的逻辑:

复制代码
{{- if .AnnotationsJSON.recovery_value}}
- **恢复时值**: {{formatDecimal .AnnotationsJSON.recovery_value 4}}
{{- end}}

如果 .AnnotationsJSON 中包含 recovery_value 就展示,展示的时候把 recovery_value 保留 4 位小数。这个 .AnnotationsJSON 是夜莺告警规则中的自定义字段部分,如果告警事件中有恢复时的值,就会在这个字段中体现。

最终效果如下:

把阈值从 promql 中拿掉,promql 只用于查询原始数据

使用这个做法的产品,以 FlashDuty 举例,FlashDuty 不但支持类似夜莺这样的配置 recovery_promql 的方式,还支持 promql 中不带阈值的方式。我们重点讲解 promql 中不带阈值的方式。

以 Memcached 的某个告警规则举例,查询条件里不写阈值,判定规则里写阈值,如下图所示:

这种方式需要先查到当前值,再拿着当前值去做判定,所以不管是告警时还是恢复时,都可以拿到当前值。这种方式非常直观,大部分场景都适用。对于一个查询条件过滤到很多时序的场景,这种方式会查到特别多的数据,对告警引擎也是个压力。需要斟酌。

如果你也想让自己的监控系统支持在恢复时拿到恢复时的值,可以参考上述两种方式。

相关推荐
qq_232045571 天前
非容器方式安装Prometheus和Grafana,以及nginx配置访问Grafana
nginx·grafana·prometheus
夜莺云原生监控1 天前
Prometheus 监控 Kubernetes Cluster 最新极简教程
容器·kubernetes·prometheus
SRETalk2 天前
Prometheus 监控 Kubernetes Cluster 最新极简教程
kubernetes·prometheus
川石课堂软件测试2 天前
JMeter并发测试与多进程测试
功能测试·jmeter·docker·容器·kubernetes·单元测试·prometheus
SRETalk3 天前
夜莺监控的几种架构模式详解
prometheus·victoriametrics·nightingale·夜莺监控
Ditglu.3 天前
使用Prometheus + Grafana + node_exporter实现Linux服务器性能监控
服务器·grafana·prometheus
SRETalk4 天前
监控系统如何选型:Zabbix vs Prometheus
zabbix·prometheus
睡觉z4 天前
云原生环境Prometheus企业级监控
云原生·prometheus
归梧谣4 天前
云原生环境 Prometheus 企业级监控实战
云原生·prometheus
果子⌂4 天前
云原生环境 Prometheus 企业级监控实战
linux·运维·服务器·kubernetes·云计算·prometheus