Spark有多种运行模式,可以运行在一台机器上,称为本地(单机)模式,也可以以YARN或Mesos作为底层资源调度系统以分布式的方式在集群中运行,称为Spark On YARN模式,还可以使用Spark自带的资源调度系统,称为Spark Standalone模式。
本地模式通过多线程模拟分布式计算,通常用于对应用程序的简单测试。本地模式在提交应用程序后,将会在本地生成一个名为SparkSubmit的进程,该进程既负责程序的提交,又负责任务的分配、执行和监控等。
接下来将重点讲解其他几种常用集群模式的运行架构。
在讲解Spark集群架构之前,首先需要了解YARN集群的架构。YARN集群总体上是经典的主/从(Master/Slave)架构,主要由ResourceManager、NodeManager、ApplicationMaster和Container等几个组件构成。YARN集群架构如图2-4所示。
图2-4 YARN集群架构
下面对各个组件进行解析。
以后台进程的形式运行,负责对集群资源进行统一管理和任务调度。ResourceManager的主要职责如下:
● 接收来自客户端的请求。
● 启动和管理各个应用程序的ApplicationMaster。
● 接收来自ApplicationMaster的资源申请,并为其分配Container。
● 管理NodeManager,接收来自NodeManager的资源和节点健康情况汇报。
集群中每个节点上的资源和任务管理器,以后台进程的形式运行。它会定时向ResourceManager汇报本节点上的资源(内存、CPU)使用情况和各个Container的运行状态,同时会接收并处理来自ApplicationMaster的Container启动/停止等请求。NodeManager不会监视任务,它仅监视Container中的资源使用情况,例如,如果一个Container消耗的内存比最初分配的更多,它会结束该Container。
应用程序的具体执行任务。一个应用程序可能有多个任务,例如,一个MapReduce程序可以有多个Map任务和多个Reduce任务(第5章会对MapReduce进行详细讲解)。
YARN中资源分配的基本单位,封装了CPU和内存资源的一个容器,相当于是一个Task运行环境的抽象。从实现上看,Container是一个Java抽象类,定义了资源信息。应用程序的Task将会被发布到Container中运行,从而限定Task使用的资源量。Container类的部分源码如下:
从上述代码中可以看出,Resource是Container类中定义的一个重要属性类型,内存和CPU的资源信息正是存储于Resource类中。Resource类也是一个抽象类,其中定义了内存和CPU核心数,该类的部分源码如下:
Container的大小取决于它所包含的资源量。一个节点上的Container数量由节点空闲资源总量(总CPU数和总内存)决定。
在YARN的NodeManager节点上拥有许多动态创建的Container。NodeManager会将机器的CPU和内存的一定值抽离成虚拟的值,然后这些虚拟的值根据配置组成多个Container,当应用程序提出申请时,就会对其分配相应的Container。
此外,一个应用程序所需的Container分为两类:运行ApplicationMaster的Container和运行各类Task的Container。前者是由ResourceManager向内部的资源调度器申请和启动的,后者是由ApplicationMaster向ResourceManager申请的,并由ApplicationMaster请求NodeManager进行启动。
我们可以将Container类比成数据库连接池中的连接,需要的时候进行申请,使用完毕后进行释放,而不需要每次独自创建。
应用程序管理者主要负责应用程序的管理,以后台进程的形式运行。为应用程序向ResourceManager申请资源(CPU、内存),并将资源分配给所管理的应用程序的Task。
一个应用程序对应一个ApplicationMaster。例如,一个MapReduce应用程序会对应一个ApplicationMaster(MapReduce应用程序运行时会在NodeManager节点上启动一个名为MRAppMaster的进程,该进程则是MapReduce的ApplicationMaster实现),一个Spark应用程序也会对应一个ApplicationMaster。
在用户提交一个应用程序时,会启动一个ApplicationMaster实例,ApplicationMaster会启动所有需要的Task来完成它负责的应用程序,并且监视Task运行状态和运行进度,重新启动失败的Task,等等。应用程序运行完成后,ApplicationMaster会关闭自己并释放自己的Container,以便其他应用程序的ApplicationMaster或Task转移至该Container中运行,提高资源利用率。
ApplicationMaster自身和应用程序的Task都在Container中运行。
ApplicationMaster可在Container内运行任何类型的Task。例如,MapReduce ApplicationMaster请求一个容器来启动Map Task或Reduce Task。也可以实现一个自定义的ApplicationMaster来运行特定的Task,以便任何分布式框架都可以受YARN支持,只要实现了相应的ApplicationMaster即可。
总结来说,我们可以这样认为:ResourceManager管理整个集群,NodeManager管理集群中单个节点,ApplicationMaster管理单个应用程序(集群中可能同时有多个应用程序在运行,每个应用程序都有各自的ApplicationMaster)。
YARN集群中应用程序的执行流程如图2-5所示。
图2-5 YARN集群中应用程序的执行流程
(1)客户端提交应用程序(可以是MapReduce程序、Spark程序等)到ResourceManager。
(2)ResourceManager分配用于运行ApplicationMaster的Container,然后与NodeManager通信,要求它在该Container中启动ApplicationMaster。ApplicationMaster启动后,它将负责此应用程序的整个生命周期。
(3)ApplicationMaster向ResourceManager注册(注册后可以通过ResourceManager查看应用程序的运行状态)并请求运行应用程序各个Task所需的Container(资源请求是对一些Container的请求)。如果符合条件,ResourceManager就会分配给ApplicationMaster所需的Container(表达为Container ID和主机名)。
(4)ApplicationMaster请求NodeManager使用这些Container来运行应用程序的相应Task(将Task发布到指定的Container中运行)。
此外,各个运行中的Task会通过RPC协议向ApplicationMaster汇报自己的状态和进度,这样一旦某个Task运行失败,ApplicationMaster就可以对其重新启动。当应用程序运行完成时,ApplicationMaster会向ResourceManager申请注销自己。
Spark Standalone模式为经典的Master/Slave(主/从)架构,资源调度是Spark自己实现的。在Standalone模式中,根据应用程序提交的方式不同,Driver(主控进程)在集群中的位置也有所不同。应用程序的提交方式主要有两种:client和cluster,默认是client。可以在向Spark集群提交应用程序时使用--deploy-mode参数指定提交方式。
当提交方式为client时,运行架构如图2-6所示。
图2-6 Standalone模式架构(client提交方式)
集群的主节点称为Master节点,在集群启动时会在主节点启动一个名为Master的守护进程,类似YARN集群的ResourceManager;从节点称为Worker节点,在集群启动时会在各个从节点上启动一个名为Worker的守护进程,类似YARN集群的NodeManager。
Spark在执行应用程序的过程中会启动Driver和Executor两种JVM进程。
Driver为主控进程,负责执行应用程序的main()方法,创建SparkContext对象(负责与Spark集群进行交互),提交Spark作业,并将作业转化为Task(一个作业由多个Task任务组成),然后在各个Executor进程间对Task进行调度和监控。通常用SparkContext代表Driver。在图2-6的架构中,Spark会在客户端启动一个名为SparkSubmit的进程,Driver程序则运行于该进程。
Executor为应用程序运行在Worker节点上的一个进程,由Worker进程启动,负责执行具体的Task,并存储数据在内存或磁盘上。每个应用程序都有各自独立的一个或多个Executor进程。在Spark Standalone模式和Spark on YARN模式中,Executor进程的名称为CoarseGrainedExecutorBackend,类似运行MapReduce程序所产生的YarnChild进程,并且同时与Worker、Driver都有通信。
当提交方式为cluster时,运行架构如图2-7所示。
图2-7 Standalone模式架构(cluster提交方式)
Standalone cluster提交方式提交应用程序后,客户端仍然会产生一个名为SparkSubmit的进程,但是该进程会在应用程序提交给集群之后就立即退出。当应用程序运行时,Master会在集群中选择一个Worker进程启动一个名为DriverWrapper的子进程,该子进程即为Driver进程,所起的作用相当于YARN集群的ApplicationMaster角色,类似MapReduce程序运行时所产生的MRAppMaster进程。
具体Spark应用程序的提交及参数的设置将在2.6节详细讲解。
Spark On YARN模式遵循YARN的官方规范,YARN只负责资源的管理和调度,运行哪种应用程序由用户自己实现,因此可能在YARN上同时运行MapReduce程序和Spark程序,YARN很好地对每一个程序实现了资源的隔离。这使得Spark与MapReduce可以运行于同一个集群中,共享集群存储资源与计算资源。
Spark On YARN模式与Standalone模式一样,也分为client和cluster两种提交方式。
Spark On YARN的client提交方式提交应用程序后的主要进程有:SparkSubmit、ResourceManager、NodeManager、CoarseGrainedExecutorBackend、ExecutorLauncher,运行架构如图2-8所示。
图2-8 Spark On YARN模式架构(client提交方式)
与Standalone模式的client提交方式类似,客户端会产生一个名为SparkSubmit的进程,Driver程序则运行于该进程中,且ResourceManager的功能类似于Standalone模式的Master;NodeManager的功能类似于Standalone模式的Worker。当Spark程序运行时,ResourceManager会在集群中选择一个NodeManager进程启动一个名为ExecutorLauncher的子进程,该子进程是Spark的自定义实现,承担YARN中的ApplicationMaster角色,类似MapReduce的MRAppMaster进程。
使用Spark On YARN的client提交方式提交Spark应用程序的执行步骤如下:
(1)客户端向YARN的ResourceManager提交Spark应用程序。客户端本地启动Driver。
(2)ResourceManager收到请求后,选择一个NodeManager节点向其分配一个Container,并在该Container中启动ApplicationMaster(指ExecutorLauncher进程),该ApplicationMaster中不包含Driver程序,只负责启动和监控Executor(指CoarseGrainedExecutorBackend进程),并与客户端的Driver进行通信。
(3)ApplicationMaster向ResourceManager申请Container。ResourceManager收到请求后,向ApplicationMaster分配Container。
(4)ApplicationMaster请求NodeManager,NodeManager在获得的Container中启动CoarseGrainedExecutorBackend。
(5)CoarseGrainedExecutorBackend启动后,向客户端的Driver中的SparkContext注册并申请Task。
(6)CoarseGrainedExecutorBackend得到Task后,开始执行Task,并向SparkContext汇报执行状态和进度等信息。
Spark On YARN的cluster提交方式提交应用程序后的主要进程有:SparkSubmit、ResourceManager、NodeManager、CoarseGrainedExecutorBackend、ApplicationMaster,运行架构如图2-9所示。
图2-9 Spark On YARN模式架构(cluster提交方式)
与Standalone模式的cluster提交方式类似,客户端仍然会产生一个名为SparkSubmit的进程,且ResourceManager的功能类似于Standalone模式的Master;NodeManager的功能类似于Standalone模式的Worker。ResourceManager会在集群中选择一个NodeManager进程启动一个名为ApplicationMaster的子进程,该子进程即为Driver进程(Driver程序运行在其中),同时作为一个YARN中的ApplicationMaster向ResourceManager申请资源,进一步启动Executor(这里指CoarseGrainedExecutorBackend)以运行Task。
使用Spark On YARN的cluster提交方式提交Spark应用程序的执行步骤如下:
(1)客户端向YARN的ResourceManager提交Spark应用程序。
(2)ResourceManager收到请求后,选择一个NodeManager节点向其分配一个Container,并在该Container中启动ApplicationMaster,ApplicationMaster中包含SparkContext的初始化。
(3)ApplicationMaster向ResourceManager申请Container。ResourceManager收到请求后,向ApplicationMaster分配Container。
(4)ApplicationMaster请求NodeManager,NodeManager在获得的Container中启动CoarseGrainedExecutorBackend。
(5)CoarseGrainedExecutorBackend启动后,向ApplicationMaster的Driver中的SparkContext注册并申请Task(这一点与Spark On YARN的client方式不一样)。
(6)CoarseGrainedExecutorBackend得到Task后,开始执行Task,并向SparkContext汇报执行状态和进度等信息。
无论是Spark On YARN的client提交方式还是Standalone的client提交方式,由于Driver运行在客户端本地,因此适合需要与本地进行交互的场合,例如Spark Shell。这种方式下,客户端可以直接获取运行结果,监控运行进度,常用于开发测试与调试;但缺点是,客户端存在于整个应用程序的生命周期,一旦客户端断开连接,应用程序的执行将关闭。
Spark On YARN的cluster提交方式和Standalone的cluster提交方式,Driver都运行于服务端的ApplicationMaster角色中,客户端断开并不影响应用程序的执行,这种方式适用于生产环境。