推广一下自己的个人主页zicesun.com
Spark的集群调度其实蛮简单的,这里总结了一些集群资源调度的基本原理。
资源注册
spark集群是怎么管理集群的计算资源和内存资源的呢?其实在这背后有一个资源注册的机制。简单来说,就是Worker节点讲自己的资源报告给master。
首先,在Worker节点启动的时候,Worker进程对应的worker类会实现一个WorkerArguments类从配置文件中读取Worker进程相关的配置信息,或者使用默认值,包括分配给Worker的核心数和内存容量。如果没有在配置文件配置这些资源,那么则采取默认的值,可有inferDefaultCores方法和inferDefaultMemory方法得到。如果配置文件中配置了,那么则采用SPARK_WORKER_CORES和SPARK_WORKER_MEMORY设置的值。如果再启动Worker的时候,显式地指定了参数--core(--c)或者--memory(--m),则采用显式制定的值。
然后,Worker需要向Master进程注册,汇报Worker拥有的计算资源和内存容量。在实现的时候,调用Worker类中的tryRegisterAllMaster方法,通过Akka通信机制向Master节点发送注册消息RegisterWorker。包括Worker的节点编号,主机地址,端口,用户分配的内存以及核心数等信息。
最后,Master节点将Worker发送来的信息进行记录,存储在WorkerInfo对象中。
以上就是Worker资源注册的过程。
资源申请和分配
在集群环境下,Driver程序通过实例化CoarseGrainedSchedulerBackend类实现Driver程序与集群之间的通信。不同的集群模式实例化不同的CoarseGrainedSchedulerBackend子类。对于Standalone来说,使用的是SparkDeploySchedulerBackend类,该类在启动的时候会实例化一个APPClient类,APPClient会想Master节点发送应用注册请求,注册请求含有应用需要的资源情况。
Master节点在接收到应用程序的注册请求之后,会把应用放在等待队列中,并调用Master.scheduler方法,这个方法对应的是Master进程的驱动程序/应用程序调度逻辑:首先对Driver进程调度,在YARN集群模式下,Driver程序可以运行在worker节点上,因此Master节点需要专门分配相应的集群资源来运行Driver进程,采用的方法就是随机把Driver分配到空闲的Worker上。
具体的应用程序的调度,多个应用程序之间采用FIFO调度方法,按照应用程序注册的先后顺序分配资源。对于单个应用程序之间,则有SpreadOut和非SpreadOut两种调度策略。在分配资源之前,Master需要查询当前集群的内存资源是否满足运行应用程序最低的需求,并且之前为这个应用程序分配过Executor的Worker不能参与资源调度,一个应用程序在一个Worker上只能有一个Executor。
SpreadOut策略,Master采用轮询的方式,让每个可用的Worker为应用程序分配一个核心,知道分配的核心满足应用程序的需求。 非SpreadOut策略,会一次性把一个Worker上所有可分配的核心全部分配给应用程序。
被分配任务的Worker会调用addExecutor函数来添加相应的Executor,并调用launchExecutor方法启动Executor,改方法会记录Worker被消耗的资源,并向对应的Worker发送消息,通知其启动Executor。Worker接收到消息之后,会记录资源消耗量,并启动新的Executor进程。
上图是YARN集群模式下资源调度的流程图,根据这幅图,我们也可以了解资源调度的情况。 提交一个Spark应用程序,首先通过Client向ResourceManager请求启动一个Application,同时检查是否有足够的资源满足Application的需求,如果资源条件满足,则准备ApplicationMaster的启动上下文,交给ResourceManager,并循环监控Application状态。当提交的资源队列中有资源时,ResourceManager会在某个NodeManager上启动ApplicationMaster进程,ApplicationMaster会单独启动Driver后台线程,当Driver启动后,ApplicationMaster会通过本地的RPC连接Driver,并开始向ResourceManager申请Container资源运行Executor进程(一个Executor对应与一个Container),当ResourceManager返回Container资源,则在对应的Container上启动Executor。
Driver线程主要是初始化SparkContext对象,准备运行所需的上下文,然后一方面保持与ApplicationMaster的RPC连接,通过ApplicationMaster申请资源,另一方面根据用户业务逻辑开始调度任务,将任务下发到已有的空闲Executor上。
当ResourceManager向ApplicationMaster返回Container资源时,ApplicationMaster就尝试在对应的Container上启动Executor进程,Executor进程起来后,会向Driver注册,注册成功后保持与Driver的心跳,同时等待Driver分发任务,当分发的任务执行完毕后,将任务状态上报给Driver。