一、 默认情况
默认情况下,每个NdeManager自己的资源是在yarn-site.xml中配置的。如下面的配置时32个cpu cores,100G内存。现在集群中服务器cpu的线程数有32,40,48等几类。
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>32</value>
</property>
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>102400</value>
<!--<value>126976</value>-->
</property>
以32来计算,如果我们的MapReduce作业的每个Mapper任务占用<1 cores,2GB>、每个Reduce<1 cores,4GB>来计算。当一个大的MapReduce任务提交的时候(如10万个Mapper,5000个 Reducer),由于现在配置Mapper任务都运行结束,再运行Reducer任务。当任务提交时,在一台计算机上回运行32个Mapper任务(因为任务大,并且这台服务器上有空闲资源,所以会分配)。
由于服务器的性能在长时间运行之后性能变的不一致,并且这些任务都是IO密集型的,而我们的服务器只有7块硬盘。会变成同时有32个程序同时读写本地硬盘(这还不包括32个程序的log输出,还有拷贝运行程序的本地化,Reducer程序的并发远程读后写本地硬盘等)。随着时间的推移,本地硬盘的碎片化。有些服务器运行10几个程序就会导致可用CPU 为0,Load值非常高(大于cpu核心的两倍)。但是资源调度器还是往这台服务器上分配任务,因为CPU和内存还有空闲呢。导致以下结果:
1. 本来正常几十秒结束的MapReduce程序超过10分钟还没有运行完。
2. 对于A ppMaster来说,网络接收到的一些事件不能快速处理,导致事件堆积,如已经结束的任务,由于没有处理这个事件,推测执行程序会认为没有结束,而再在其他服务器上重复调度。
3. 对于Spark来说,由于框架开启的线程较多,不能有效处理导致各种问题。因为所有的异步操作都是要求在规定的时间内返回结果的,否则认为远程的程序宕了。
NodeManager默认的healthcheck程序
NodeManager的默认healthcheck程序,可以配置一个shell程序,默认每五分钟执行一次。当输出以“OK”开头的字符串,代表正常; 当输出以“ERROR”开头的字符串,则代表异常。我们的判断,可以依据cpu的利用率或者load值。当异常时,NodeManager会把unheallthy的状态发送给ResourceManager,并把本机的所有Container杀死。ResourceManager收到此信息,会把此节点放到unhealthy列表,并且以后不会分配任何任务到这台服务器。一直到healthcheck程序检查返回OK。
这种方式的缺点: 这种方式会造成服务器抖动,并且导致任务失败。
理由如下:
当大任务提交时,如一个任务10w个Mapper任务,我们有600多台服务器,平均每个服务器分配170个任务。当然,由于我们服务器资源不够,所以很多Mapper任务会排队。但是这些服务器的会一下分配30多个任务。导致CPU利用率100% 。然后NodeManager health check程序检查到当前负载大,然后把unheallthy的状态发送给ResourceManager,并把本机的所有Container杀死。下次检查时,由于没有任何任务了,这时的负载降低了,再给ResourceManager发送healthy的状态。这时如果大任务没有结束,又会一下子分配30多个任务,再一次导致CPU利用率100% 。然后NodeManager health check程序又检查到当前负载大,然后把unheallthy的状态发送给ResourceManager,并把本机的所有Container杀死。如此恶性循环,以至于不能完成任何任务。
结果:
1. 如果AM运行在这台服务器上,会导致整个Application重新运行。
2. 如果连续4次AM运行的Container进入unhealthy状态,则job失败。
3. 如果连续4次同一任务的Container杀死4次,导致这个任务失败,也会导致作业失败。
资源动态调整技术
如果某个NodeManager进入unhealthy状态,说明本NodeManager承诺ResourceManager可以分配的资源太多了,如果能在NodeManager检测到负载太高时,把承诺的cpu vcores的数量降低1,在重新启动下NodeManager以便加载配置,这比单纯的进入unhealthy有用,因为cpu vcores降低,代表可同时分配的任务数降低。
现在NodeManager采用这种策略的缺点。
1. 当系统升级等原因,所有NodeManager的vcores为初始值时。当大任务到来时,会出现很多NodeManager同时进入unhealthy状态。会几乎同时降低vcores,然后重启。也会导致上一方法的结果。即:
1. 如果AM运行在这台服务器上,会导致整个Application重新运行。
2. 如果连续4次AM运行的Container进入unhealthy状态,则job失败。
3. 如果连续4次同一任务的Container杀死4次,导致这个任务失败,也会导致作业失败。
下一步可能采取的方法
- 近可能少的次数把vcore的数量调整到合适的值。如果40core的服务器,最终调整到20合适,怎么用较小的次数调整到20。我们在做vcore数量减一的操作时,没有考虑当时load的值到底超过多少? 如超过cpu核心的3倍时,是否可以直接减半? 如果不超过3倍时,如果当前设置的vcore数量大于15,直接减掉三分之一? 其他情况直接减2。 现在的方法需要20次重启NodeManager,整个集群的NodeManager需要12000次才能达到稳定状态。
- 如果cpu的可用率不是接近0的话,可以加上一些随机化方法,并不是大任务一来,很多NodeManager同时重新启动。而是让NodeManager保持,也不进入unhealthy状态。如我们规定NodeManager离上次重新启动时间小于三小时不能重新启动。大于三小时的话,选择一个概率为100分之一的随机数。因为NodeManager每5分钟执行一次,大任务也并不是一直都存在。假如集群一直空闲,现在来了一个大任务。那么基本所有的NodeManager离上次重启都大于3小时,假如这个大任务运行时间为30分钟,那么这30分钟,每个NodeManager会执行health check 6次。每个NodeManager在此大任务执行请假重启的概率是6%。整个集群(600台)重启的NodeManager的数量大约是42台(6%)。
缺点:这样会使整个集群花费更长的时间达到最优的状态。