cjuexuan / mynote

237 stars 34 forks source link

spoor(通用的监控系统)设计的一些感悟 #52

Open cjuexuan opened 6 years ago

cjuexuan commented 6 years ago

spoor概览

最开始立项做spoor的时候,我们仅仅想做的是对yarn上的任务的状态进行监控,当发生故障的时候时候能及时通知到开发。后来,我们想着去适配spark的metric system,采集更多任务的指标,让定位问题和性能调优的成本更低。在开发的过程中,我们发现这可以做的很通用,可以把我们所有的大数据组件的metric info都接入进来,所以从需求本身推动我们不断的迭代这个系统

整体架构

架构

这是我们目前的整体架构,最左边是客户端(agent和application),我们会在每个application中起一个我们的channel,发送到我们mq(目前是基于kafka实现的),对于进入mq的数据,我们会有两个流程序处理它,首先,我们先说上面的流程序,这个流程序主要用来做数据的预聚合以及做一个存储的拆分,接下来就是按照不同的metric的类型(gauge/delta),以及不同的聚合周期(分钟,10分钟,1小时),以及当天的日期写到不同的es的index,最终我们的看板按照用户指定的起始时间动态的路由到对应的index中去,按照选中的条件进行筛选过滤(没有聚合),选到一个比较小的数据集(几十万)之后再本地通过多线程完成最终的聚合。说完了第一个流,我们再说第二个流程序,第二个流程序主要用来做告警的,我们这里并没有选择常规的基于db的pull模式去做,而是选择通过streaming的push模式做告警,这里有一个后台程序和我们的流程序建立一个长连接,后台负责对规则的crud,并且在变更后下发到流程序,另外就是消费由流程序产生的告警事件,做告警的合并,并且最终通过钉钉,邮件,短信等方式通知到对应的开发

细节

client端

  1. java application需要采集的信息主要是jvm的heap使用,direct memory的使用,新生代和老年代的使用,gc的情况,线程池的情况,这些都可以通过MXBean获取到,其中一个比较需要值得思考的地方是gc的情况,具体可以看之前写过的 es gc的启发
  2. spark除了上述信息外,我们还需将spark现有的metric system产生的信息做一个适配,适配到我们系统中来,这部分的工作可以按照spark的规范实现一个sink,通过submit的时候框架级别在spark的conf中加入metric相关的配置就可以做到业务代码无入侵的方式去启动你相关的sink,至于你自己监控系统需要的一些配置,我们建议通过dist file的方式传过去,这部分的逻辑都可以用公司封装的提交框架解决掉。spark的指标通常推过来的时候,指标的名字类似application_1516850428106_6144.driver.spoor.example.StreamingMetrics.streaming.receivers或者application_1493749888910_1641.{executorId}.executor.filesystem.hdfs.largeRead_ops,由于每个周期都要有指标产生,而且我们对名字做适配,并且将executor id之类的信息作为我们的timeseries上的标签,所以如果事先知道executor id的话,我们就不需要用正则之类的东西去处理指标名了,我们可以直接按照字符串长度进行截取,对于一个调用频率很高的方法,这个优化是值得的,也是有必要的,对于具体的executor id的获取,可以参考我之前写的 spark on yarn如何获得applcationID和executorId
  3. 另外其他的一些组件也提供了丰富的接口,比如hadoop生态系统的http的jmx,hbase的serverload,以及es的clusterState,都可以通过agent获取一些相关的信息,并且发送到我们系统中来

transform流程序

  1. transform主要做预聚合和数据的拆分,由于监控指标的量非常的大,动辄一天几十亿数据,不做拆分的话,db的压力会比较大,另外用户查看仪表盘的时候,通过不一定只会看当天数据,有可能需要浏览的是过去七天,半个月甚至一个月的数据。考虑到存储成本,我们并不会给用户保留那么多的原始数据,一个点的指标,它同时属于不同聚合周期(可以同时被分钟级别聚合和小时级别聚合),所以就需要事先做一个预聚合,这样按照用户选中的时间范围,我们可以动态的路由到我们不同的聚合周期中去。拆分过程中我们建议同时带上metric的类型和value的类型,这样类似spark的分区字段的方式,第一可以让我们少存两个字段,另外在处理value的时候我们按照value type拆分之后也不需要进行强转。最后我们建议按照天级别做归档,这样删数据会简单一点,对于不同的聚合周期可以设置不同的ttl :),我们目前用es的curator管理我们的索引,所以我们一个索引的命名如下 spoor_gauge_double_10minute_2018_03_17

dashboard

  1. dashboard程序主要用来对用户进行展示,我们通常筛选的条件是时间,用户的metric,用户的domain,以及用户的一些标签,用来分组聚合的条件是标签,这些标签是一个tuple的数组。大概查询的case会是 取spoorTransform这个domain下,从昨天到今天的jvm heap使用情况的平均值,按照executorId进行分组,并且筛选出executorId这种分组模式下heap的和最大的两个executor ,考虑到从天级别的数据(亿级别)中筛选出十万数据还是比较快的(几百ms),另外对几十万数据用多线程进行聚合也比较快,所以我们查询es的时候只用了es的过滤,并没有用es的聚合。

看板

judge

  1. 为什么我们没用基于db的pull模式做告警,而采用push模式呢,主要考虑到鸡蛋不要放在一个篮子里,如果基于db的话,万一db出问题或者transform出问题,我们的告警都会有影响。另外这样也减轻了db的压力
  2. 告警合并和收敛,至少要做到以下几点: 按照domain做收敛,比如一段时间内同时有很多cpu load和网络告警,太多的信息对开发排查问题其实是干扰,所以需要合并。另外通常收到信息到打开电脑需要两三分钟,所以需要做告警的频控

效果展示

os

hdfs

dingTalk

sms

overTimeAlert