背景
阿里团体针对故障解决提出了“1/5/10”的指标 – 1 分钟发现、5 分钟定位、10 分钟复原,这对咱们的定位能力提出了更高的要求。
EMonitor 是一款集成 Tracing 和 Metrics、服务于饿了么所有技术部门的一站式监控零碎,其笼罩了
- 前端监控、接入层监控;
- 业务 Trace 和 Metric 监控;
- 所有的中间件监控;
- 容器监控、物理机监控、机房网络监控。
每日解决总数据量 近 PB,每日写入指标数据量 几百 T,日均几 千万 的指标查问量,内含 上万个 图表、数千个 指标看板,并且曾经将所有层的监控数据买通并串联了起来。然而在故障来长期,用户依然须要破费大量工夫来查看和剖析 EMonitor 上的数据。
比方阿里本地生存的下单业务,波及到诸多利用,每个利用诸多 SOA 服务之间盘根错节的调用关系,每个利用还依赖 DB、Redis、MQ 等等资源,在下单成功率上涨时,这些利用的负责人都要在 EMonitor 上查看指标曲线以及链路信息来进行人肉排障以自证清白,耗时耗力,所以自动化的根因剖析必不可少。
根因剖析建模
业界曾经有好多在做根因剖析的了,然而大都准确率不高,大部分还在 40% 到 70% 之间,从侧面阐明根因剖析的确是一个难啃的骨头。
根因剖析看起来很宏大,很形象,无从下手,从不同的角度(可能是表象)去看它,就可能走向不同的路。那如何能力透过表象来看到实质呢?
我这里并不会一开始就给你列出高大上的算法解决方案,而是要带你从新认知根因剖析要解决的问题到底是什么。其实好多人对要解决的问题都模糊不清,你只有对问题了解分明了,能力有更好的计划来解决它。
要解决什么样的问题
举个例子:现有一个利用,领有一堆容器资源,对外提供了诸多 SOA 服务或者 Http 服务,同时它也依赖了其余利用提供的服务,以及 DB 资源、Redis 资源、MQ 资源等等,具体见下图;那咱们如何才可能全面的掌控这个利用的健康状况呢?
咱们须要掌控:
- 掌控这个利用的所有 入口服务 的「耗时」和「状态」
- 掌控每个入口服务之后 每种操作 的「耗时」和「状态」
一个利用都有哪些入口?
- SOA 服务入口;
- Http 服务入口;
- MQ 生产音讯入口;
- 定时 job 入口;
- 其余的一些入口。
进入每个入口之后,都可能会有一系列的如下 5 种操作 和 1 种行为(上面的操作属性都是以阿里本地生存的理论状况为例,并且都蕴含所在机房的属性):
- DB 近程操作:有 dal group、table、operation(比方 select、update、insert 等)、sql 的操作属性;
- Redis 近程操作:有 command 的操作属性;
- MQ 近程操作(向 MQ 中写音讯):有 exchange、routingKey、vhost 的操作属性;
- RPC 近程操作:有 近程依赖的 appId、近程 method 的操作属性;
- Local 操作(即除了上述 4 种近程操作之外的本地操作): 暂无属性;
- 抛出异样的行为:有异样 name 的属性。
那咱们其实就是要去统计每个入口之后的 5 种操作的 耗时状况 以及 状态状况 ,以及 抛出异样的统计状况。
针对近程操作其实还要明细化,上述近程操作的 每一次耗时 是蕴含如下 3 大部分:
- 客户端建设连贯、发送申请和接管响应的耗时;
- 网络的耗时;
- 服务器端执行的耗时。
有了上述三要素,能力去确定近程操作到底是哪出了问题,不过理论状况可能并没有上述三要素。咱们的理论状况也是没有的,只能找简略的替换计划,通过判断每个利用的 tcp 重传是否抖动回升来繁难判断是否存在网络问题
能够简略形象成如下几种类型的指标:
指标名
tags
fields
{appId}.soa_provider
appId 的 SOA 服务总耗时指标
ezone: 所在机房
clientApp: 调用方 appId
hostName: 服务方的机器名
method: 服务方的办法名 | timerCount: 总耗时
timerSum: 总次数
timerSum/timerCount: 均匀耗时 |
| {appId}.soa_exception
appId 的 SOA 服务跑出异样的状况 | ezone: 所在机房
hostName: 抛出异样的机器
method: 抛出异样的 soa 服务
name: 抛出异样的异样名 | count: 抛出异样的次数 |
| {appId}.soa_span
具体记录了每个 method 中各个 span 环节的耗时 | ezone: 所在机房
method: 服务方的办法名
spanType: 操作类型,即上述的 DB、Redis、MQ、RPC、Local | timerCount: 总耗时
timerSum: 总次数
timerSum/timerCount: 均匀耗时 |
| {appId}.db
具体记录了每个入口 SOA method 的 DB 操作耗时 | ezone: 所在机房
method: 以后 db 调用是在哪个 soa 服务的 method 中触发的
table:db 操作的表
operation:db 操作的类型,如 insert、update、select
sql:db 操作的 sql | timerCount: 总耗时
timerSum: 总次数
timerSum/timerCount: 均匀耗时 |
| {appId}.rpc
具体记录了每个入口 SOA method 的 rpc 操作耗时 | ezone: 所在机房
method: 以后 rpc 调用是在哪个 soa 服务的 method 中触发的
serviceApp: 依赖的近程调用的 appId
serviceMethod: 依赖的近程调用 method | timerCount: 总耗时
timerSum: 总次数
timerSum/timerCount: 均匀耗时 |
| {appId}.redis
具体记录了每个入口 SOA method 的 redis 操作耗时 | 类同下面 | 同上 |
| {appId}.mq
具体记录了每个入口 SOA method 的 mq 操作耗时 | 类同下面 | 同上 |
故障的论断
针对故障的论断,咱们并不是只给出一个最终的根因,而是须要可能具体出现整个故障链路,以及每个链路节点中故障的详细信息
有了上述数据的全面掌控,当一个故障降临的时候,咱们能够给出什么样的论断?
- 哪些入口受到影响了?
- 受影响的入口的本地操作受到影响了?
-
受影响的入口的哪些近程操作受到影响了?
- 具体是哪些近程操作属性受到影响了?
- 是客户端到服务器端的网络受到影响了?
- 是服务器端出了问题吗?
- 受影响的入口都抛出了哪些异样?
上述的这些论断是能够做到十分高的准确性的,因为他们都是能够用数据来证实的。
然而第二类根因,却是无奈证实的:
- GC 问题;
- 容器问题。
他们一旦出问题,更多的体现是让上述 5 种操作耗时变长,并且是没法给出数据来明确证实他们和 5 种操作之间的关联,只是以一种揣测的形式来狐疑,从实践上来说这里就会存在误定位的状况。
还有第三类更加无奈证实的根因:
- 变更问题
昨天的变更或者以后的变更到底和以后的故障之间有没有关联,更是无奈给出一个证实的,所以也只能是瞎揣测。
咱们能够明确的是 5 种操作的根因,还须要进一步去查看是否是上述第二类根因或者第三类根因,他们只能作为一些辅助论断,因为没法给出谨严的数据证实。
咱们的特点
针对链路拓扑,咱们并不会应用所谓的高大上的常识图谱的计划,咱们只须要将调用关系寄存在相干指标的 tag 上即可,咱们就能够通过实时的指标数据来获取实时的链路拓扑,比方指标 appId1.rpc 中蕴含如下 tag 组合
- method:metho1-1, serviceApp:appId2,serviceMethod:method2-1
- method:metho1-1, serviceApp:appId3,serviceMethod:method3-1
比方咱们查问 appId1.rpc 指标,过滤条件为 method=method1-1,group by serviceApp,serviceMethod,从查问后果中咱们就天然得悉 appId1 依赖了 appId2、appId3 以及依赖的办法。相似的,从调用指标上{appId}.soa_provider,咱们也能够看到是哪些 client appId 在调用以后 appId 的服务的哪些 method 服务
针对因果关系 ,咱们并不会去采纳所谓的曲线相似性检测来辨认相干的指标,而是通过更加谨严的逻辑关系来证实而不是所谓的揣测。 上帝素来都没有通知你曲线类似的指标是有关联的,曲线不类似的指标是没有关联的,比方某个 appId 的 SOA method1 曲线抖动了,它的 DB 也抖动了,难道是 DB 抖动引起了 SOA method1 的抖动?这 2 者到底有没有关联?(有可能是别的 SOA 服务调用 DB 抖动引起 DB 抖动,而以后 SOA 服务抖动可能是因为依赖的 RPC 调用抖动),咱们不是靠曲线相似性检测而是通过实实在在的数据来证实他们的关联性。咱们会记录每个 SOA 服务的各个环节的耗时,比方指标 appId1.soa_span,蕴含如下 tag 组合
- method:metho1,spanType=DB
- method:metho1,spanType=Redis
- method:metho1,spanType=RPC
那么咱们只须要查问 appId.soa_span 指标,过滤条件 method=method1,group by spanType,就是能够从中看失去 method1 服务抖动的根因到底是不是 DB 类型的抖动引起的
根因剖析实现
在明确了咱们要解决的问题以及要求的故障论断之后,咱们要采取什么样的计划来解决呢?上面首先会介绍一个基本功能「指标下钻剖析」。
指标下钻剖析
一个指标,有多个 tag,当该指标总体稳定时,指标下钻剖析可能帮你剖析出是哪些 tag 组合导致的稳定。
比方客户端的 DB 操作抖动了,利用指标下钻剖析就能够剖析出
- 哪个机房抖动了?
- 哪个 dal group 抖动了?
- 哪张表抖动了?
- 哪种操作抖动了?
- 哪个 sql 抖动了?
再比方近程 RPC 操作抖动了,利用指标下钻剖析就能够剖析出
- 哪个机房抖动了?
- 哪个近程 appId 抖动了?
- 哪个近程 method 抖动了?
其实这个就是去年 AIOPS 比赛的题目,具体见:
http://iops.ai/competition_detail/?competition_id=8&flag=1
咱们的计划:
什么叫抖动?某个 SOA 服务耗时 500ms,某个 RPC 环节耗时 250ms,那么这个 RPC 环节肯定是有问题的吗?不肯定,必须要跟它失常状况进行一个比照能力晓得它的这个耗时是否失常。所以须要先去学习一个失常水准,而后基于失常水准的比照能力确定某个环节是否抖动了
咱们的要求:在圈定的曲线范畴内,前一半工夫范畴要失常,异样抖动存在于后一半工夫范畴内
通过对前一半失常工夫范畴的学习,而后基于学习的水准来断定辨认后一半工夫范畴的异样抖动
步骤 1 对整体的曲线确定稳定范畴
关键点 1.1
对前一半工夫范畴提取数据抖动特色,使用二阶指数平滑算法进行数据预测,依据预测值和理论值的稳定信息,提取如下 3 个信息
- 原始数据的最大值
- 原始数据的最小值
- 原始数据和预测数据之间的稳定方差
问题 1:为什么不对原始数据求稳定方差?
要对一些趋势性的曲线可能适应,比方邻近高峰期,均匀响应工夫也是在失常范畴内迟缓回升的
关键点 1.2
对后一半数据依然应用二阶指数平滑算法进行数据预测,依据预测值和理论值的稳定,验证稳定是否超过上述规范稳定方差的 3 倍,如果超过则为稳定点,并提取如下 6 个信息:
- 第一次稳定点:第一次稳定的点
- 最大的稳定点:稳定值最大的点
- 最初一次稳定点:稳定值最大的点
- 初始点:它和稳定值最大的点对于第一次稳定点对称
- 最初一次稳定点
- 向上稳定还是向下稳定
那么初始点~ 第一次稳定点之间的数据是失常范畴,第一次稳定点~ 最大的稳定点是异样范畴
步骤 2 在计算范畴内算出每根工夫线的稳定值
关键点 2.1
对每根工夫线,先求出失常范畴下的平均值,而后基于这个失常的平均值来计算该工夫线在 0 到最大稳定点的稳定方差(加上权重比例,越靠后的点权重比例越大),并且方差要除以该工夫线在失常范畴内的平均值的开方
问题 2:为什么要除以平均值的开方?
方差针对场景是:各个工夫线都在对立的平均值下,能够应用方差来断定各个工夫线的抖动状况。然而真实情况下,各个工夫线的平均值都不一样,这就会造成工夫线 1 在某个时刻从 1 抖动到 100,然而工夫线 2 在哪个时刻从 1000 抖动到 1200,显著工夫线 1 抖动更大,然而方差却是工夫线 2 比拟大。
所以对方差除以平均值,然而为什么又抉择了平均值的开方而不是平均值?
其实是否除以平均值,不论除不除都是 2 个极其,如果除以那么也会将一些抖动小的案例的稳定值进步了,所以应该是取一个衡量
关键点 2.2
对于每个工夫线的第一次稳定点、最大稳定点、最初一次稳定点减去失常范畴的平均值,而后再除以失常范畴的平均值求出 3 个稳定比率
步骤 3 对所有工夫曲线进行一些过滤
关键点 3.1
如果整体是向上稳定,那么对于上述 3 个稳定比率均 <0 则过滤掉
如果整体是向下稳定,那么对于上述 3 个稳定比率均 >0 则过滤掉
关键点 3.2
求出 top10 的稳定比率,而后在 top10 中去掉一个最大值和一个最小值,求出一个均匀稳定比率,再依照肯定比率比方 1 /10,算出一个基准稳定比率,对于小于该基准稳定比率的都能够过滤掉
同时判断 0 到第一个异样点之前的数据,都要小于第一个异样点,同时容忍肯定比例的大于第一个异样点,如果不合乎的话,都能够过滤掉
步骤 4 对过滤后的工夫曲线依照稳定值进行 KMeans 聚类
对于过滤后的所有工夫线依照稳定方差进行 KMeans 聚类,目前 N 的抉择是依据数据量的大小来进行抉择的
比方工夫线个数小于等于 10 则聚 3 类
步骤 5 从排名靠前的分类中挑选出计划,为每个计划计算计划分数
而后从对聚类靠前的工夫线提取公共特色,公共特色其实就是一个计划
将前 2 个聚类划分为一个集群 A,将所有聚类划分成一个集群 B
遍历上述所有计划,计算每个计划在上述集群 A、B 中的体现行为打分,打分项有如下几点:
- 每个计划在指标集群 A 中的占比
- 每个计划在指标集群 A 中满足的个数与在指标集群 B 中满足的个数占比
- 每个计划的大小分数,每个计划蕴含的内容越多,分数越小,也就是说偏向于用最简略的计划
- 每个计划,稳定比率的满足率,高于基准稳定率的组合满足,否则不满足,从而计算出该计划的一个满足率
综上所有项的乘积后果算进去的分数即为计划的分数
分数最高的计划即为咱们最终求解的计划
根因剖析流程
有了指标下钻剖析性能之后,咱们来看如何进行根因剖析:
- 如果 QPS 上涨然而成功率并未上涨,那么就须要上钻剖析,通过指标下钻剖析算法{appId}.soa_provider 剖析出 QPS 降落的 clientApp,持续递归去剖析这个 clientApp 的 SOA 服务情况
- 执行下钻剖析,对指标{appId}.soa_span 执行指标下钻剖析算法,失去异样抖动的 ezone、spanType 属性,比方 WG 机房的 DB 操作抖动了,比方 WG 机房的 RPC 操作抖动
-
而后到对应操作类型的指标中再次进行指标下钻剖析,比方 DB 操作抖动则剖析{appId}.db,比方 RPC 抖动则剖析{appId}.rpc,得出该操作下:
- 哪些入口受到该操作的稳定影响了?
- 哪些操作属性异样稳定了?
- 如果该操作是近程操作,还要持续深刻服务器端进行剖析
如果是 DB 操作,那么能够持续到近程服务器端再深入分析,比方咱们的近程服务器端是 DAL 中间件代理层,能够持续剖析它相干的指标,能够剖析出相干的抖动属性,比方
- 某个 table 的所有操作耗时抖动?
- 某条 sql 操作耗时抖动?
- 某台具体 DB 实例抖动?
- SQL 的停留时间 or 执行工夫抖动?
如果是 RPC 操作,那么能够持续到近程 appId 端再递归剖析
- 针对受影响的这些入口应用指标下钻剖析{appId}.soa_exception,哪些异样也抖动了(有些异样始终在抛,然而跟本次故障无关);
- 再次查看上述抖动的操作是否是由 GC 问题、容器问题、变更问题等引起的。比方对指标{appId}.soa_provider 进行指标下钻剖析,剖析其抖动是不是仅仅因为某个 hostName 导致的,如果是某个 hostName 导致,则能够持续剖析是不是 GC 抖动导致的
落地状况
阿里本地生存的根因剖析能力,1 个月内从产生根因剖析的想法到实现计划上线到生产(不包含指标下钻剖析的实现,这个是之前就曾经实现好的了),2 个月外在生产上试验和优化并推广开来,总共 3 个月内实现了从 0 到 1 并获得了如下问题
- 85 个具体记录的案例中精确定位 81 次,准确率 95%;
- 最高一天执行定位 700 屡次;
- 均匀定位耗时 1 秒;
- 具体的定位后果展现。
下图即为从 4 月 1 号到 6 月 23 号每天根因剖析次数的统计状况
咱们对定位准确性的要求如下:
- 要明确给出受到影响的入口服务有哪些;
- 每个受到影响的入口服务抖动的操作根因以及操作属性都要正确;
每个入口服务抖动的根因很可能不一样的,比方以后利用的 SOA1 是因为 Redis 操作抖动,以后利用的 SOA2 是因为近程 RPC 依赖的其余 SOA3 抖动导致,SOA3 是因为 Redis 操作抖动导致;
- 客户端操作耗时抖动到底是客户端起因还是服务器端起因要保障正确;
- 容器问题时,要保障不能定位到谬误的容器上。
准确率为什么这么高?
我认为次要是以下 3 个方面:
数据的残缺度
如果是基于采样链路去剖析,必然会存在因为漏采导致误判的问题。
咱们剖析的数据是全量链路数据转化成的指标数据,不存在采样的问题,同时在剖析时能够间接基于指标进行疾速剖析,长期查采样的形式剖析速度也是跟不上的。
建模的准确性
你的建模计划能答复出每个 SOA 服务抖动的根因别离是什么吗?
绝大部分的建模计划可能都给不出谨严的数据证实,以 DB 是根因为例,他们的建模可能是 SOA 服务是一个指标,DB 操作耗时是一个指标,2 者之间是没有任何关联的,没有数据关联你就给不出谨严的证实,即没法证实 DB 的这点抖动跟那个 SOA 服务之间到底有没有关联,而后就只能处于瞎揣测的状态,这里就必然存在误判的状况。
而咱们上述的建模是建设了相干的关联,咱们会统计每个入口后的每种操作的耗时,是能够给到谨严的数据证实。
异样断定的自适应性
比方 1 次 SOA 服务整体耗时 1s,近程调用 RPC1 耗时 200ms,近程调用 RPC2 耗时 500ms,到底是哪个 RPC 调用耗时抖动呢?耗时最长的吗?超过肯定阈值的 RPC 吗?
如果你有阈值、同环比的限度,那么准确率肯定不高的。咱们的指标下钻剖析在解决此类问题的时候,是通过当前情况下的稳定贡献度的形式来计算,即便你耗时比拟高,然而和之前失常状况稳定不大,那么就不是稳定的根因。
速度为什么这么快?
我认为次要是以下 2 方面的起因:
业内当先的时序数据库 LinDB
根因剖析须要对诸多指标的全量维度数据进行 group by 查问,因而背地就须要依附一个弱小的分布式时序数据库来提供实时的数据查问能力。
LinDB 时序数据库是咱们阿里本地生存监控零碎 E-Monitor 上一阶段的自研产物,在查问方面:
- 强悍的数据压缩:时序数据原始数据量和理论存储量之比达到 58:1,雷同 PageCache 的内存能够比别的零碎包容更多的数据;
- 高效的索引设计:索引的预过滤,革新版的 RoaringBitmap 之间的 and or 操作来进行高效的索引过滤;
- 单机内充沛并行化的查问机制:利用 akka 框架对整个查问流程异步化。
整体查问效率是 InfluxDB 的几倍到几百倍,具体见文章
分布式时序数据库 – LinDB https://zhuanlan.zhihu.com/p/35998778
指标下钻剖析算法的高效
- 咱们 不须要 每个工夫线都进行预测;
- 理论要计算的 计划个数十分之少;
- 计划算分上能够适应于任何加减乘除之类的指标计算上,比方根因定位中的均匀响应工夫 = 总工夫 / 总次数
SOA1 的均匀响应工夫 t1 和 SOA2 的均匀响应工夫 t2,SOA1 和 SOA2 的总体均匀响应工夫并不是 t1 和 t2 的算术平均而是 加权均匀,如果是加权均匀,那么久须要多存储一些额定的信息,并且须要进行额定的加权计算
理论案例
案例 1 -Redis 抖动
故障现场如下,某个外围利用的 SOA 服务抖动回升:
间接点击根因剖析,就能够剖析到如下后果
AppId1 的 SOA 服务在某个机房下抖动了
-
依赖的 AppId2 的 SOA 服务抖动
-
依赖的 AppId3 的 SOA 服务抖动
- 依赖的 AppId5 的本地解决和 Redis 操作耗时抖动
- 依赖的 AppId6 的本地解决和 Redis 操作耗时抖动
- 依赖的 AppId4 的本地解决和 Redis 操作耗时抖动
-
这里的本地解决蕴含获取 Redis 连贯对象 Jedis 的耗时,这一块没有耗时打点就导致划分到本地解决上了,后续会进行优化。这里没有给出具体的 Redis 集群或者机器跟咱们的理论状况无关,能够临时疏忽这一点。
点击下面的每个利用,上面的表格都会列出所点击利用的具体故障信息
- 受影响的 SOA 服务有哪些,比方 AppId1 的 3 个 SOA 服务受到影响;
- 每个 SOA 服务抖动的根因,比方 AppId1 的 3 个 SOA 服务都受到 AppId2 的 1 个 SOA 服务的抖动影响;
- 表格中每一个链接都能够跳转到对应的 指标曲线监控面板 上。
再比方点击 AppId4,显示如下:
AppId4 的 1 个 SOA 办法抖动
- 该办法的本地解决抖动(理论是获取 Redis 连贯对象 Jedis 的耗时抖动);
- 该办法依赖的 Redis 操作抖动;
- 该办法抛出 Redis 连贯异样;
案例 2 -DB 实例抖动
故障现场如下,某个外围利用的 SOA 服务抖动回升
点击根因剖析,就能够帮你剖析到如下后果
AppId1 的 SOA 服务在某个机房下抖动了
-
依赖的 AppId2 的 SOA 服务抖动
- 依赖的 DB 服务抖动
-
依赖的 AppId3 的 SOA 服务抖动
-
依赖的 AppId2 的 SOA 服务抖动
- 依赖的 DB 服务抖动
-
点击 AppId2,能够看下详细情况,如下所示:
从表格中就能够看到,根因是 DB 的一台实例抖动导致这个 dal group 所有操作抖动。
案例 3 - 上钻剖析案例
故障现场如下,某个外围利用的 SOA 服务调用量降落,成功率并未变动
点击根因剖析,就能够帮你剖析到如下后果
就是这个 appId 的上游的上游操作了相干的变更导致调用量上涨
案例 4 - 独特依赖抖动
故障现场如下,某个外围利用的 SOA 服务相应工夫抖动
点击根因剖析,就能够帮你剖析到如下后果
均是因为依赖图中标出的 appId1 而导致抖动
案例 5 - 整体机房网络抖动
故障现场如下,某个外围利用 SOA 服务相应工夫抖动、成功率抖动
点击根因剖析,就能够帮你剖析到如下后果
链路中的各个利用 SOA 调用均呈现 TCP 重传过高的问题,点击每个利用的详情,查看 TCP 重传的指标曲线如下
的确是 TCP 重传过高
案例 6 - 单容器抖动
故障现场如下,某个外围利用 SOA 服务相应工夫抖动
点击根因剖析,能够剖析到如下后果
以后 appId 近程依赖的另一个 appId 的容器抖动导致的
作者
李刚 ,网名乒乓狂魔,饿了么监控组研发专家,饿了么外部时序数据库 LinDB 的我的项目负责人,饿了么根因剖析我的项目负责人,目前致力于监控的智能剖析畛域以及下一代全景监控的体系化构建;
林滨(予谱),饿了么监控组前端工程师,现负责一站式监控零碎 EMonitor 的前端开发,旨在将繁冗的数据以高可视化输入。