使用RecodingRules提升报警规则配置效率和易读性

什么是RecodingRules?

将计算频繁需要或计算成本高昂的表达式的结果保存为一组新的时间序列。然后,查询预先计算的结果通常会比每次需要时都执行原始表达式快得多。这对于仪表盘特别有用,因为仪表盘每次刷新时都需要反复查询相同的表达式。

Recording是一种特殊的报警规则,将计算/书写复杂的PromeQL预先定义好,然后输出为看起来更直观更简单的series和value,在其他报警中直接使用这个新的series作为报警条件即可,如下图:

报警抑制规则复杂性问题

在实际场景中,比如组内的多个SRE在配置报警的时候都会去判断机房的网络情况,如果网络出现中断,抑制其他报警防止报警泛滥:

# dc1的专线信号 0是0%丢包 1是100%丢包

dedicated_line_ping_loss{telecom="China Unicom",target="1.1.1.1",dc="dc1"} 0

dedicated_line_ping_loss{telecom="China Telecom",target="2.2.2.2",dc="dc1"} 0

# 判断专线是否故障

dedicated_line_ping_loss{} == 1

判断多条专线同时故障,只需要拿最小的专线查看即可

min(dedicated_line_ping_loss{}) == 1

但是当我们把这条规则作为Prometheus或VM-Alert的rules发现,只用min函数会丢失 telecom、target、dc等所有标签。

于是,我们必须要加上by操作

min by(telecom,target,dc) (dedicated_line_ping_loss{}) == 1

现在我们的表达式写完了,作为其他表达式的抑制条件可以这么写:

# 业务pod心跳丢失报警 1是存在 心跳丢失就是没有数据,假设我们已经通过服务发现能够实时获取到Pod名称

pod_heartbeat{pod_name="jwidsfjw-1"} 1

pod_heartbeat{pod_name="jwidsfjw-2"} 1

pod_heartbeat{pod_name="jwidsfjw-3"}

# 检测业务pod心跳丢失报警

absent_over_time(pod_heartbeat{pod_name="jwidsfjw-1"})

absent_over_time(pod_heartbeat{pod_name="jwidsfjw-2"})

absent_over_time(pod_heartbeat{pod_name="jwidsfjw-3"})

# 使用专线来抑制,防止专线故障时报警泛滥

absent_over_time(pod_heartbeat{pod_name="jwidsfjw-1"})

and on(dc)

min by(telecom,target,dc) (dedicated_line_ping_loss{}) == 1

这里可以看到,在最简单的单条件抑制情况下,整个表达式就已经不够直观了

使用RecordingRules简化

我们使用RecordingRules来简化一下实现

- name: dedicated_line_all_down

rules:

    - record: dc:dedicated_line_all_down:min # 官方建议使用冒号来进行分割,具体规则是 目标:原指标名称:聚合方法

    expr: min by(telecom,target,dc) (dedicated_line_ping_loss{}) default vector(0) # 使用default来保证这个指标查询不到的时候也能够打上value

    labels: # 这里可以补充标签

上述规则生效后,我们便可从时序数据库中查询到名称 dc:dedicated_line_all_down:min{dc=”dc1”} 的线

上面的报警规则就可以直接使用这个新的指标,从而简化规则:

absent_over_time(pod_heartbeat{pod_name="jwidsfjw-1"})

and on(dc)

dc:dedicated_line_all_down:min{dc=”dc1”}

并且在大量报警规则都会用到 【机房网络断掉】这个指标的时候,就不用每次都进行min 计算了,因此也降低了时序数据库本身的计算压力,提高了查询效率。

使用PromeQL/MetricsQL实现宕机报警思路

宕机报警的意义

保证机器从系统外部访问的可达性。

如果机器从系统外部无法访问,那便是宕机报警(或我们认为的宕机报警)。


思路

每台机器上都运行着我们的监控Agent,代码健壮的情况前提下,可以忽略Agent异常退出(或Agent异常退出概率极低)的情况,我们视Agent数据未如期上报的情况为宕机情况。

所以便得到一个公式:

宕机报警 == 失联报警 == 数据断点检测

断点检测,依赖Metrics指标的及时上报,最好不使用单次上报容易超时的数据类型,所以心跳是最合适的数据。

所以重新明确目标: 宕机报警 == 心跳断点检测。 (断点时间与阈值时间进行比较)


如何对心跳数据做断点检测?

如果心跳指标是heartbeat,使用ip作为label来确保机器唯一性

指标为:

heartbeat{ip="10.58.99.166"}

正常情况下,会上报value = 1

异常情况下,会没有数据

所以我们可以使用MetrisQL中的absent_over_time函数来进行心跳断点检测:

absent_over_time(heartbeat{ip="10.58.99.166"}) == 1

absent_over_time(query)函数在query表达式没有数据的时候返回1,所以当整个表达式 == 1时,就能够检测到心跳断点。

值得注意的是:query表达式必须返回单条线而不是多条线,如果query查询出来的是多条线,absent_over_time永远无法返回数据,断点检测也永远不会触发。

如上图,当tao_metrics_frp_heartbeat{frp="10350"}断点的时候,absent_over_time函数开始补点,所以配置报警规则后,可以及时检测到机器断点的情况。

当label匹配出多条线时,absent_over_time函数永远不返回数据。


如何降低宕机报警的误报频率?

降低误报频率可以从以下几个方向入手:

  • 提高目标集的准确性:如果ip:10.58.99.166的机器不存在,配置absent_over_time(heartbeat{ip="10.58.99.166"}) 这条规则是一定会触发报警的,所以要提高10.58.99.166是否存在的准确率。可能听起来很可笑,10.58.99.166这种基础数据怎么可能会出错呢。 现实情况是,很多目标集并不使用物理机、虚拟机的ip来作为机器心跳的唯一标识符,比如在K8S集群中,我们可能使用cluster+namespace+deployment+pod+container_name+port的多label形式来定位某个具体服务,K8S中的container销毁情况非常频繁,因此如果要判断某个端口是否存在,就需要及时拿到多label的变化情况,从而生成对应的报警规则。
  • 使用前置规则来进行排除: 举个例子,如果10.58.99.166这个ip在lf机房下,而我们的网络丢包指标network_packets_loss 是从外部探测的,那么在网络丢包率为100%时(相当于专线断开),应当排除机器的宕机情况(因为监控数据此时也无法上报),在MetricsQL中可以使用absent_over_time(heartbeat{ip="10.58.99.166"}) ifnot (network_packets_loss{dc="lf"} == 1) 这条规则来实现(if语句是MetricsQL中额外实现的,原生PromeQL不支持)
  • 降低转发过程带来的数据延时误报情况:合理配置规则中的for和keep_firing_for等字段,更好地过滤掉短暂的网络延迟导致的指标尖峰。