当前项目中是将Spark任务提交到Yarn上运行的,但是发现了一个问题,这个任务在Yarn占用的内存远超程序中所申请的内存两。例如我现在有一个yarn-client模式运行任务,向Yarn申请两个Executor,每个Executor使用1G内存,Driver内存配置的是5G,发现Yarn给程序分配的内存如下:

yarn的FederationStateStore找不到任务id yarn任务申请不到资源_应用程序

    Yarn启动了3个Container,内存给了4.125G…所以研究下,Yarn是怎么给Spark程序分配内存的。

 

研究下内存分配:

    之前《SparkOnYarn与StandAlone模式的区别》文章中分析过,Spark On Yarn程序启动的时候会创建Client,Client会检查是否有足够的资源启动AM,足够就启动AM,由AM向RM申请资源启动Executor,来看下检查资源是否足够的方法:

    方法里面检查了两部分的资源,一部分是Executor的内存资源,另外一部分是AM的内存资源,两部分资源都有Overhead额外的内存资源...

yarn的FederationStateStore找不到任务id yarn任务申请不到资源_内存分配_02

AM总内存资源:

yarn的FederationStateStore找不到任务id yarn任务申请不到资源_应用程序_03

从代码中可以看出,AM在ClusterMode下获取的就是Driver端配置的资源,Cores就是Driver端配置的核数;Client模式下获取的就是AM_MEMORY,即程序给AM配置的资源(我们使用的CDH版本的Hadoop默认是1G)。

Overhead是额外申请的内存量,如果程序中有过配置,那么使用程序中设置的内存;否则会默认使用一个公式计算内存量:

默认情况下为:math.max(0.1 * amMemory,384M),即默认最少会额外申请384M内存。

 

Executor总内存资源:

yarn的FederationStateStore找不到任务id yarn任务申请不到资源_应用程序_04

Executor额外内存申请的方式和AM额外内存申请的方式一样,使用的是相同的公式计算的,默认情况下也是最少会申请384M额外内存。

 

所以可以看出来,Spark任务提交到Yarn上要申请四部分的内存:AM+AMOverhead+Executor+ExecutroOverhead,其中AM在Cluster其实是Driver设置的内存,在Client模式才是真正AM设置的内存。(Cluster模式下还没有验证过,等后续有时间了再验证下,先把结论放在这里,不对再改吧)

 

为什么会有3个container?

不管CLient或CLuster模式下,AM都会占用一个Container来运行;程序申请了两个Executor,占用了两个Container,加上AM正好三个Container。在当前我们使用的CDH版本中,Container默认有1G内存,1个cpu核。

 

Yarn内存分配规整化:

为了易于管理资源和调度资源,Yarn内置了资源规整化算法,它规定了最小可申请资源量、最大可申请资源量和资源规整化因子。

如果应用程序申请的资源量小于最小可申请资源量,则YARN会将其大小改为最小可申请量;如果应用程序申请的资源量大于最大可申请资源量,则会抛出异常,无法申请成功;规整化因子是用来规整化应用程序资源的,应用程序申请的资源如果不是该因子的整数倍,则将被修改为资源规整化因子的最小的整数倍。

    例如配置文件中设置成128M,如果Executor + ExecutorOver 加起来是126M,那么就会规整化到128M上;如果加起来是240M,那么就会规整化到256M上。配置文件中对应的配置项为:yarn.scheduler.increment-allocation-mb