根因分析初探:一种报警聚类算法在业务系统的落地实施

本文讨论了在业务服务中,随着系统复杂度增加,错误日志量级剧增导致故障定位困难的问题。作者提出通过聚类算法对报警进行处理,以生成概括性报警摘要,帮助运维人员快速定位故障。文章介绍了算法选择、实现细节和实验验证,尽管存在不足,但展示了提升故障排查效率的潜力。
摘要由CSDN通过智能技术生成

文章目录

背景

众所周知,日志是记录应用程序运行状态的一种重要工具,在业务服务中,日志更是十分重要。通常情况下,日志主要是记录关键执行点、程序执行错误时的现场信息等。系统出现故障时,运维人员一般先查看错误日志,定位故障原因。当业务流量小、逻辑复杂度低时,应用出现故障时错误日志一般较少,运维人员一般能够根据错误日志迅速定位到问题。但是,随着业务逻辑的迭代,系统接入的依赖服务不断增多,引入的组件不断增多,当系统出现故障时(如Bug被触发、依赖服务超时等等),错误日志的量级会急剧增加。极端情况下甚至出现“疯狂报错”的现象,这时候错误日志的内容会存在相互掩埋、相互影响的问题,运维人员面对报错一时难以理清逻辑,有时甚至顾此失彼,没能第一时间解决最核心的问题,很难做到快速定位

错误日志是系统报警的一种,实际生产中,运维人员能够收到的报警信息多种多样。如果在报警流出现的时候,通过处理程序,将报警进行聚类,整理出一段时间内的报警摘要,那么运维人员就可以在摘要信息的帮助下,先对当前的故障有一个大致的轮廓,再结合技术知识与业务知识定位故障的根本原因

围绕上面描述的问题,以及对于报警聚类处理的分析假设,本文主要做了以下事情:

选定聚类算法,简单描述了算法的基本原理,并给出了针对报警日志聚类的一种具体的实现方案。
在分布式业务服务的系统下构造了三种不同实验场景,验证了算法的效果,并且对算法的不足进行分析阐述。
目标
对一段时间内的报警进行聚类处理,将具有相同根因的报警归纳为能够涵盖报警内容的泛化报警(Generalized Alarms),最终形成仅有几条泛化报警的报警摘要。如下图1所示意。
图一
我们希望这些泛化报警既要具有很强的概括性,同时尽可能地保留细节。这样运维人员在收到报警时,便能快速定位到故障的大致方向,从而提高故障排查的效率。

设计

如图2所示,异常报警根因分析的设计大致分为四个部分:收集报警信息、提取报警信息的关键特征、聚类处理、展示报警摘要。

在这里插入图片描述
算法选择
聚类算法采用论文“Clustering Intrusion Detection Alarms to Support Root Cause Analysis [KLAUS JULISCH, 2002]”中描述的根因分析算法。该算法基于一个假设:将报警日志集群经过泛化,得到的泛化报警能够表示报警集群的主要特征。以下面的例子来说明,有如下的几条报警日志:

server_room_a-biz_tag-online02 Thrift get deal ProductType deal error.
server_room_b-biz_tag-offline01 Pigeon query deal info error.
server_room_a-biz_tag-offline01 Http query deal info error.
server_room_a-biz_tag-online01 Thrift query deal info error.
server_room_b-biz_tag-offline02 Thrift get deal ProductType deal error.

我们可以将这几条报警抽象为:“全部服务器 网络调用 故障”,该泛化报警包含的范围较广;也可以抽象为:“server_room_a服务器 网络调用 产品信息获取失败”和“server_room_b服务器 RPC 获取产品类型信息失败”,此时包含的范围较小。当然也可以用其他层次的抽象来表达这个报警集群。

我们可以观察到,抽象层次越高,细节越少,但是它能包含的范围就越大;反之,抽象层次越低,则可能无用信息越多,包含的范围就越小。

这种抽象的层次关系可以用一些有向无环图(DAG)来表达,如图3所示:
图3 泛化层次结构示例
为了确定报警聚类泛化的程度,我们需要先了解一些定义:

  • 属性(Attribute):构成报警日志的某一类信息,如机器、环境、时间等,文中用Ai表示。
  • 值域(Domain):属性Ai的域(即取值范围),文中用Dom(Ai)表示。
    泛化层次结构(Generalization Hierarchy):对于每个Ai都有一个对应的泛化层次结构,文中用Gi表示。
  • 不相似度(Dissimilarity):定义为d(a1, a2)。它接受两个报警a1、a2作为输入,并返回一个数值量,表示这两个报警不相似的程度。与相似度相反,当d(a1, a2)较小时,表示报警a1和报警a2相似。为了计算不相似度,需要用户定义泛化层次结构。

实现

  1. 提取报警特征
    根据线上问题排查的经验,运维人员通常关注的指标包括时间、机器(机房、环境)、异常来源、报警日志文本提示、故障所在位置(代码行数、接口、类)、Case相关的特殊ID(订单号、产品编号、用户ID等等)等。

但是,我们的实际应用场景都是线上准实时场景,时间间隔比较短,因此我们不需要关注时间。同时,Case相关的特殊ID不符合我们希望获得一个抽象描述的要求,因此也无需关注此项指标。

综上,我们选择的特征包括:机房、环境、异常来源、报警日志文本关键内容、故障所在位置(接口、类)共5个。

  • 算法实现
    (1) 提取关键特征
    我们的数据来源是日志中心已经格式化过的报警日志信息,这些信息主要包含:报警日志产生的时间、服务标记、在代码中的位置、日志内容等。
  • 故障所在位置
    优先查找是否有异常堆栈,如存在则查找第一个本地代码的位置;如果不存在,则取日志打印位置。

重点思考,博主原创:
笔者公司的业务主机日志绝大多数统一放入ELK中,其中关键业务做了埋点,绝大多数信息都可以采集到,并且按照业务的完整流程,将失败的点,通过代码调用链路的方式在EFK中展示出来,极大的提高了运维效率,快速定位问题。

如下图,清晰明了,运维终于不用天天打dump了
在这里插入图片描述

java 埋点实现 代码埋点优点

  • 异常来源
    获得故障所在位置后,优先使用此信息确定异常报警的来源(需要预先定义词典支持);如不能获取,则在日志内容中根据关键字匹配(需要预先定义词典支持)。

  • 报警日志文本关键内容
    优先查找是否有异常堆栈,如存在,则查找最后一个异常(通常为真正的故障原因);如不能获取,则在日志中查找是否存在“code=……,message=……” 这样形式的错误提示;如不能获取,则取日志内容的第一行内容(以换行符为界),并去除其中可能存在的Case相关的提示信息

  • 提取“机房和环境”这两个指标比较简单,在此不做赘述。

(2) 聚类算法
算法的执行,我们以图4来表示。
图4 报警日志聚类流程图
(3) min_size 选择
考虑到日志数据中可能包含种类极多,且根据小规模数据实验表明,min_size = 1⁄5 * 报警日志数量时,算法已经有较好的表现,再高会增加过度聚合的风险,因此我们取min_size = 1⁄5 * 报警日志数量,ℇ参考论文中的实验,取0.05。

(4) 聚类停止条件
考虑到部分场景下,报警日志可能较少,因此min_size的值也较少,此时聚类已无太大意义,因此设定聚类停止条件为:聚类结果的报警摘要数量小于等于20或已经存在某个类别的count值达到min_size的阈值,即停止聚类。

  1. 泛化层次结构
    泛化层次结构,用于记录属性的泛化关系,是泛化时向上抽象的依据,需要预先定义。

根据实验所用项目的实际使用环境,我们定义的泛化层次结构如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
“故障所在位置”此属性无需泛化层次结构,每次泛化时直接按照包路径向上层截断,直到系统包名。

实验
以下三个实验均使用C端API系统。

  1. 单依赖故障
    实验材料来自于线上某业务系统真实故障时所产生的大量报警日志。

环境:线上
故障原因:产品中心线上单机故障
报警日志数量:939条
部分原始报警日志如图9所示,初次观察时,很难理出头绪。
在这里插入图片描述
经过聚类后的报警摘要如表1所示:
在这里插入图片描述
我们可以看到前三条报警摘要的Count远超其他报警摘要,并且它们指明了故障主要发生在产品中心的接口。

  1. 无相关的多依赖同时故障
    实验材料为利用故障注入工具,在Staging环境模拟运营置顶服务和A/B测试服务同时产生故障的场景。

环境:Staging(使用线上录制流量和压测平台模拟线上正常流量环境)
模拟故障原因:置顶与A/B测试接口大量超时
报警日志数量:527条
部分原始报警日志如图10所示:
在这里插入图片描述
在这里插入图片描述
从上表可以看到,前两条报警摘要符合本次试验的预期,定位到了故障发生的原因。说明在多故障的情况下,算法也有较好的效果。

  1. 中间件与相关依赖同时故障
    实验材料为利用故障注入工具,在Staging环境模拟产品中心服务和缓存服务同时产生超时故障的场景。

环境:Staging(使用线上录制流量和压测平台模拟线上正常流量环境)
模拟故障原因:产品中心所有接口超时,所有缓存服务超时
报警日志数量:2165
部分原始报警日志如图11所示:
在这里插入图片描述
在这里插入图片描述
从上表可以看到,缓存(Squirrel和Cellar双缓存)超时最多,产品中心的超时相对较少,这是因为我们系统针对产品中心的部分接口做了兜底处理,当超时发生时后先查缓存,如果缓存查不到会穿透调用一个离线信息缓存系统,因此产品中心超时总体较少。

综合上述三个实验得出结果,算法对于报警日志的泛化是具有一定效果。在所进行实验的三个场景中,均能够定位到关键问题。但是依然存在一些不足,报警摘要中,有的经过泛化的信息过于笼统(比如Other Exception)。

经过分析,我们发现主要的原因有:其一,对于错误信息中关键字段的提取,在一定程度上决定了向上泛化的准确度。其二,系统本身日志设计存在一定的局限性。

同时,在利用这个泛化后的报警摘要进行分析时,需要使用者具备相应领域的知识。

未来规划
本文所关注的工作,主要在于验证聚类算法效果,还有一些方向可以继续完善和优化:

日志内容的深度分析。本文仅对报警日志做了简单的关键字提取和人工标记,未涉及太多文本分析的内容。我们可以通过使用文本分类、文本特征向量相似度等,提高日志内容分析的准确度,提升泛化效果。
多种聚类算法综合使用。本文仅探讨了处理系统错误日志时表现较好的聚类算法,针对系统中多种不同类型的报警,未来也可以配合其他聚类算法(如K-Means)共同对报警进行处理,优化聚合效果。
自适应报警阈值。除了对报警聚类,我们还可以通过对监控指标的时序分析,动态管理报警阈值,提高告警的质量和及时性,减少误报和漏告数量。

笔者所在公司也是通过EFK实现,将每一个点的报错信息输出后,进行汇总打印,确实有助于定位问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值