前几天有好几个朋友问我关于spark作业分配资源的问题:即在提交作业的时候,不知道该分配多少资源比较好?我的回答是靠经验,仔细想想靠经验这等于不是没说吗,总有一些方法论或者思路的吧。所以就有了这篇文章,下笔的时候着实是不知道该怎么写,所以在网上搜索了一下,看看大佬们是怎么回答的。赶巧了不是,还真发现3年前就有人问过这个问题。
看了下评论,我感觉我能看懂,但不知道朋友们是否能看懂,所以我想还是要再详细啰嗦一下吧
首先呢,spark官网给我们提供了一些硬件层面的建议,先上链接https://spark.apache.org/docs/latest/hardware-provisioning.html。
但是这个文章并没有很细粒度介绍应用级别该如何分配资源。不过涉及到的几个方向可以参考一下,比如内存,CPU。
首先来复习一下spark涉及到这两块部分参数如下:
--num-executors/(SparkSQL中是spark.executor.instances 参数) | 该参数代表了作业使用的总共executor个数 |
--driver-memory/(SparkSQL配置的是spark.driver.memory参数) | 该参数代表了Driver端所消耗的内存,通常Driver端不会消耗太多资源,所以不需要给太多资源。 |
--executor-memory(SparkSQL配置的是spark.executor.memory参数) | 该参数代表了每个executor所占用的内存 |
--executor-cores/(SparkSQL配置的是spark.executor.cores参数) | 该参数代表了一个executor中能够并行执行的task数(注意并行和并发的区别!) |
通常一个Spark作业所消耗的总内存数>=(spark.executor.instances)*(spark.exector.memory);
所占用的CPU个数>=(spark.executor.instances)*(spark.exector.cores)。
这里之所以要用大于,是因为AppMaster也要占用资源的!扯到AppMaster,那需要再复习一下Yarn部分的两个参数:
yarn.nodemanager.resource.memory-mb | 该参数表示的是nm可供Yarn使用最大可用内存 |
yarn.nodemanager.resource.cpu-vcores | 该参数表示的是nm可供yarn使用的最大虚拟cpu数量 |
一般在实际生产环境中,yarn可分配的总资源肯定是已经分配好了的。甚至是会有单独的spark作业队列(这里也要看yarn的调度模式,是fair,还是fifo,还是capacity)。
我们这里先举个例子来说明下:假设我们有一个裸集群,共有6个节点,每个节点都是16c 64g的配置(真实情况下每个节点的配置可能是参差不齐的)。
但是我们不可能把所有的资源全分配到yarn上,还是要留一部分作为系统运转的,那么每个节点留1C 1G 好了,采取极限的方式。最后每个节点可供yarn分配的资源如下:
yarn.nodemanager.resource.memory-mb=63g
yarn.nodemanager.resource.cpu-vcores=15
那么总资源就是all_memory=63*6=378g;all_cpu=6*15=90c
我们假设每个Executor并行度为5,那么就需要90/5=18个executor,但是需要留一个给AppMaster,所以–num-exectors=17;刚才算下来总共需要18个Executor,一共6个节点,平均每个节点是启动3个exector,一个节点是63g,那么每个executor可占用内存就是21g,请注意哦:每个executor并不是把21g全部给作业用,还要留一部分资源用来存储栈,buffer之类的开销以此来保证稳定性,也就是常说的堆外内存,通过spark.executor.memoryOverhead参数来设置这块的开销,其值=max(384mb,0.1*executorMemory)。因此这部分堆外内存=max(384mb,0.1*21)~=2g。那么堆内内存=21-2=19g。
这里用表格来汇总一下:
也就是说在提交作业的时候,其参数配置值不能大于以下值(这里的例子是假设把资源全给你的任务去用!):
--executor-cores / spark.executor.cores = 5--executor-memory / spark.executor.memory = 19--num-executors / spark.executor.instances = 17
节点数 | 内存/每节点 | CPU/每节点 | Yarn可分配最大内存数 | Yarn可分配最大CPU | 假设每个executor并行度数 | 总executor数 | 实际干活的executor数 | 每个节点可启动的executor数 | 每个executor占用的内存 | 堆外内存 | 实际干活所用到的内存 |
6 | 64g | 16C | 63g*6=378g | 15C*6=90C | 5 | 90C/5=18 | 18-1=17 | 18/6=3 | 63g/3=21g | max(384mb,0.1*21g)=2g | 21g-2g=19g |
9 | 90c/9=10 | 10-1=9 | 10/6~=2 | 63g/~=2~=37g | max(384mb,0.1*37g)=3g | 37g-3=34g |
可能有朋友会困惑,为什仫每个executor并行度设置为5,当然也可以调大。如果调大每个executor的并行度,也相当于是提高了并行能力,这样的话就减少了task来回轮询的次数,比如你有100个task,每个executor执行20个task,那么要轮询5次;如果每个executor设置50个task,那么要轮询2次;这样一来就会增大每个executor所持有的内存。
其实可以按照前面论坛里提到的一种评估方式就是按照task数量来倒推,官方建议一个cpu core是处理2~3个task。
那么假设有100个task,最多需要50c,还是按照上面的例子,这个时候就要看你是采取每个节点均摊的方式呢,还是某几个节点集中处理。采取何种方式还要结合你实际处理的数据量和计算复杂逻辑(因为这里还要思考网络消耗和本地化计算,每个节点的负载情况等等因素),当然通常情况下是采取均摊的方式,那么每个节点大概会使用8c。那么每个节点启动几个executor呢?是启动一个executor,还是2个executor,每个executor,每个executor大概占用多大内存呢?这个时候就要看你的代码逻辑是计算多一些还是缓存多一些。这里就涉及到内存模型的东西了,不知道内存模型的朋友再复习一下我原来写的文章:Tungsten On Spark-内存模型设计
尽管以上资源分配都已经评估好了,但并不代表后续不需要优化了,不然网上怎么会有那么多优化文章,同时还要注意团队协作资源,不能把所有的资源都给你用,不然别人还怎么干活?
所以还要在你评估的资源上再缩减一部分,这样一来一回可能需要不断的调整,这也是为什仫很多人问到怎么分配资源的时候,都会说一句靠经验!
小编觉得这玩意真的是没有公式可套用的。
如果你有更好的分配资源的方法论或者有哪里写的不对的地方,欢迎大佬联系我分享给其他小伙伴们。
如果你有其他困扰的问题,欢迎骚扰我~
参考:
-
spark官网
-
https://bbs.csdn.net/topics/392153088