Spark 2.4.8 Job调度
概览
Spark 有多种工具为资源调度在复杂计算. 首先,回顾下,在集群模式概念中,每个 Spark application(SparkContext实例)运行一个独立一组executor 进程.群集管理 Spark运行的集群管理器提供工具为了调度跨应用. 其次,每个Spark application, 多个"jobs"(spark actions)可以并行运行,如果他们提交到不同 线程. 这里常见的,如果你的应用通过网络请求提供服务.Spark包含公平调度来调度资源在每个SparkContext.
跨应用程序调度
当运行在群集上,每个 Spark application获得独立的一组executor JVMs,它只运行任务和为application存储数据.如果多个用户需要共享你的集群,有不同的选项来管理分配,这取决于集群管理器.
最简单的选项,可以用在所有集群管理器,是静态分区资源.用这种方法,每个应用给一个最大的资源,它可以使用并在整个运行阶段一直占用.这个方法被用在 Spark’s standalone 和YARN 模式,也在 coarse-grained Mesos模式里.资源配置可以通过下边,是根据集群类型:
- Standalone mode: 默认,应用提交到Standalone mode集群会运行先进先出顺序,并且 每个应用会试着使用所有可用节点. 你可以限制应用中node数量使用
spark.cores.max
配置合适值,或者改变应用默认值没有 设置这个 配置通过spark.deploy.defaultCores
.最后,另外控制核数量,每个application的spark.executor.memory
设置控制使用的内存. - Mesos: 使用静态分区在Mesos中,设置
spark.mesos.coarse
配置属性为true,并且可选项spark.mesos.coarse
来限制每个 application共享资源在standalone mode.你也应该设置spark.executor.memory
来控制executor内存. - Yarn: 对于Spark YARN 客户端–num-executors选项控制多少个executors 分配在集群上(spark.executor.instances作为配置属性),虽然–executor-memory(spark.executor.memory选项)和–executor-cores(spark.executor.cores配置属性)控制每个executor资源.更多信息查看Yarn Spark 属性.
第二个在Mesos 上可用选项是动态分配CPU cores.在这个模式中,每个 Spark application仍然有固定和独立内存分配(设置通过spark.executor.memory),但是当application 没有运行任务在机器时,其它application 可能运行任务在这些核上.这种有用的当你期望大量没有运行的应用时,像来到不同用户的shell session.然而,他可以带来一个难以预测的延时,因为它可能等因为一个application获得cores在运行.使用这种模式,简单使用 mesos:// URL和设置 spark.mesos.coarse为false.
注意当前没有一个模式提供跨application内存共享.如果你想要用这样方式共享数据 ,我们推荐运行一个单独server可以提供多个请求通过查询相同的RDD.
动态资源分配
Spark提供一个机制动态调整你的程序占用的资源基于工具负载.这意味着你的application可能给资源还回到集群,如果他不在使用或过后还请求他们当有这样需求时.这个特性非常有用如果多个共享资源在你的集群中.
这个特性默认是禁用,在粗粒度集群管理是可用,如standalone mode, YARN mode, 和Mesos coarse-grained 模式.
配置和设置
使用这个特性有两个一定要调整.1,你application一定设置park.dynamicAllocation.enabled 为true.2 你必须设置一个 external shuffle服务在每个 worker node 在同一个集群中并且设置 spark.shuffle.service.enabled 为 true在你的application. external shuffle服务目的是允许executors 移除没有删除的shuffle 文件写通过他们(更多细节描述在下边).这个方式设置这个服务调整通过 集群管理.
在standalone 模式,简单开始 在你的workers 使用配置 spark.shuffle.service.enabled为 true.
在Mesos coarse-grained 模式,运行$SPARK_HOME/sbin/start-mesos-shuffle-service.sh
在所有slave nodes用spark.shuffle.service.enabled设置为true.例如,你可能这样做通过Marathon.
在Yarn模式可以查看使用说明
所有其它相关配置是可选的并且在the spark.dynamicAllocation.*
和spark.shuffle.service.*
下.更多细节查看配置页面.
资源分配策略
在高层次,Spark应该放弃executors当他们不在使用并且获得executors 当需要时.因为没有明确预测一个executor即将被移除运行的任务在不久将来,或是否一个新的executor 将要被加入将要空置,我们需要一组试探来决定什么时来移动和请求executors.
请求策略
用动态分配的Spark Application开始请求额外的executors当他有待处理任务在等来调度.这个条件必需意味着已存在executors不足同时处理所有任务,它他们已经提交但是没有完成.
Spark请求executors 是轮询.实际请求被触发当有待处理任务在spark.dynamicAllocation.schedulerBacklogTimeout 后,然后再次触发每个spark.dynamicAllocation.sustainedSchedulerBacklogTimeout 之后,如果待处理队列存在.另外,executors 的数量请求在每个轮询以指数增加相对上次轮询.例如,application将要增加1个executor 在第一次轮询,然后是2,4,8个executor, 依此类推.
指数增加策略动机有两个:1,application 应该请求executors 在开始时要谨慎,以防它结果仅有几个额外的executors有够了.这也回应了TCP 启动慢. 2, application 应该可以增加资源利用在及时方式,防止它生产很多实现需要用的executors .
移除策略
移除executors策略非常简单.spark application移除executor 当它已经空闲超过spark.dynamicAllocation.executorIdleTimeout设置的时间.注意:在大多数情况下,这个条件与请求条件互斥,在那样情况下executor 不应该空闲如果有待处理任务在被安排.
优雅关闭Executors
在动态分配之前,Spark executor 退出当失败或当关联的application也退出.在这种场景,所有executor 关联的状态不在需要并且可以安全丢弃. 使用动态分配,然而 ,application仍然运行当执行者明确移除.如果应用试图访问存储状态里边或通过executor写,它会必须执行重新计算状态. 因此,Spark需要一个机制来优雅关闭executor 通过保存它的状态在删除之前.
这个需求对于shuffles非常重要.在shuffle过程中,Spark executor 首先写它自己map输出到本地磁盘,然后作为这些文件的服务器当其它executors 试图获得时. 在掉队情况下,它运行时间比其它更长,动态分配可能移除executor 在shuffle完成之前,在这样例子中,shuffle 文件写通过那个executor需要做不必要的再次计算.
保留shuffle 文件方案用在扩展shuffle服务,在Spark1.2里有介绍.这个服务涉及一个长时运行进程,它运行在集群你Spark application每个node和他们的executors.如果这个服务开启, Spark executors会获取shuffle 文件从这个服务而不是他们彼此.这意味着任何suffle状态被executor 写可能继续服用在executor生命周期外.
除了写shuffle文件之外, executors 也缓存数据在磁盘或内存.当executor被移除,既然这样,所有的缓存数据不能再被访问.为了减轻这样,默认executor包含缓存数据是决不删除.你可以配置这个行为用spark.dynamicAllocation.cachedExecutorIdleTimeout
.在未来版本,缓存数据可能保留通过堆外存储像 external shuffle 服务保存shuffle 文件.
Application内部调度
Spark application(SparkContext实例)内,多个并行job同时运行,如果他们从不同线程提交.通过"job",在这部分,我们打算一个Spark action(像. save, collect)和任意任务,需要运行评估那个action. Spark调度是全线程安全并且支持使用开启应用服务多个请求(如,多个用户查询).
默认,Spark调度运行job以FIFO方式.每个job是被分成"stage"(如map和reduce阶段),并且首个job是先获得所有可用资源当它的stages任务启动,然后 是第二个job优先获得,等等.如果jobs队列中最前不需要使用集群全部资源 ,后边的jobs就能马上启动,但是如果job队列任务很大,后边的队列可能有显著延时.
从Spark0.8开始,可以配置job公之间公平分配.在公平分配下,Spark分配任务在jobs之间是"轮询"方式,导致所有job获得大约相同群集资源份额. 这意味着小job提交,在此期间大job能运行开始接收立刻并且 仍然获得好的回应时间,不需要等待长job结束.这种模式最适合多用户配置.
打开公平调度,仅设置spark.scheduler.mode
属性为FAIR
当配置SparkContexnt时.
val conf = new SparkConf().setMaster(...).setAppName(...)
conf.set("spark.scheduler.mode", "FAIR")
val sc = new SparkContext(conf)
公平调度池
公平调度也支持把job分组到pools中,并且 配置不同的调度选项(如.weight)对每个pool.这是非常有用的创建"高优先"池给更重要的job,例如,或每个用户job分组在一起,无论用户有多少个并发jobs,均给用户一样配额.这个方法仿照Hadoop 公平调度.
不需要任何干预,新提交的jobf进入默认pool,并且job pool可以被设置通过增加spark.scheduler.pool
"本地属性"给提交它们线程中SparkContext. 需要下边操作:
// Assuming sc is your SparkContext variable
sc.setLocalProperty("spark.scheduler.pool", "pool1")
设置完本地属性,在这个线程(在这个线程中被调用RDD.save, count,collect等等)中所有提交job将要使用这个pool名字.这个设置是每个线程非常容易代表同一个用户使一个线程运行多个job.如果你想要清除pool,线程关联的,简单调用:
sc.setLocalProperty("spark.scheduler.pool", null)
默认Pools行为
默认,每个pool获得集群(也等同于在默认池里的每个job)相同份额,但是在里边的每个pool,jobs运行是用FIFO 顺序.例如,如果你为每个用户创建一个线程池,这意味着每个用户会在集群中获得相同资源,并且每个用户的查询会以顺序运行,代替晚查询用户使用早运行用户的资源.
配置Pool属性
专门pool属性也可以通过配置文件修改.每个pool支持3个属性.
- schedulingMode: 可以是FIFO 或FAIR, 来控制pool 中job是彼此后边排队还是公平配置共享pool资源
- weight: 控制集群中池相对其它池份额.默认,所有池weight为1.如果你指定weight为2, 例如,它会有倍资源比其它运行的池.设置高weight像1000,本质上也使实现不同pool的优先级成为可能, weight-1000池会总是先获得启动任务,无论什么时候他的job运行.
- minShare: 除了weight,每个pool可以指定最小份额(像cpu核数),管理员可以拥有.公平调度总是试图满足活跃的pool最小份额在重新分配额外资源之前通过权重. minShare 属性可以成放心另一种方式保证 pools可以总是获得确定数量的资源 (像10核)非常快,无需要设置优先级给集群中其它部分.默认每个pool=0.
pool属性可以通过创建xml文本来配置,类似于 conf/fairscheduler.xml.tmplate,并且或者放文件名为fairscheduler.xml到类路径,或者设置spark.scheduler.allocation.file的属性在SparkConfig
conf.set("spark.scheduler.allocation.file", "/path/to/file")
XML文件格式是简单的 元素为每个pool,用不同元素在为不同配置.例如
<?xml version="1.0"?>
<allocations>
<pool name="production">
<schedulingMode>FAIR</schedulingMode>
<weight>1</weight>
<minShare>2</minShare>
</pool>
<pool name="test">
<schedulingMode>FIFO</schedulingMode>
<weight>2</weight>
<minShare>3</minShare>
</pool>
</allocations>
完整例子在conf/fairscheduler.xml.template中.注意任意pools没有配置在 XML 文件中将为默认值对所有设置.(调度模式为FIFO,weigth 1, minShare 0).
调度使用JDBC 连接器
设置公平调度给JDBC client session,用户可以设置spark.sql.thriftserver.scheduler.pool变量
SET spark.sql.thriftserver.scheduler.pool=accounting;