RM挂掉和资源打不满的排查
一.RM挂掉的原因的分析及其解决方案
3月1号,凌晨 04:17分RsourceManager进程挂掉,YARN发生了主从切换,查看RsourceManager进程在挂掉时输出的JVM相关的错误文件hs_err_pid31250.log,可以看到RsourceManager进程是因为内存溢出挂掉的,
理论上,RsourceManager里面保存的基本上都是临时变量,不像HDFS的NameNode那样里面保存大量的永久的对象,不应该会因为内存溢出挂掉,通过分析日志发现,里面有大量的事件堆积,
通过分析其dump文件发现内存泄露严重
这个和之前在大集群RM出现的问题很像,当时的解决方案是关闭了Resourcemanager发送metrics信息到timelineserver的开关,但是在5K集群中tez的作业依赖这些信息,不能关闭发送的事件,另外由于AsyncDispatcher类是一个统一的事件分发中心,原始代码中其只输出了积压的事件数量,并不能确定此次积压的事件仍然是发送到timelineserver的事件,因此首先需要确定积压的是不是发送timelineserver的事件?如何解决等问题。
1.确定发送慢的是什么类型的事件?
通过修改代码,在代码中为AsyncDispatcher分发的线程添加了名称用于调试(YARN-6015),之后在代码中添加输出积压事件数量的同时也输出积压的事件的线程信息的日志,确定了积压的事件确实为发送给timelineserver的事件。
通过查看dump文件中泄露的对象发现其中大量存在的是发送给timelineserver的ContainerCreatedEvent和ContainerFinishedEvent事件。
2.如何解决?
通过分析代码,发现可以通过参数(yarn.resourcemanager.system-metrics-publisher.dispatcher.pool-size)增大发送事件的并行量,因此将参数的值由50改成了200,测试发现,事件积压仍然还是很严重。
再次通过分析代码,发现其发送事件到timelineserver是逐条的使用http的post的方式发送,这是个很严重的问题,因为http协议需要每次都需要重新建立连接,需要经过三次握手四次挥手,这是一个非常消耗时间的地方,正常的话大家都是使用rpc通过长连接的方式规避掉每次的都要建立连接这种耗时的操作,另外其采用的是逐条发送的,理论上性能会非常的差。
通过上面的分析提出了两种改进思路,
1)改成RPC的方式,这个需要timelineserver端支持,并且改的比较大,需要的时间比较长。
2)将追条的发送改成批量的发送,这种方式简单,但是需要在长时间没有新来事件时,及时的排空队列中的事件,这个需要控制好锁。
通过分析比较,最终选择了先使用第2种方式,进行代码的修改,修改完代码后,基于YARN-2556增加了个测试类,用于分析发送timelineserver时,使用多少线程进行并发以及发送时采用多少一批进行发送能够获得最大的QPS,通过测试发现,默认的情况下10个线程,逐条发送,QPS只有34/s,猜测出来最优的方式是采用10个线程的时候,每批发送1000条事件,性能最高,QPS能达到1.8W/s,性能提升 500多倍,上线后发现事件只会少量的积压(此处少量的积压是由于RM在使用线程向timelineserver发送事件的数据时,是按照application id进行分发给各个线程的,这就导致了如果某几个大作业都正好分发给了同一发送的线程,该线程会出现短暂的事件积压,但是由于发送是异步的,因此对调度无影响),很快就能消费掉,不会再发生OOM了,至此OOM的问题解决。
不过通过上面的测试,也说明了一个问题,之前的发送timelineserver的功能虽然开启了,但是未能够正常的使用,追查原因,猜测可能与在发送timelineserver前会先执行写数据到HDFS的操作,由于其只往一个目录中写,但是HDFS中对应的目录中已经超过了单目录下最大子文件或目录数量的限制(104W)的限制,导致在写HDFS的时候报错,日志中有相关的错误,从而导致其下发送timelineserver的代码未能正常的执行。
通过上面的分析,之前应该是没有成功向timelineserver发送事件的,随着扩容,ResourceManager出现资源打不满的情况,为了排除影响,又暂时的将发送timelineserver的功能关闭了,不过这是一个很好的解决了RM发送数据到timelineserver的瓶颈的方法,性能提升显著,准备有时间提交给社区。
二、RM资源打不满的原因排查
随着最近2次集群的扩容,RM的压力越来越大,最终导致了RM出现了资源长时间不能打满的情况
通过图中可以看出,13号和14号作业完成情况逐渐的变慢,14号周六早上接到作业运行缓慢的电话,发现作业完成情况比前天慢了将近5K个,通过监控发现昨天夜里集群一直处于没有打满的状态,大部分时候集群只利用了60%,之前遇到这种情况,通过切换RM的主从的方式可以让集群很快打满,因此当即切换了RM的主从,资源很快被打满,RM的HA主从的切换对用户来说基本无感知的。
1. 分析原因
通过分析日志发现,没有相关的重要的报错信息,也没有事件的积压,分析dump文件没有内存泄露,没有线程死锁等相关的信息,因此猜测可能是RM的调度性能到达了瓶颈了,添加YARN-8213用于检测容量调度的性能指标,第二天观察指标发现如下的问题
图中可以发现node update事件处理的速度越来越慢,container创建的速度也很慢,但是container完成的速度非常的快,通过分析节点更新事件的源码,发现,节点更新事件中需要对每个节点循环释放container的操作,也就是说有大量的finished的container时,会导致该事件的处理速度越来越慢。
因为按照理论上来说,NodeUpdate事件处理速度即便慢点,也不应该一直慢,于是猜测一直慢是由于处理重复的container的释放的事件导致的,通过分析代码找到了怀疑的地方,查找相关的issue,发现了这个YARN-4862这个bug,重复的container状态信息没有删除,导致越积累越多,处理速度就变得很慢了。
于是修复在RM1上进行上线,凌晨的时候起来实时的观察结果,发现,资源仍然不能打满,但是此时切换为RM2为主的时候,资源很快就能打满,但是在此切换为RM1为主的时候,RM1仍然不能将资源打满,这样重复了几次,发现先最终的结果RM1都不能将资源打满,即便有时打满,也很快就不行了,RM2都能够快速的打满,这让人很是郁闷,查找这两台服务器的区别,发现唯一的一点区别是 RM1 上运行了一个占用了30G内存的agent服务,RM2上没有,而我们给RM进程分配的内存是220G,加上agent使用的30G,也只有250G,几乎将256G的内存暂满,但是由于我们的RM进程并没有将220G的内存全部使用完,
通过分析GC情况,RM1的GC频率更高些,RM2的频率更低些,只有一点点的差异,当时觉得可能是这个方面的原因的概率很小,但是也找不到别的原因了,除非是这两台服务器的性能有些许的差异,抱着试一试的心态,还是让同事帮忙把RM1上的agent移动到了RM2,凌晨发现RM2的资源也不能打满了,但是切换到RM1后,RM1的资源又能打满了,通过查找相关的RM瓶颈的文章发现,RM在达到性能瓶颈后,处理能力会逐渐的下将,而不是保持最大的瓶颈处的性能,因此猜测RM处在性能瓶颈的边缘,任何一点的波动都会影响到RM的性能,通过分析各个指标,以及在晚上集群闲的时候,进行的压测,发现,夜里打不满的时候的情况和压测出来的瓶颈展现个各种指标相似,再加上之前一直都知道RM在5K节点左右会出现性能瓶颈,但是之前的服务器都是128G内存的,我们现在的集群虽然只有3294台,但是有很多节点是256G和192G内存的,如果都按照128G的内存算的话,也已经达到了5K节点了,因此确定是到RM瓶颈了。
2.临时解决方式
为了使RM能先暂时的跨过瓶颈,采取了如下的临时措施:
①我们查找相关的参数,尝试挖掘最后一丝的性能,发现可以再尝试提高RM处理调度和线程的数量,看看是否能够提升一下性能。
yarn.resourcemanager.scheduler.client.thread-count
200
用于通过调度器接口处理请求的线程数量,默认值: 50
yarn.resourcemanager.client.thread-count
200
用于处理应用程序管理请求的线程数量,默认值: 50
yarn.resourcemanager.amlauncher.thread-count
200
yarn.resourcemanager.resource-tracker.client.thread-count
200
②关闭了不怎么使用的RM的审计日志输出,暂时调整RM的日志级别为WARN。
执行上面两个操作后,集群最近两天表现平稳,作业完成速度最高时比之前快了1千多个作业。
3.最终解决方案
1)由于将YANR升级到 2.9 版本已经调研很久了,现在处于最后的测试阶段,因此需要加快测试的进度,尽快上线,看看RM的性能是否有所提高。
2)分析容量调度代码的各个地方的性能,找到RM调度耗时的地方,进行优化,提升调度效率。
3)删除掉不必要的事件,提升集群的调度效率。(这个参考的今日头条发的文章中提到的地方,https://www.ctolib.com/mip/topics-136711.html?from=singlemessage)。