一、现有告警平台分析
在设计核心的告警数据模型时,本质上是对业务逻辑与整理数据流的梳理与设计。对于这部分不可高屋建瓴、想当然,需要有成熟的
取其精华、根据自身业务需求再做优化与改进才是正道。
笔者所在的部门SRE平台恰巧已有相关告警能力,在详细分析后可以获悉如下特性:
- 服务进行告警策略的配置时,会同时配置告警收敛和恢复策略;
- 所谓收敛策略,就是相同告警(内容一致)在多长时间内不重复发送;
- 所谓恢复策略,就是多长时间内未产生告警自动恢复;
- 在告警的收敛周期内,所有的告警消息会压缩在同一条告警事件中;
- 在恢复周期定义的时长内未有新告警产生,该告警事件会自动恢复;
上述的基本流程与设计对于告警核心功能而言已经足够,但对于更高维度的告警抽象、事件关联、响应与信息反馈,这种模式仍存在某些痛点:
- 告警汇聚层次较低
- 虽然支持以服务为维度的聚合,但展开后依然是以告警单元事件为数据颗粒;
- 举个例子,当我们想吃大包子时,我们希望重新调配、制作馅料,而不是把一堆小包子放一起直接包起来;
- 如果不能对数据做重新梳理、聚合,当几十个服务、上百个指标同时故障,整体告警会变得不可观测:
- 无法支持根因事件的关联
- 事件仅仅是一层的、与特定服务、特定环境、特定策略相绑定,没有以服务或指标维度再做事件的抽象;
- 很多时候,告警故障的产生并不是随机的,而是由某个核心服务的故障导致的连锁反应;
- 这个时候,只要核心服务故障恢复,我们就可以认为当前整个故障传播链上的告警均已恢复;
- 但当前告警能力并不支持这样,只能手动或自动地等待所有事件自行恢复;
总的来说,结合告警发送及时性与全面性、告警消息量与体验、以及告警根因事件响应的相关诉求,我们在告警消息与事件的基础上,引入事件树系统,基于事件节点进行告警事件的自动汇聚与消息通知推送,并支持服务手动的根因事件创建与关联。
二、事件与事件树系统
1、消息与事件
(1)告警消息
当第三方服务发起告警请求时,会调用中台所提供的OpenAPI,此时,我们会将所有的告警请求均进行记录、生成一条告警消息-AlarmMsg
并保存起来。对于告警消息,其具备如下特点:
- 是一种信息表达,代表在某个时间点,xx服务、xx环境下产生了满足xx指标、xx条件的相关异常;
- 无状态;
- 告警消息是否触发通知操作,根据告警策略与收敛策略决定;
- 在发起告警后,会生成一条或多条告警通知记录-
AlarmRecord
;
- 在发起告警后,会生成一条或多条告警通知记录-
- 告警消息本身会作为最小粒度,汇聚到原子事件下;
(2)告警事件
对于告警事件(AlarmEvent
)而言,首先根据基本性质,我们可以将事件分为两类:
- 原子事件
- 聚合事件
同时,事件本身除了能够手动创建、关联外,主要通过自动生成来完成整体事件树的构造,根据聚合粒度,事件本身还可以分类为:
- 单服务单指标(L1)
- 属于原子事件;
- 故障类型为单个服务、单个指标下的相关告警;
- 样例:构建检查服务接口状态500类型异常;
- 单服务多指标(L2)
- 属于聚合事件;
- 单个服务多个指标类型出现故障的相关告警;
- 主要为标记在一段时间内同一个服务所有故障的汇聚情况;
- 样例:构建检查服务异常告警事件;
- 多服务多指标(L3)
- 属于聚合事件;
- 多个服务、多个指标出现故障时的相关告警;
- 是单服务多指标的再向一层的抽象汇聚,一般是抽象到服务组(产品)层级,可在一定程度上代表局点的健康状态;
- 样例:构建产品异常告警事件;
a、原子事件
对于原子事件而言, 指的是在特定的告警策略下对应告警消息所关联的对应事件,其具备如下特点:
- 是事件的最小单元,不可再分;
- 自动生成;
- 有状态,存在生命周期;
- 支持自动或手动恢复;
- 当事件状态转为恢复后,不再开启、直接归档;
- 单服务单指标;
- 也即基于特定微服务、特定环境、特定指标(告警策略);
- 事件本身存在等级,与告警策略等级保持一致;
- 在事件树体系中,原子事件一定是叶子节点;
b、聚合事件
所谓聚合事件,是基于原子事件进行聚合、或基于其他聚合事件聚合而成的更高层级的父节点,其特点如下:
- 可自动生成、也可手动生成;
- 自动生成类型:单服务多指标、多服务多指标;
- 手动生成类型:人为地选择多个节点作为子节点而生成,节点含义人为指定;
- 有状态,且父节点状态与子节点状态相关联;
- 当父节点手动标记恢复时,所有父节点下子节点状态同步更新为恢复;
- 当父节点下所有子节点状态均为恢复时,其父节点状态也同样会更新为恢复;
- 在事件树中为父节点,且不可能为叶子节点;
作为事件树的重要组成部分,原子事件与聚合事件的具体生成与关联办法,会在下部分中详细说明。
2、事件树体系
(1)事件树基本定义
首先,所谓的事件树体系,就是针对不同层级的告警事件汇聚而出现的。
在事件树中,我们能够做到:
- 自动的对告警事件生成L1、L2、L3三个层级的事件;
- 对不同层级告警事件的操作,能递归地作用于下面所有的子事件;
- 例如,当对父亲节点的状态标记为已恢复时,其下面所有服务、所有指标的相关告警事件均会直接标记为已恢复;
- 能够自由的根据告警产生的根因分析情况,选择或创建对应的根因事件节点,并将对应受影响服务告警事件做关联进行跟踪、处理;
其次,对于事件的数据接口定义层面,我们主要包含:
{"event_name" # 事件名称"event_uuid" # 事件节点uuid"event_level" # 事件层级,可选:L1 L2 L3 L4(自定义类型事件层级)"alarm_level" # 事件对应告警层级"parent_uuid" # 父亲节点uuid,可为空"service_info" # 事件关联服务信息"started_at" # 事件开始事件"recovered_at" # 事件恢复事件"responser" # 事件响应人"closer" # 事件关闭人"event_desc" # 事件描述"alarm_reason" # 事件告警根因
}
(2)事件生成过程
在生成事件节点与事件树时,具体流程如下:
- 当告警产生时,会自动生成单服务、单指标事件,也即叶子节点;
- 在生成叶子节点后,首先会尝试查找对应父节点——也即单服务多指标事件节点:
- 如果找到,则说明当前告警时间区间内已经完成了总体事件树的生成构造,直接将叶子节点的父节点关联到对应聚合事件节点即可;
- 如果没有找到,则说明当前事件为告警时间区间内的第一个产生事件,直接根据服务、产品等信息,生成相对应的父节点,并尝试找到对应L3层级的祖父节点——找到关联、未找到则创建;
- 而后,如果产生了同样服务、同样指标的告警,会直接收敛聚合到对应叶子节点下;
- 如果产生了同样服务、不同指标的告警,会自动再走上面的流程、并关联到对应的L2层级节点上;
- 如果产生了不同服务、但相同产品的对应告警,则会生成对应L2层级节点,并同样关联到对应根节点上;
总的来说,自动生成构建的事件树一定是一个三层的树形结构,从上到下分别对应着:
- L3:多服务多指标事件节点;
- 根节点,有且只有一个;
- L2:单服务多指标事件节点;
- 中间层节点,可能存在多个;
- L1:单服务单指标事件节点;
- 叶子节点,原子事件;
(3)事件恢复过程
对于事件的恢复机制,我们主要有自动与手动两种方法:
a、自动恢复
当同一个产品在某个故障周期内产生一系列告警时,会自动生成一颗事件树。
很多时候,告警本身并不是很重要:例如CPU使用率短暂飚高等,在告警产生后很快就回落了,因此也不再产生新的告警。
对于最基础的告警体系而言,告警本身是存在生命周期概念的,因此在上一篇章中我们所定义的恢复时间周期概念下,如果一个告警原子事件在一段时间内没有产生新的告警请求,那么我们就认为该原子事件已恢复:
- 这里,我们是通过定义一个原子事件状态巡检任务来进行时间与状态检查的。
同时,每当这样的告警原子事件恢复时,都会自动触发一轮事件树状态检查任务,事件树会递归地检查自身的孩子节点状态:
- 如果所有孩子节点的状态均为“已恢复”,则该父亲节点的状态也会同步修改为“已恢复”;
- 如果存在状态为“告警中”的孩子事件节点,则不做任何操作、维持父亲节点“告警中”的相关状态;
最后,有一些时候服务会持续不断的产生轻微类型的告警,可能导致某一棵事件树上挂了大量的告警信息,既不方便观察、也不方便处理,更会影响后续新的告警事件的产生与事件树的生成。
对于这种情况,我们会每天定时自动关闭持续告警事件,并对服务做相关标记、提醒服务可能存在的相关问题,从而避免上述问题。
b、手动恢复
除了上述的自动恢复外,本身较为紧急的告警事件,我们是希望服务OnCall响应、处理、并手动标记恢复的。
因此,对于手动恢复告警事件,支持对各个层级的告警事件进行操作:
- 从上到下,递归地将父亲节点下所有孩子、孙子事件节点标记为恢复;
- 从下到上,递归地探查当前父亲、祖父节点所有孩子是否已恢复已更新其父亲、祖父节点状态;
(4)事件的手动创建与关联
除了上述自动生成的三层服务树结构外,很多时候在服务OnCall或SRE完成告警根因与异常分析后,明白多个告警之间的关联关系与因果关系,因此希望能够有一个统一的事件节点来跟踪这一系列的告警事件。
这时,对于告警警情的跟踪,只需要跟踪该根因事件即可:
例如,某个下午出现了多个服务接口5xx异常告警,经过快速分析后发现实际是其中一个上游服务A的OpenAPI出现异常,导致调用该接口的相关服务对应业务也都出现了报错告警。
对于这种情况,只要该上游服务的接口问题修复,那么下游服务的一系列问题也自然恢复。
但是,由于本身告警根因并不容易挖掘发现,而且我们自动生成的告警事件树也仅仅是基于指标与服务自身维度围绕构建的,这种业务逻辑层次的关联关系我们很难去自动构造事件树,因此对于上面的问题,就会有数棵逻辑上有关、但数据与形式上无关的事件树被构造出来。
基于上述问题,我们提供了事件树的手动关联机制。
例如在上面的场景中,对于多个告警事件树,支持用户直接创建新的类型为L4
的事件节点,说明具体事件类型,并将多个L3层级的事件树根节点关联到该节点上。
这样,就人为地构造出了一棵四层的事件树结构,该树形结构同样支持上面的手动与自动恢复机制。
这种手动创建、关联而成的四层事件树结构,方便在较大规模、相互联系故障发生时,有一个更加抽象、统一的观测视角,在把控告警全局的同时,在根因问题恢复时也方便一键恢复所有受影响服务告警事件。
需要注意的是,对于事件树而言,我们设计与实践的核心思想在于“规范化”、“条理化”,因此我们对告警事件做了严格的层级与类型分类,事件树只有自动生成的三层或手动创建的四层,并不支持无限制的节点生成与关联。
这样有限层级的事件树方面处理和展示的同时,也避免了过深层的事件树而造成的事件树检查递归时间复杂度过高、性能损耗等问题。