一、告警与通知
告警与通知是服务监控平台的主要输出,但二者是又一定差别的。
告警会在某些时间发生时(如指标达到阈值)时触发。然而,这并不一定意味着有人被告知此事件的发生)这是通知的来源。
所谓通知,是将告警告知到某人某事:通过发送IM消息、发送短信/电子邮件、电话通知或创建工单等。
虽然看起来非常简单,但其中通常包含众多复杂因素,很难实施和管理:
- 哪些问题需要被通知?
- 谁需要被通知?
- 如何告知他们?
- 多久告知他们一次?
- 何时停止告知以及何时升级到其他人?
- 等等、等等
如果配置不当,会导致海量的告警通知产生,那么人们将无法对它们采取任何行动,甚至有可能故意忽略掉。笔者在工作中有这样的经历:每天邮箱中充满了来自监控系统成千上万的通知邮件,根本无从看起,后面直接告警疲劳、直接全选告警邮件标记已读。现在来看,这样一定会错过真正重要的通知。
更重要的是告警通知的内容——它们需要间接、清晰、准确,易于理解且可以操作。
例如,笔者曾经介绍过关于数据库慢SQL告警,但整条通知内容洋洋洒洒一大堆,却就是没有告诉被通知对象慢SQL具体是什么,导致收到之后想要去进一步打开、定位是极其困难的。
因此,对于告警的通知,我们应当重点关注:
- 应当清晰、准确、可操作。
- 应当使用人而非计算机的语言来进行通知内容的表达。
- 应当为通知添加上下文,包含组件的其他信息。
- 仅发送有意义的通知。
二、告警中台设计与实践
1、平台定位
告警平台是DevOps下运维中心的标配,相信从业者或多或少都有了解与使用。
由于整体内容较多、文章较长,笔者会分成几部分来做记录。
在这些告警平台中,一般的配置过程为:
- 选择监控对象;
- 配置告警触发条件;
- 配置告警通知对象;
- 配置收敛周期(多长时间内告警不重复发送);
这样的监控平台一般是以自身所提供的监控能力为准、对指标,普遍来说功能可能相对单一,从而造成服务的运维人员需要在不同的平台(日志、调用链、拨测等)配置,使得告警分散难以集中配置管理、也难以对告警做统一的数据分析。
举个例子,对于接口的5XX错误故障类型,可以通过很多维度进行观测——接口日志、调用链分析等;一般为了避免监控遗漏,SRE都会对这方面做冗余告警配置:也即在多个平台配置相似的监控类型(接口5xx错误监控告警)。
当服务真的产生这方面故障时,在监控平台整体稳定可靠的基础上,会同时为服务推送类似的告警,这对于服务OnCall来说是冗余的。
如果能够有一个中心化的告警平台自动按照指标识别相同故障、自动收敛避免重复告警,可以很大程度提升用户的告警体验。
这里我们所提出的 “告警中台” 的概念,主要在于:
- 平台本身不具备监控与指标收集能力;
- 通过OpenAPI的形式对外暴露告警能力;
- 基于指标类型、故障产生阶段、告警等级等,对告警请求做约束和管理;
- 对告警通知的发送提供收敛、汇聚能力;
- 提供告警抽象汇聚、告警信息数据全量保存管理能力;
这其中,我们主要从以下三个角度进行设计与实现。
- 告警策略定义
- 告警消息与事件
- 告警通知发送
2、告警策略定义
基于中台化的告警策略在定义与设计上,与普通的监控告警平台在告警策略有部分不同,本质上有些类似于一种 “过滤器”:
- 三方服务调用告警OpenAPI时提供必要参数,筛选后找到满足条件的一条告警策略;
- 根据告警请求信息内容,生成告警消息与告警事件;
- 根据告警策略详情,找到告警对象、检查告警收敛情况,并发起告警通知;
(1)基于统一配置与维护的策略设计
总体来说,告警策略会在中台统一托管、维护,三方服务在发起告警请求时只需关注告警本身,无需在意策略本身的分发、实施,以及告警的收敛、恢复与关闭操作。
a、策略信息组成
- 策略名称
- 用与描述该策略的具体用途,必选;
- 指标类型
- 说明策略的具体指标类型,必选;
- 参与标识与筛选策略的字段,限定在指定范围:
- 例如主机、数据库、状态码等;
- 指标子类型
- 在某种指标下的子指标类型,非必选;
- 参与标识与筛选策略的字段,限定在指定范围:
- 例如在主机类型下,子类型可选为进程、CPU、内存等;
- 告警等级
- 策略对应的告警等级,必选;
- 参与标识与筛选策略的字段,限定在指定范围:
- 提示、轻微、严重、致命;
- 阶段类型
- 策略对应的服务环境阶段,必选;
- 参与标识与筛选策略的字段,限定在指定范围:
- ALPHA、BETA、GAMMA、PROD;
- 通知方法
- 告警策略通知的具体类型,必选,json;
- 限定在指定范围:
- PUBLIC:公共类型告警,具体通知主体通过通知策略详情记录;
- SERVICE:服务类型告警,具体通知主体根据告警服务对象OnCall内容记录(OnCall为独立维护);
- 二者可以同时选择;
- 样例:
{"notice_types": ["PUBLIC", "SERVICE"],"notice_methods": {"PUBLIC": ["IM", "MSG", "TEL"],"SERVICE": ["IM", "TEL", "TICKET"],}}
- 通知策略详情
- 通知类型为PUBLIC时所需的告警主体信息,非必选;
- 通知类型为PUBLIC时需要写明,否则无法定位到通知对象;
- 限定在指定范围:
- IM卡片、电话、短信、提单等;
- IM卡片需要写名群号,电话短信需要写明电话号码,提单需写明具体责任人;
- 样例:
{"IM": ["114514"],"MSG": ["138XXXX9527"],"TEL": ["138XXXX9527"], }
- 责任人
- 告警策略的定义与维护责任人;
- 恢复周期
- 作用于告警原子事件恢复策略,必选,默认值为15分钟;
- 时间长度,说明告警原子事件在多长事件无新增告警后会自动转换状态为“已恢复”;
b、兜底的默认全局策略
除了上述的定义为特定指标的告警策略外,很多时候三方服务可能并不需要、也没有定义特定告警策略,这个时候我们会提供默认的全局告警策略以作为兜底的告警策略,避免致命告警未能触达OnCall等问题,这里我们根据告警等级类型做了如下分类:
- 提示:非生产环境不发送 / 生产环境发送告警到服务对应OnCall工作群;
- 轻微:发送告警到服务对应OnCall工作群;
- 严重:发送告警到服务对应OnCall群(环境级)、提3级单;
- 致命:发送告警到服务对应OnCall群(环境级)、提3A级单、打电话、发短信;
需要注意的是,上述策略是一种兜底策略,只有在为匹配到策略时使用上述策略。
但同时也要求告警请求参数合法,如果请求参数中连告警等级、对象都不携带,那么我们会认为请求非法。
(2)策略实施与说明
在我们完成最基础的告警策略模型与相关机制设计后,对于策略具体如果落地实施、以及策略与通知动作的具体关系还需要详细说明。
a、策略实施流程
在告警策略的具体实施上,主要通过三方服务调用OpenAPI传递特定参数、执行策略匹配、最后由告警发送模块实施。
这里,服务的接口请求参数应当包含:
{"alarm_type" # 告警指标类型"alarm_sub_type" # 告警子类型,可选"alarm_level" # 告警等级"alarm_stage" # 告警阶段"reference_uuid" # 告警对象UUID,为CMDB环境层级信息
}
这里简单举个例子:
- 三方拨测服务希望发起告警,并给定了以下参数:
{"alarm_type": "dial_test","alarm_level": "SEVERE","alarm_stage": "PROD","reference_uuid": "DEV_TOOL-ENV-9527",
}
- 中台侧接受到告警请求后,解析对应字段,并找到了对应的告警策略;
- 根据解析得到的告警发送方法,以此发送告警;
b、告警策略与通知动作
告警策略本质上是一种过滤器,一方面找到对应的通知方法动作,另一方面也对告警请求进行了具体识别与分类、方便告警数据归档与后续分析。
- 匹配策略采取DFS(深度优先搜索)思想进行策略查找;
- 若未查找到对应策略,则会使用我们上面所定义的兜底策略发起告警;
- 对于完全无效、未按照接口参数要求的请求:
- 我们会直接返回接口调用失败、告知参数问题;
- 我们也会打印日志、采取日志关键字告警方法,根据接口请求APPID,知会到对应服务OnCall;
而对于通知动作而言,这里主要涉及告警通知方法的解析、告警策略详情的匹配,以及SERVICE下的OnCall获取。
具体流程大致如下:
- 解析对应对应策略通知方法字段,获得相应内容,根据上述样例,我们可以得到:
- 告警需要发送PUBLIC类型以及SERVICE类型;
- PUBLIC类型需要进行IM卡片、短信、电话通知;
- SERVICE类型需要进行IM卡片、电话、提单通知;
- PUBLIC类型,直接解析通知策略详情,可得到对应IM群号、发送短信的电话号,打电话的电话号;
- SERVICE类型,根据告警请求参数中的reference_uuid进行服务OnCall的查找,获取服务相关信息:
- 服务在对应告警等级、对应阶段下的IM群组号;
- 服务对应的OnCall人员(包含电话号);
- 服务提单所对应的公共编码信息;
- 汇总两种通知类型所有的通知方法和通知对象,在进行去重后,依次发起通知操作;