夜莺监控设计思考(五)告警原理和处理流程深度剖析

夜莺监控设计思考系列第五篇,系统拆解夜莺告警流程:规则同步、告警事件生成、事件标签和属性、屏蔽规则、事件持久化、通知规则、订阅规则和事件处理器 Pipeline。

作者 巴辉特

这将是一个系列,讲解 夜莺监控 的设计思考。你可以把它理解为:原理、最佳实践和产品设计时的折中取舍。

本系列其他文章:

本篇聊夜莺最核心的逻辑:告警。涉及告警事件的产生、告警事件的后续处理、告警事件的通知。

夜莺的告警逻辑整体追随 Prometheus 的逻辑,本文默认你已经对 Prometheus 的告警逻辑比较清楚。

核心要点

  • 夜莺的告警流程从规则配置开始,经过规则同步、周期性查询、异常判定、事件生成、屏蔽、持久化和通知处理。
  • n9e 部署在中心端,既是 WebAPI 又是告警引擎;n9e-edge 部署在边缘机房,作为边缘告警引擎。
  • 告警事件由告警规则查询数据源产生,事件包含属性和标签,后续屏蔽、订阅、通知都依赖这些属性和标签做筛选。
  • 夜莺通过 alert_his_event 保存历史事件,通过 alert_cur_event 保存当前活跃告警。
  • V8 开始,通知规则和事件处理器被抽象得更清楚,事件可以经过 relabel、event update、event drop、ai summary 等处理后再通知。

前置知识

夜莺有两个进程角色:

  • n9e:部署在中心,既是告警引擎,又是 WebAPI。
  • n9e-edge:部署在边缘机房,作为告警引擎。

夜莺作为告警引擎,可以对接多种数据源。哪些告警引擎负责哪些数据源的告警判定,需要用户告知夜莺。

因此,在启动夜莺进程时,要在 config.toml 配置文件里指定 EngineName,也就是引擎名字。相同名字的引擎会被当成一个引擎集群。

同时,要在夜莺里配置数据源信息,包括连接地址,以及数据源和告警引擎集群的关联关系。之后,告警引擎就开始对自己负责的数据源做告警判定。

告警判定需要告警规则,也就是哪些监控指标、阈值是多少、多久执行一次。这些规则由用户在夜莺页面上配置,也可以导入内置告警规则后再二次修改。

告警处理流程概述

先看整体告警流程示意图:

告警流程示意图

流程可以概括为五步:

  1. 用户在 WebUI 配置告警规则,规则保存在 DB 中,通常是 MySQL。
  2. 告警引擎从 DB 同步告警规则到内存。n9e-edge 通常无法直接读 DB,因此会调用中心端 n9e 的接口获取告警规则。
  3. 告警引擎为每条告警规则创建一个 goroutine(协程,可以理解为轻量级线程),按照规则里的执行频率周期性查询存储,对数据做异常判定,最终生成告警事件。
  4. 产生告警事件后,先判断是否被屏蔽。如果被屏蔽,就直接丢弃;否则把事件持久化到 DB 中,再进入后续通知流程。
  5. 通知规则包含两部分:一部分是若干事件处理器,比如 relabel、event update、event drop、ai summary;另一部分是若干告警通知配置,比如 Critical 告警打电话、发短信,Warning 告警只发邮件。

1. 生成告警事件

生成告警事件这块逻辑和 Prometheus 很像。不同点在于,Prometheus 只支持对自身 TSDB 的数据做告警判定,而夜莺支持更多类型的数据源。

以 Prometheus、VictoriaMetrics 等时序数据源为例,夜莺会根据告警规则里的 PromQL,按照执行频率周期性查询数据源。查到了符合条件的数据,就认为存在异常数据,需要产生告警事件。

如果配置了持续时长,则需要连续等待更长时间,并且每次都查到异常数据,才会产生告警事件。

产生告警之后,如果后续某次查询查不到对应异常数据,就认为告警恢复。如果配置了留观时长,则要继续等待留观时长对应的时间;这个时间内没有再查到异常数据,才算恢复。

注意:告警之后,如果新的监控数据写入时序库有延迟,例如采集链路出问题或变慢,告警引擎查询时查不到数据,也会报恢复。因为引擎无法分辨“数据不符合阈值所以没有返回”和“数据延迟所以没有返回”。

2. 告警事件的数据结构

告警事件是根据告警规则查询数据源产生的。告警事件有很多属性,也有很多标签。

  • 属性:例如告警级别、规则标题、事件状态、触发时间、触发时的值等。很多属性来自事件对应的告警规则。
  • 标签:有两个来源,一个是查询数据源返回的数据标签,另一个是告警规则的附加标签。

例如某个告警规则的 PromQL 为:

disk_used_percent>0

根据这个 PromQL 查到的数据如下:

promql 查询结果

这会产生 3 条告警事件,因为 PromQL 返回了 3 条数据。3 条数据的标签不同,用于区分三个事件。

后续各类规则,例如屏蔽规则、订阅规则、通知配置,都要对告警事件做筛选。筛选的核心,就是按照告警事件的属性和标签匹配。

3. 告警屏蔽

告警事件产生之后,会去查找告警规则所属业务组下是否有屏蔽规则。如果有,就拿着这个告警事件逐个匹配屏蔽规则。匹配到某个屏蔽规则后,告警被屏蔽,事件直接丢弃,不会写入 DB。

后续版本可能会修改这块逻辑:屏蔽的告警事件也存 DB,只是打上“已屏蔽”标记。

需要注意,屏蔽规则只对同业务组下告警规则产生的事件生效。这是为了防止某个新手配置一个全局屏蔽规则,把公司所有告警都屏蔽掉。

屏蔽规则的多个条件之间是 AND 关系,也就是“与”的关系。官网文档里也有相关说明,这里不再展开。

4. 告警事件持久化

告警事件如果没有被屏蔽,就要入库持久化,写入 alert_his_event 表。这个表是所有历史事件的全量存档表。

同时,夜莺也会修改 alert_cur_event 表。这个表是活跃告警表,用来保存当前尚未恢复的告警事件。

如果从细节上讲,其实可以把告警和事件区分开。后续版本的夜莺可能会在这个方向做改进。

举个例子:a 机器的 mem_free 指标在 10:00:00 告警,产生了一条告警事件,id 是 1,hash 是 xx。告警规则通常会配置重复通知,每隔 1 小时重复产生一条新事件。于是 a 机器的 mem_free 指标在 11:00:00 会再次产生一条告警事件,id 可能是 2,但 hash 仍然是 xx

这两条事件发生在两个时刻,是两个对象,所以 id 不同。但它们说的是同一件事,都是 a 机器的 mem_free 告警,所以 hash 相同。后面告警恢复时,会产生一个新的事件,id 可能是 3,hash 仍然是 xx,这样才能关联起来。

基于这个逻辑,alert_cur_event 表的更新过程是:

  • 来了一个新事件,根据这个事件的 hash,删除 alert_cur_event 里已有记录。
  • 如果新事件是恢复事件,就不用再写入,相当于把活跃告警清掉。
  • 如果新事件是告警事件,就把最新事件写入 alert_cur_event

这个设计不算特别理想,因为整个体系里只有 event 概念,没有 alert 概念,少抽象了一个领域对象。后续版本可能会改进。

5. 事件如何关联通知规则

告警事件产生后,后续可能要做二次处理,例如 relabel、过滤、丰富上下文,也可能要把事件投递出去。这就涉及通知规则。

在夜莺里,告警事件和通知规则有关联方式:

  1. 在告警规则里直接配置通知规则:这个告警规则产生的所有告警事件,都直接走该通知规则。
  2. 使用订阅规则:订阅规则相当于写了一个告警事件筛选条件。筛选到的一批事件,会交给订阅规则里配置的通知规则处理。

通常用第 1 种方案即可。对于某些全局级别的通知规则,可以使用第 2 种方案。

6. 通知规则

通知规则最核心的是配置通知媒介和告警接收人。之前这些信息直接配置在告警规则里,灵活性不够。从 V8 开始,夜莺把通知规则单独抽象出来。

如果各个团队分别收告警,通常每个团队对应一个通知规则。在这个通知规则里,一次性配置好什么级别的告警发什么通知媒介,然后这个团队的所有告警规则都绑定这个通知规则。

通常可以根据告警级别定义不同通知媒介:

  • Critical 告警:打电话、发短信、发邮件、钉钉。
  • Warning 告警:发短信、发邮件、钉钉。
  • Info 告警:发邮件、钉钉。

通知这里还要对两个东西做实体建模抽象:通知媒介和消息模板。V8 版本可以更方便地对接企业内部通知媒介。具体可参考相关文档,这里不再展开。

7. 事件处理器

V8 还引入了事件处理器概念,可以把多个处理器组成一个 Pipeline,通知规则里可以引用多个 Pipeline。

这个设计的出发点是:告警事件产生之后,可能需要各种不同处理,例如 relabel、filter、enrichment 等。Pipeline 相当于抽象了一个 workflow,让用户在事件处理链路上插入自定义逻辑,提供更大的灵活性。

告警流程中的关键对象

对象 作用
告警规则 定义查询表达式、执行频率、持续时长、级别、通知等配置
告警引擎 周期性同步规则并查询数据源,生成告警事件
告警事件 一次告警或恢复的具体事件,包含属性和标签
屏蔽规则 在事件入库前拦截不需要处理的告警
alert_his_event 保存历史事件全量记录
alert_cur_event 保存当前活跃告警事件
订阅规则 按事件属性和标签筛选事件,再关联通知规则
通知规则 定义通知媒介、接收人和事件处理 Pipeline
事件处理器 对事件做 relabel、过滤、更新、丰富等处理

FAQ

一条 PromQL 查询结果有多条数据,会产生几条告警事件?

查到几条数据,就产生几条告警事件。每条数据的标签不同,夜莺用这些标签区分不同事件。

告警恢复一定代表故障真的消失了吗?

不一定。如果采集链路延迟或数据写入变慢,告警引擎查询不到数据,也可能判断为恢复。引擎无法区分“指标真的恢复”和“数据延迟未返回”。

屏蔽规则为什么只在业务组内生效?

这是为了控制风险。否则一个错误的全局屏蔽规则可能把全公司的告警都屏蔽掉。

通知规则和订阅规则有什么区别?

通知规则定义事件怎么通知、通知谁、走哪些处理器。订阅规则用于筛选一批告警事件,并把筛选结果交给指定通知规则处理。

总结

夜莺告警流程可以用一句话概括:规则在中心配置,告警引擎周期性查询数据源,生成事件后先做屏蔽和持久化,再通过通知规则和事件处理器完成分发。

理解夜莺告警,关键是把几个对象分清楚:告警规则定义“何时触发”,告警引擎负责“执行判定”,告警事件承载“发生了什么”,屏蔽和订阅负责“哪些事件要处理”,通知规则和 Pipeline 负责“如何处理和通知”。

延伸路径

继续看解决方案和产品对比

如果你正在做监控、可观测性或故障定位相关选型,建议从解决方案和产品对比继续往下看。

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云