0%

业务监控告警的一点思考

一、前言

很多开发同学,只关心功能开发和上线,上线后的运维工作,作为软件产品的生命周期中非常重要的一个环节,常常被忽视。即使一些同学可能有一些意识知道要关心产品上线后的情况,但是不知道如何去下手。

在这里,我们主要从开发的角度,聊聊如何做好软件上线后的监控,以及异常情况下的告警。

(这里,不会去关心业务层面上的一些运行数据,比如GMV,PV等等,虽然这些数据的异常变化,可能也跟系统稳定性也存在一些关系)

1.1 为什么关心监控

真实世界中,我们不会构建一个100%稳定可靠的服务,一个是因为各种软件上的bug或者硬件设备的故障,再或者各种人为操作的配置问题或者网络问题等等;二是因为如果我们保证极端可靠性,解决前面的问题,其超高成本带来的收益非常低,低到所有公司,哪怕是Google也不会去接受。

因此,我们需要做的是,在产品整体成本可接受的情形下,尽量通过后期运维手段来降低服务不可用时间,从而提高服务的SLA,保证服务的高可靠性。

而,降低服务不可用时间,就需要做到最快发现故障问题,最快解决故障问题。这些就需要监控来辅助开发解决。

因此,在【Site Reliability Engineering: How Google Runs Production Systems】书中,介绍了一些监控场景,这里抽取一些进行说明:

分析长期趋势。比如,根据当前数据库数据量的增长速度和存量情况,可以提前发现数据库容量问题等,提早改造来规避可能的故障。
跨时间范围的比较。比如老的接口新加了redis缓存之后,响应是不是比之前快了很多,可以上线前后的时间区间接口rt做一个对比拿到结论。
问题关联分析。比如,我们发现某个接口的rt增长了,分析原因,可以看看其他监控数据是不是也出现关联变化,比如可能是请求量暴增,或者下游依赖的接口出现响应变慢的问题。然后,再采取对应的方案去解决。
也就是,从监控上,我们可以获取到 系统各个维度的请求情况,处理情况。

1.2 为什么需要告警

有了监控大盘,我们就可以随时观察系统运行状况。但是,系统不会在我们看大盘的时候,才出现异常情况,因此,我们需要主动快速去发现系统可能的异常点,将可能的系统失败阻挡在产品故障之前,或者刚刚开始的时候。

二、监控

2.1 监控什么

前面说,我们的服务不会存在百分百可靠,所以对服务而言就存在一定的不确定性,这个不确定性,就是风险。

和业务一样,存在风险,我们就需要对风险进行管理。监控的对象或者说目标,就是风险。

2.2.1 业务风险

所谓风险,也就是不确定性,与之对应的就是服务的可用性。一般,现有衡量业务可用性,最常用的是:系统正常运行时间/(系统正常运行时间+停机时间)

虽然,我们在很多文章或者宣传册中,都用这个时间比来宣传系统的可用性,4个9,5个9啥的,但是,这种时间维度的可用性,在当前微服务化的时代,衡量服务可用性毫无意义。

这里,我们使用另一种计算公式:请求成功数/(请求成功数 + 请求失败数) = 请求成功数/ 请求总数。例如,一个服务,一天请求总数10000次,失败1次,那就是99.99%。

因此,我们在监控的时候,肯定是需要监控请求总数,成功数,对应失败数的。

但是,不同服务,相同服务不同的业务源,对于服务风险的诉求是不一样,比如,实时用户登录查询用户信息的请求,和定时任务跑脚本时获取用户信息请求,对服务的容忍度显然是不一样的。

因此,我们需要针对风险,分出个三六九等。

2.2.2 业务风险容忍度

和风控中风险识别规则一样,不同场景下识别风险的规则阈值一般会存在不同。而,在风控中,我们会针对不同接入业务,按照业务属性,配置不同的阈值和规则,来识别、监控风险。

业务系统监控,一样需要这么做。

因此,我们就需要梳理,一个服务接口,是否需要针对不同接入场景,单独出来不同的监控大盘,进行更专业的风险监控。

那么,接下来就是监控关注的第二个点:判断业务对外接口或者实现逻辑内部,哪些点存在风险,或者有助于我们发现相关风险,做好埋点,制好大盘。

举几个例子:

场景一、准入提供通用的决策接口,外部一些地方需要做风险判断时,都通过改接口接入。这个时候,肯定会存在一个现象:不同业务对失败的容忍度是不一样的,因此对不同业务做细粒度的埋点监控,是必要的,对应可能还会做服务细粒度的隔离,限流措施。

场景二、特征中心会提供大量指标查询,从接口层面看如果总qps1w,失败10,千一的失败率,可能怀疑是网络抖动,偶尔超时等问题。但是从细粒度监控上,发现某个API/指标的特征查询qps 10,失败10,百分比的失败率,则需要立马去查原因了。

2.2.3 具化容忍度–风险带来的故障程度

和上面风险容忍度类似,如果某个接口出现问题,会导致业务故障,那么必然需要进行监控。

比如,提供给电商交易主链路使用的风险决策接口,如果RT超过约定的超时时间,这个时候即使我们都是成功返回,但是对于业务方而言,其实对业务是有损的,甚至会导致业务故障。

因此,对于业务RT的监控,尤其是某些重要请求业务方细粒度的RT监控,是非常必要的。

回到故障程度,故障程度其实是风险容忍度一个具化的点。我们可能不知道业务方的容忍程度,那么我们就看业务方会不会因为我们接口返回失败/超时,导致出现有损服务,甚至故障。

2.2.4 风险溯源

当我们从大盘上发现风险/系统问题的时候,我们最需要做的就是找出原因,也就是风险溯源。

风险出现的原因,分为外部原因和内部原因。外部原因,就是基础中间件组件,外部依赖的接口等出现问题影响了我;内部原因,就是自己代码bug导致的失败问题影响了最终返回。

针对外部原因,我们监控的东西其实很明显:依赖三方接口(包括基础中间件)的成功数,失败数,RT耗时等,其实就是我们在基础设施层中的所有实现接口,都需要进行监控。

针对内部原因,通常,一些非预期异常数需要监控;此外,一般而言,对于RT敏感的服务,内部核心逻辑的耗时需要监控,甚至需要细粒度的场景耗时监控。

2.2.5 总结

说了这么多,总结一下,我们到底需要监控哪些东西。

就两个:次数,耗时。所有你根据上面一些原则,总结梳理出来的风险相关点,都需要去监控。

三、告警

告警,其实就是风险触达,或者预期风险触达。

3.1 告警现状

说触达可能有点专业术语,其实就是你收到的各种APP通知,营销短信通知,广告电话通知之类的。就问你,没完没了,你根本就不需要的通知,你烦不烦!

现在,存在一种现象,就是系统要不没有告警,要不就是一堆告警。ERROR日志报警,企业微信各种同比环比告警,失败数、耗时等超阈值告警,等等。

目前有的告警,大部分设置之初是合理的。但是,随着业务不断发展,系统不断优化,很多一直告警就需要进行调整或者删除了。

但是,开发同学并没有同步去调整这些设置,导致无效告警越来越多,最后大家产生疲劳,对需要关注的告警也忽略了。

3.2 告警什么

如何做风险触达?做什么风险数据的触达?

3.2.1 风险触达

所谓风险触达,就是我发现系统存在故障,或者可能即将存在故障的时候,给相关同学进行告警通知。让开发同学立马去追踪系统问题,恢复服务,或者采取其他措施避免即将出现的服务不可用。

风险触达,首先需要准确触达,然后就是智能触达。当然智能也分三六九等。所谓智能触达,就是一个故障,可能会导致大量告警设置都被触发,这个时候,可以根据关联或者历史学习,直接将原因告警进行推送。

这里,我们基于目前的告警平台来配置告警,所以目前要做的是准确性。准确性包含两个方面,风险准确,对象准确。

所谓风险准确,按照目前告警平台设置而已,就是阈值设置是合理的。触发监控阈值的告警,确实是存在异常情况,避免一堆非异常告警,耗费大家的精力;其次,该有的告警要有,不能系统出现异常或者故障了,没有相关的告警被触达给开发同学。

所谓对象准确,就是我们告警通知的对象,要设置合理,不要随意设置一堆人。告警对象需要在告警通知时,及时响应告警,查找问题,回复告警原因。因此,该业务owner,备份owner,必须要指定清楚。非相关干系人,一般不需要设置,除非一些特殊情况,比如特征中心的接口,使用三方数据采集系统的指标监控告警,那么可以加上三方系统的owner。

3.2.2 风险准确触达

要做到准确触达,其实挺难的,而且要不断优化不断调整。

告警设置,首先有个问题,就是是针对现象设置告警,还是针对原因设置告警。如果我们对业务十分熟悉,只要出现原因级别的告警,就知道哪些对外业务会存在问题,但是,现阶段,我们优先对现象进行告警,然后在日常问题中对原因也设置告警,不过不建议不告警现象。

比如,我们监控的对外接口RT耗时过大告警,然后数据库查询耗时过大告警,都出现了。这里,接口告警其实是现象告警,数据库sql慢告警就是“原因”告警。当然,再底层原因,可能是数据量大导致sql慢,也可能请求多导致sql慢,或者机器本身原因导致sql执行慢,需要具体看。(所以这里”原因”打了引号。)

因此,简单而言,我们现在,对所有我们关心的监控大盘,进行告警设置。

确认好了针对啥去设置告警,接下来需要考虑告警阈值如何设置。

一般而言,我们对上线的服务,可以做一个大致的预估,给出一些阈值。比如,预估准入决策下,某个场景业务就最高也就10qps,那么我们就按照10qps来去做一些阈值设置。

还有一种比较简单的情况,就是业务正常运行几天,通过运行的数据,来推测合理请求量,合理失败率,合理耗时等等,然后来设置告警阈值,在通过告警的一些反馈,不断修正阈值,最后接近正确的触达。

3.2.3 告警维度

嗯,针对监控大盘,我们需要怎样设置异常告警。

划重点!!!

以下是多年经验的总结,抛砖引玉。

系统请求量类告警

  • 我们预估一个服务的处理能力是QPS 1w,那么当请求量超过1w的时候,就肯定需要告警,或者当请求接近1w的时候,就需要告警,给开发同学一个时间提前准备;
  • 假设,当我们预估正常的请求量都在10以上,那么我们就需要设置一个低于10的QPS告警,这类告警阈值需要考虑清楚,避免低峰期间无效告警。

系统耗时类告警

  • 我们预估一个服务的最大处理RT 50ms,那么当1分钟(天网监控粒度)超过50ms,就需要告警;
  • 假设,针对某些对外服务的业务,请求方超时设置30ms,则意味着我们需要对这些进行告警,当平均耗时超过30ms时,需要告警,这意味着上游业务都是失败的。

系统异常类告警

  • 我们预估对外服务的接口,能够容忍的一个系统失败数10,那么当失败数超过10,就需要告警(这类告警很容易变成无效告警,所以需要根据业务发展进行调整);
  • 同样,某个业务可能容忍的失败数比较大,直接用本系统的失败告警,但是有些业务可能请求数比较小,但是业务还很重要,这个时候需要针对这些业务细粒度设置告警阈值。

系统同比环比告警

某些情况下,无法给直接阈值。一般而言,因为电商交易很多时候以周为周期,所以可以采用周同比,环比一般因为业务一直不断发展,可参照价值反而比较小。

  • 针对请求量和异常类次数告警,可以采用周同比的方式,去设置一些震荡比率,触发告警。比如异常次数周同比增幅20%,可能认为系统存在未知问题,需要定位。

3.2.4 总结

依然做一个总结,我们要怎么做告警。

因为监控是基于对业务的梳理,所以告警前期针对监控大盘(现象)进行合理阈值的设置即可;后期,逐步针对一些原因监控,设置阈值告警。目的,同时接收现象和原因告警,可以关联起来,从而收到告警的时候,能大致感知到系统异常原因。

3.2.5 最后

告警,有新增就得有删除。

如果一个告警出现之后,没有人去关注,或者不需要去关注,那么这个告警需要删除或者调整了;
如果一个告警出来之后,接收人不明所以,或者根本猜都猜不出来原因,那么这个报警是无价值的,需要其他原因告警项来新增进来,辅助决策;
如果一个告警十分频繁的触发,那么请立马修复问题,或者调整告警阈值。
告警内容,最大的价值,是不上线也能大概知道哪里除了问题,如果做不到,那么告警的优化还需要持续进行…….

四、日志

日志,目的是为了协查问题。所以,日志需要规范化,正式化。

4.1 日志内容

日志的目的,是为了发现异常,定位问题。所以,如果一个参数失败,但是不把参数打出来,你怎么知道为啥参数失败。

日志打印,可以使用日志模板。根据自己的喜好,可以分多个日志文件,或者业务日志都放在一个文件里。日志需要上传到天网,便于分布式环境下,查问题。

划重点!!!

日志需要打印哪些内容。

  • 对外服务的请求入参,返回出参 ;
  • 对外服务的请求整体耗时,不管成功失败,都需要打印耗时。
  • 依赖的三方服务,请求发起的参数和时间,请求接收到的参数和耗时。
  • 所有业务失败,抛异常的地方,都需要打印日志,包括异常堆栈,功能描述,失败原因,可能查问题时需要的上下文数据。
  • 所有非预期异常或失败,打印ERROR级别日志,预期的失败异常,一般使用WARN级别日志级别即可。
  • 打印日志,必须要有traceid,便于调用链查询,需要关注的功能简述,使用中文,且放在日志内容前面,便于天网日志平台检索。例如,log.info(“[支付预下单]业务请求request:{}”,request) 或者 log.info(“[{}]业务请求request:{}”,this.getClass().getSimpleName(),request)

五、总结

SRE 一般讲 四个黄金指标:延迟,流量,错误 ,饱和度。

  • 延迟:处理请求所需要的耗时,区分成功和失败都需要;
  • 流量:针对系统负载需求锁进行度量的指标,例如QPS,TPS等;
  • 错误:请求失败的速率;
  • 饱和度:服务容量有多满。受限资源指标度量,例如内存利用率,IO利用率等。

以上,根据实践过程,随时调整和补充。