智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

在上一篇智能运维 | 解放程序员,一个工具就能锁定程序故障文章中我们主要介绍了一种在服务发生故障时自动排查监控指标的算法。算法的第一步利用了概率统计的方式估算每个指标的异常分数,第二步用聚类的方式把异常模式相近的实例聚集在一起形成摘要,第三步用ranking的方式向工程师推荐最有可能是根因的摘要。

由于运维场景的特点是数据量大,但是标定很少,生成标定的代价高昂而且容易出错,所以我们综合利用了概率统计、非监督学习和监督学习的方法解决问题,在尽量减少训练数据的前提下取得了良好的效果。

在离线实验的70个故障案例中,我们的算法能够把60个案例的根因摘要rank在第一位,显示出了算法强大的生命力。

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

异常检测算法

异常检测算法是我们整个自动排查监控指标工具的核心,它需要解决跨实例跨指标的异常程度比较问题。这涉及一个本源的问题:什么是异常?

请闭上眼睛想1分钟,再接着往下看。

是不是觉得这个问题似乎不大好回答?作者也拿这个问题为难过不少人。有的人说异常就是不正常呗,但是正常是什么就说不清楚了。有的人说,你给我一些具体的数据我能告诉你哪些是异常,但是脱离具体数据抽象的说不出来。还有人一上来就说,这个问题还真是从来没想过。

正所谓“少见多怪”,说的是因为很少见或者干脆没见过,所以觉得奇怪。又有说“司空见惯”,说的是天天见,所以就习惯了。这两个成语表达的就是不常见的事情容易被看成是怪的,常见的事情就不怪了。换成数学的语言就是:小概率事件是异常,大概率事件是正常。虽然这个说法不是百分之百准确,但是大部分时候还就是这样。所以,衡量指标的异常程度可以用观测到指标值的概率来描述,既然是概率,当然是可以比较的,而且可以跨实例、跨指标地比较。

指标值的观测概率可以从多个不同的角度来建模计算,这里我们主要关注突增突降的情况。指标的突增突降是很容易被人所理解的。比如,工程师们很容易就同意图1中的指标有异常。另一方面,许多故障都会体现在某些指标的突增突降上。最关键的是,计算指标的突增突降对应的观测概率是比较容易的,很适合我们这种需要扫描大量指标的场景。

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

图1  CPU_WAIT_IO的突增说明出现了磁盘竞争

计算突增突降的观测概率需要先收集一些数据。假设故障从智能运维 | 解放程序员,一个工具就能锁定程序故障(下)时刻开始发生,我们收集某一指标在区间智能运维 | 解放程序员,一个工具就能锁定程序故障(下)内的数据智能运维 | 解放程序员,一个工具就能锁定程序故障(下)和区间智能运维 | 解放程序员,一个工具就能锁定程序故障(下)内的数据智能运维 | 解放程序员,一个工具就能锁定程序故障(下)显然智能运维 | 解放程序员,一个工具就能锁定程序故障(下)是故障发生前的数据,如果智能运维 | 解放程序员,一个工具就能锁定程序故障(下)小于故障持续时间,智能运维 | 解放程序员,一个工具就能锁定程序故障(下)就是故障发生中的数据。一般来说,我们可以让智能运维 | 解放程序员,一个工具就能锁定程序故障(下)长一点,提高概率的准确度。但是智能运维 | 解放程序员,一个工具就能锁定程序故障(下)必须尽量短,这样才能尽快的产出诊断结果。

有了智能运维 | 解放程序员,一个工具就能锁定程序故障(下)智能运维 | 解放程序员,一个工具就能锁定程序故障(下),我们就可以比较两组数据,产出观测概率。用数学的语言说就是要计算

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

也就是在智能运维 | 解放程序员,一个工具就能锁定程序故障(下)时刻之前观测到智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的前提下,在时刻之后观测到智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的概率。

首先我们计算单点概率智能运维 | 解放程序员,一个工具就能锁定程序故障(下)。假设智能运维 | 解放程序员,一个工具就能锁定程序故障(下)是由某一个随机变量智能运维 | 解放程序员,一个工具就能锁定程序故障(下)产生的样本,我们可以通过智能运维 | 解放程序员,一个工具就能锁定程序故障(下)来估算智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的分布,然后根据智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的分布计算智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的观测概率。因为大多数指标的取值都是连续的,所以智能运维 | 解放程序员,一个工具就能锁定程序故障(下)也应该是连续型随机变量,相应的概率就变成

上溢概率:

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

下溢概率:

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)智能运维 | 解放程序员,一个工具就能锁定程序故障(下)大的概率很小时,说明智能运维 | 解放程序员,一个工具就能锁定程序故障(下)太大了,对应的是突增;当智能运维 | 解放程序员,一个工具就能锁定程序故障(下)智能运维 | 解放程序员,一个工具就能锁定程序故障(下)小的概率很小时,说明太小了,对应的是突降。假设智能运维 | 解放程序员,一个工具就能锁定程序故障(下)是由与相互之间独立且与智能运维 | 解放程序员,一个工具就能锁定程序故障(下)同分布的随机变量产生的样本,则集合概率

上溢概率:

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

下溢概率:

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

为了方便计算和人的查看,通常会计算概率的对数。概率的对数都是负数,大家看正数比较自然,所以我们就把概率的对数的负数作为异常分数

上溢分数:

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

下溢分数:

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

剩下的问题就是如何确定智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的分布了。常用的办法是假设服从正态分布,并利用智能运维 | 解放程序员,一个工具就能锁定程序故障(下)估计正态分布的两个参数

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

最后计算基于智能运维 | 解放程序员,一个工具就能锁定程序故障(下)计算检验智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的统计量

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)服从以0为均值,以1为标准差的正态分布智能运维 | 解放程序员,一个工具就能锁定程序故障(下)。可以通过正态分布来计算单点的上溢概率和下溢概率

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

顺便说一下,智能运维 | 解放程序员,一个工具就能锁定程序故障(下)大于3或者小于-3的概率大约是0.13%,已经很小了,所以异常检测的时候常常以3作为统计量智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的阈值。这就是智能运维 | 解放程序员,一个工具就能锁定程序故障(下)常用的方法。

这个方法最大的问题就是许多指标的值并不服从正态分布。比如,CPU_IDLE是一个在0到1之间取值的指标。按照上面的方法计算时智能运维 | 解放程序员,一个工具就能锁定程序故障(下)常常会小于0,而智能运维 | 解放程序员,一个工具就能锁定程序故障(下)常常会大于1,这样就无法正确反应指标的异常程度了。因此我们采用了核密度估计(Kernel Density Estimation)的方法。

核密度估计是一个估计概率密度函数的非参数(non-parametric)方法,适用于随机变量服从的分布类型未知的情况。基于观测样本智能运维 | 解放程序员,一个工具就能锁定程序故障(下)智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的概率密度函数可以估计为

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

其中智能运维 | 解放程序员,一个工具就能锁定程序故障(下)是核函数。如果核函数是正态分布,那么智能运维 | 解放程序员,一个工具就能锁定程序故障(下)参数大约就接近于智能运维 | 解放程序员,一个工具就能锁定程序故障(下)的标准差智能运维 | 解放程序员,一个工具就能锁定程序故障(下),于是概率密度函数就变成了

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

对于像CPU_IDLE一样的指标,我们可以用Beta分布作为核函数,这时

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

这里智能运维 | 解放程序员,一个工具就能锁定程序故障(下)是一个正数,用来控制Beta分布的形状。智能运维 | 解放程序员,一个工具就能锁定程序故障(下)越大,Beta分布越尖;智能运维 | 解放程序员,一个工具就能锁定程序故障(下)越小,Beta分布越平坦。

有了概率密度函数,单点的概率就可以利用积分计算了

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)


智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

聚类

在为每个指标计算异常分数之后,我们把属于同一个实例的指标的异常分数合起来,形成一个异常分数的向量

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

这里的每一个下标对应一个指标。

为了便于工程师阅读,我们需要把异常模式差不多的向量通过聚类的方法聚在一起。聚类是无监督学习(Unsupervised Learning)的典型方法之一。与监督式学习(SupervisedLearning)不同,聚类不能通过样本的标记(Label)来控制输出的结果,所以控制聚类方向的核心就落在了距离函数和聚类算法上。

距离函数的选择很多。通常的有欧氏距离,或者更加一般的智能运维 | 解放程序员,一个工具就能锁定程序故障(下)距离;计算向量之间夹角大小的余弦(cosine)距离;甚至可以使用相关系数来度量两个向量之间的距离。当属于同一个模块的不同实例之间负载几乎完全相同时,欧式距离就应该能给出不错的结果。但是,实际中实例之间的负载总是会有差别,这就使得同样的异常模式在不同的实例上表现出不同的异常分数,欧式距离就会使聚类的效果打折扣。这时,相关系数是一个不错的选择。Pearson Correlation计算两个实例对应异常分数的相关性,同时能够忽略数值的绝对水位。Spearman Correlation首先将一个实例指标按照异常程度排序,然后计算两个实例异常程度排序之间的相关性。在我们的实验中Spearman Correlation的效果是不错的。

在聚类算法上,由于我们事先无法知道有多少种不同的异常模式存在,K-means显然就不是一个很好的选择了。层次聚类和DBSCAN都不需要预先设定聚类的个数,能更好地适应我们的需求。在实验中,我们采用了DBSCAN,确实得到了很好的效果。

聚类时,理论上我们可以把所有模块的所有实例放在一起聚类。在实验中我们发现这样的聚类结果有时候也还可以,而且它可以发现跨模块的问题,比如由于网络故障导致多个模块之间通信收到影响最终产生整个系统的故障。但是,这种方法也在不少情况下的输出并不好。所以,我们采用了相对保守的方式,将聚类局限在属于同一个模块、同一个机房的实例中。

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

排序(Ranking)

聚类算法把异常模式相似的实例聚到了一起,形成摘要。每个摘要已经具备一定的可读性,我们只需要把属于根因模块的摘要排在前面推荐给工程师就大功告成了。

排序算法考虑的因素有以下三个:

1. 摘要中实例的个数,实例越多说明影响越大,就越有可能是根因。

2. 摘要中异常分数特别高的指标的个数,指标越多越可能是根因。

3. 摘要中各实例指标的平均异常分数,平均分数越高越可能是根因。

将以上三个因素的值计算出来,就形成了三个feature。我们只需要用通常的方法训练一个ranker就可以了。

表格 1 中是错误!未找到引用源。 所示的那个故障案例的自动分析结果。

标中的第一列是摘要所属的模块与机房。

第二列是摘要中实例在同模块同机房的实例中所占的比例。

第三列是摘要中的实例列表。

第四列是异常分数特别高的指标以及它们的异常分数。

我们的算法确实把根因模块G中的一个摘要最为第一个推荐出来了。通过第四列的内容,我们可以看出机房DC1中的G模块因为某种原因超载了。G超载后,它对上游模块的响应变慢,从而使得同机房中的E模块不得不维护更多的RPC连接,所以同机房的E模块被拍在了第二位,而且与网络连接数相关的指标也确实出现了异常。

表1  故障的排查分析结果

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

总  结

故障诊断作为智能运维中的一个典型的复杂场景,一直是众多运维工程师的努力方向。本文介绍的指标自动排查算法表明,除了机器学习之外,概率统计也将会在智能运维中占据重要的位置。

关于故障诊断的研究,如有任何想法和疑问,欢迎留言一起交流。

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

作者简介

运筹    百度云资深数据架构师

 

负责百度云智能运维(Noah)算法和策略的研究工作,致力于用算法和数据的力量解决运维问题。

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)

智能运维 | 解放程序员,一个工具就能锁定程序故障(下)