1. 背景
在https://blog.51cto.com/u_15327484/8260931文章中,介绍了通过抓取Yarn web页面的方式获取task级别的进度,并且将task进度通过纵向的历史进度进行预测对比检查作业进度是否符合预期。
对于task级别的进度,不仅可以用作纵向对比,还可以进行横向对比。当多次发生某个节点上执行的task比其他节点节点上执行的task时间高时,就认为该节点是慢节点,应该经过一系列硬件检查以及环境信息检查,甚至下线。
2. 异常节点比较算法
由于map下所有task都属于同一类型,它们执行相同的逻辑,正常情况下map下的所有tasks应到同时执行完,如果有task执行时间明显高于其它tasks,那么表明该task未在正常时间内执行完毕,该task的状态是异常的,因此可以统计异常的task数量;同理可以检测reduce下的异常task数量。最终统计map类型tasks和reduce类型tasks的异常总数,最终获得Job的tasks的异常tasks占比。
异常task检测方法:箱形图。其在实践上要优于标注差算法。根据输入的数组计算分位点,计算出异常边界值:mild outlier和extreme outlier。在mild outlier外的点就是异常值,箱形图如下所示:
正态分布样本对应到箱形图的区间如下:
由于tasks的progress的取值空间是[0.0, 100.0],它不是标准正态分布,因此需要对箱形图算法进行调整,调整如下:
- IQR系数从1.5调节至0.5,减小正常样本点的取值范围。
- 当IRQ为0时,表示大部分样本值一致,需要增加一个缓冲项,此时设置IQR=0.5*median。
3. 异常节点计算
基于定时模块采样后,每次采样得到的tasks信息都需要进行比较,最后查看是否有慢节点出现,其逻辑如下:
- 将tasks的elapsed time作为箱形图输入,和正常的task的elapsed time进行比较,计算异常tasks。每个task可能有多次attempt,task的elapsed time是所有attempt的elapsed time之和。因此需要获取tasks中是否有多个attempt信息,获取其中elapsed time最大的attempt。
- 获取这些异常tasks的attempt信息,这些attempt elapsed time输入到箱型图中,和正常attempt elapsed time进行比较。如果间隔超过15min,说明task异常。
- 如果task异常,将当前omega JobId写入redis中,如果异常的作业数超过10个,即10个作业跑在该节点上都发生了异常,该机器为慢节点。
在设计慢节点报警时,使用Redis的Set作为存储结构,将异常节点的主机名作为Key,将JobId作为field,将时间戳作为value。
Redis更新策略:每次job发生超时异常,将最新的timestamp更新对应的value中。保证一个作业在一个DN上即使发生了多次延迟,也只算做一次异常。这样避免因为数据倾斜导致错误判断为慢节点。
Redis过期策略:
- 应用比较每个jobId的延时时间,只当异常发生在最近一小时内,才真正统计在节点异常总数中。
- 由于会一直向上述hash存储结构写jobId,因此每个HostName包含的数据会无限膨胀。为了避免这种情况,在hostName后面加上Date时间,例如
xxx-dn01-cluster1_2021_8_25
,只统计当天异常节点和作业情况,不考虑历史数据,因此过往数据可以通过expireAt()设置过期时间。