总19期
Practice    实践
Practice    实践
智能运维中的异常定位实践
文/张闯 杨新安
分享

多维度指标的异常定位是智能运维(AIOps)领域的一个典型且有挑战的问题。在网络服务运维中,当某个总指标(如总流量)发生异常时,需要快速准确地定位到是哪些交叉维度的细粒度指标异常导致,以便尽快做进一步的修复止损操作。由于运维中的指标维度多、每个维度取值范围大,导致异常定位时的搜索空间非常大。

如图1 为多维度、可累加KPI(比如总流量)的举例。其中,A、B、C 代表不同维度(比如A表示省份,B表示运营商等),a1、b1、a1b1 表示对应维度(或交叉维度)下的属性值(或属性值组合)(比如a1表示北京,a2表示山东,b1表示联通,a1b1表示省份=北京 & 运营商=联通)。

https://github.com/iopsai/iops2019/blob/master/iops2019-master/pictures/web_problem/f1.png?raw=true

图1 多维度指标集示意图

解决该问题主要面临三个挑战:

1. 实时性要求高。如前所述,当KPI总值发生异常时,运维人员希望快速定位到根因,以快速止损或恢复。然而,随着维度的增多和各维度中属性值数目的增加,维度及其属性值组合的数目会急剧增加。因此,当维度较多以及各维度中属性值数目较多时,对算法的计算效率提出了很高的要求。

2. 元素指标之间有关系较为复杂。如图1所示,KPI维度的属性值存在着可累加关系。例如,KPI总值等于A维度(或B\C维度)下所有元素值之和,属性值为a1的KPI则等于a1b1、a1b2……等属性值的KPI的加和。此外,不同维度间的属性值也会相互影响。例如,当维度为位置,属性值为 “北京” 的KPI发生异常时,一般属性值为“北京移动”、“北京联通”的KPI也会发生异常,进而属性值为“移动、联通”的KPI也会发生异常。

3. 要求结果尽可能简洁。异常定位返回的结果是导致KPI总值发生异常的维度及其属性值集合。该结果会提供给运维人员以尽快核实并修复根因。因此,返回的结果需要尽量简洁、精确。所以,我们需要用尽可能少的维度及其属性值的组合表示出最为全面的根因。在探讨解决方法之前看看几个类似问题的解决方法。

广告系统营收问题定位

比如一个小说网站的广告系统,每当用户在看小说时,打开网页时广告系统会推送一些广告,如果用户点击了广告,则广告系统会进行记录,并向相应的广告提供商进行收费。

广告收入按维度可以分为广告提供商、用户设备类型、提供广告的数据中心。当广告收入下降时希望能尽快找到根因,比如因为系统BUG导致广告不能正常在手机端呈现,则可以第一时间尽快解决BUG,从而保证营收尽快恢复。

图2 广告收入维度

以图2 为例,预计总的广告营收是100美元,实际营收是50美元,需要定位是什么原因导致的,根因要能解释大部分的变化量,并且要尽量简洁。

我们可以从这个角度看虽然X的变化很大,但是之前他的预测值的量也是很大的,也就是预测值占总比占94%,而真实值占总比也是94%,单从设备类型的维度看呢,PC的预测值占总比是50%,真实值占总比确成了98%,这个变化还是比较令人惊讶的。因此除了能解释大部分的变化量外还可以同时考虑占总比的变化情况。

此方法对于单独维度的根因定位效果不错,但是存在一个问题就是不能处理根因是多维度交叉的情况如Revenue Drop (DT == PC ^ DC == X)。

投诉问题根因定位

在一些在线软件系统的部署运行中,会因为这样或那样的问题导致给用户提供的服务不可用,这样用户会进行问题投诉,而投诉一般也是有多个属性的,如图3所示:

图3 投诉属性

比如,如下属性组合Country=\India"; TenantType=\Edu"; DataCenter=\DC6"的投诉之前一直是每天70个左右,从12月8号起突然增加到了300多个,这个属性组合的投诉突增比较剧烈,就提供了有用的线索用来定位根因。就这个实际案例来说,工程师根据这个线索,发现了是软件配置的问题,导致了Edu订阅了教育类型服务,并且使用数据中心DC6的账户创建失败。找到问题原因后,可以及时的纠正问题。

此方法为了将真正的根因组合(对应的时间序列的值发生了明显的变化)和其它没有明显变化的属性组合分开。但iDice在实际使用中如果同时是多个根因时,会存在不准确的问题。

多维属性的加性KPI的异常定位

分析的是如网页访问量出现突然下降时怎么寻找根因的问题。使用的数据如图4所示,可以看到也是多维度的问题,包括省份、运营商、数据中心等的网页访问量。

图4 网页访问量

此方法认为当异常发生时,存在涟漪效应。从图5也可以比较清晰地看出来效果:“省份==北京”的各个子项都相应的等比例进行了变化,而子项的变化会在另一个维度影响运营商维度的值。

图5 异常涟漪效应

那当检测到总量发生变化时,怎样有效的查找根因呢?根据涟漪效应可以先假定根因,然后用涟漪效应推断各个子项的值,而后用各个子项的推断值、真实值、预测值用公式计算出potential score值。此值越大,说明越可能是根因。

深层的多维交叉时Adtributor效果很差,当根因数量比较多时iDice的效果也明显变差,HotSpot在多维交叉时相对效果最好。如图6。

图6 深层的多维交叉

智能运维实践

使用的数据集具有如下特点:

1. 每五分钟一个文件,每个文件的前五列是属性,最后一列是值,数值大部分都是0。

2. 异常的KPI增长倍数差别较大

3. 深层elements的下一层节点很少(稀疏)

4. 图7与图8每张图是14天的总KPI的数据,5分钟一个点,共4032个点,红色为需要定位根因的异常点。

图7 14天总KPI的数据-1

图8 14天总KPI的数据-2

对于单个KPI在异常点的图(如图9所示),蓝色虚线为KPI对应的曲线,黄色线为去掉异常点后进行插值得到的曲线,绿色线为二次指数平滑fit的曲线。可以看到对于单个KPI来说,发生异常时变化还是比较剧烈的。

图9 KPI异常点

斟酌如下几种方案:

  1. Prophet,每个大部分时间不为0的KPI训练一个模型,不仅能得到预测值,还能得到预测区间,顺带可以一定程度上做异常检测,难以逐点检测。
  2. R forecast包中的ETS,也需要每个大部分时间不为0的KPI训练一个模型,不仅能得到预测值,还能得到预测区间,顺带可以一定程度上做异常检测,但是涉及到python调用R的接口,增加最终运行的docker环境的复杂性。
  3. 使用python实现指数平滑类(二次或三次指数平滑),每个KPI单独训练最佳参数,可以逐点预测,进行预测时速度会非常快,应该能满足性能需要。

图10为KPI上缺省参数使用Prophet的效果,不仅能得到预测值,还能得到预测区间,顺带可以一定程度上做异常检测

图10 KPI上缺省参数使用Prophet的效果

但是,在抽样查看数据集时发现,个别的KPI的形态也会随时间发生变化,为了在新的环境部署时支持冷启动,不需要很长时间的数据用来训练模型,确定超参。并且,考虑到复杂的算法后期执行时可能会对执行时间的影响,以及需要检测预测效果,当预测偏差大时启动重新训练,增加代码复杂度,因此没有使用复杂的预测方法。

经过权衡最终决定使用移动中值作为预测方案,因为历史同期值不如移动中值更接近,因此只用了当前点和前几个点的中值作为预测值,编程简单,执行速度快,可以冷启动。选用中值是因为和均值比受异常值的影响小。

预测方案直接用前几点和当前点的中值,简单快捷,不用太多历史数据,可以冷启动,部署后可以迅速开始工作。定位方法:借鉴Hotspot中计算Potential Score的方法和Recursive Adtributor方法,实现多个决策器,加权投票得到最终结果,最终的程序执行速度快。

迭代因子(Recursive adtributor方法)

先回顾一下Adtributor方法,对于每个Attribute,分别计算此Attribute内的values的Surprise,而后排序,大于一定EP阈值的纳入根因集,Surprise做和,当总的EP大于总阈值时停止增加,每个Attribute的根因集比较总的Surprise,选取最大的。但原始Adtributor存在的问题是只能定位第一层的根因,不能定位更深层次的交叉根因。

图11 Adtributor方法

综合各个方法,我们用了迭代切片的方法,先从总的Cube对第一层使用类似adtributor的方法找到应该从哪个Attribute去切(找根因),找出相应的elements,并且将相应的element做为新的Cube,再次迭代用类似Adtributor的方法在新的Cube中找到应该从哪个Attribute中去切(找根因),以此类推。什么时候终止切片根据Isolation Power是不是能得到提升作为判断依据

图12 迭代切片方法

1. 可以想象整个cube为一个大切糕,先用类似adtributor的方法找到应该从那个Attributes去切,比如按adtributor的方法,得到应该从Attrbute C的Attrbute Values为c1的角度去切,则直接将C==c1的数据全取出形成一个新的切糕(cube),同时C这个Attribute后续不需要再考虑了,同时计算出c1的isolation power。

2. 而后在这个新的切糕(cube)中,再用adtrbutor的方法找到应该从那个维度去切,比如Attribute B中找到了b1、b3 ,Attribute A 中找到了a3,计算总surprise,发现b1、b3的surprise更大,则本层应该从B切,切出c1&b1;c1&b3,计算c1&b1;c1&b3的isolation power来同上一层c1的isolation power比较判断是不是本次切是应该的。

3. 如果c1&b1;c1&b3的isolation power大,则应该切,取出C==c1&B==b1的数据作为新的切片, C==c1&B==b3的数据也作为新的切片,继续判断余下的Attribute,以此类推,直到isolation不再下降为止。如果c1&b1;c1&b3的isolation power小,则不应该切,直接返回c1为根因。

Hotspot方法

再回顾一下Hotspot方法,其实就是每个cuboid找出根因集,比较各个cuboid的根因集得分Potential Score,得到最终根因。

图13 Hotspot方法

而用Potential Score时会遇到几个问题:

1. 使用Potential Score时必须要用到预测值,但预测值有时很不容易确定。

2. 层深的影响

使用Potential Score作为衡量标准会出现层次划越深, Potential Score会越高,道理类似与回归数树身越深,预测与真实值的差距会越小。这种主要是越深的同时根因集的elements越多的情况下,可以考虑根据这种情况特殊处理。本方案的解决方法是层次越深且根因的elements明显增多则惩罚越重。

3. 有时同层内加入的无关元素越多,Potential Score也会增加

同层加入元素数量问题:加入元素越多, Potential Score趋向于越大,图14 Ad Unit3为可以理解为正常波动而不是根因,不应该被加入,但是加入后确实可以使Potential Score增大,一种方法是使用EP(explanotory power 见前文中介绍adtributor时提到的)过滤掉正常波动,本方案的解决方法:除了用EP过滤正常波动也同时根据Potential Score增大的幅度开始明显变小停止增加(肘部法则)

图14 同层内加入的无关元素

4. 叶子节点的计算问题

如图15 {Partner 1, Partner 2} Potential Score是0.99但是所有叶子节点的Potential Score是1,本方案解决方法:每增加一个叶子节点同时综合看R值(见前文介绍idice时提到的)等值的变化和个数限制的方法来解决

图15 Potential Score

最终方案是按上面所说的改进的Hotspot Potential Score方法。定位准确率大大提升,且执行速度快。

结束语

综上所述,通过Recursive adtributor方法与Hotspot方法均可解决运维中的指标维度多、每个维度取值范围大的问题,但也都存在一定不足,采用改进的Hotspot Potential Score方法,则可以使定位准确率和效率大大提升。

分享到
关闭