Spark 集群相关概念
编写 Spark 程序的一大好处是: 如果想要提升计算力和效率,只需要增加机器就可以了。对用户来说,只需要先在本地或者小的集群上先测试,然后不需修改任何代码就可以在生产环境执行。
Spark 运行时架构

在分布式模式下,Spark 使用 master/slave 架构:中央调节器被称作 driver,driver 和分布式的 worker 通信并协调,这些 worker 被称作 executor。每个 driver 都在自己的 Java 进程中运行,而其他的 executor 分别运行在自己单独的 Java 进程中。一个 driver 和它所有的 executor 组成一个 Spark 应用。
一个 Spark 应用通过一个外部的服务 — 集群管理器 — 在一群机器上启动。
Driver
Driver是 Spark 程序中的 main() 方法运行所在的进程:driver 负责创建 SparkContext, 创建 RDD,并且负责执行 transformations 和 actions。当 driver 进程终止运行时,整个 spark 应用也结束了。
driver 在整个生命周期有两个职责:
把 Saprk 程序分解为 任务(task)
任务 是真正的物理执行过程。
所有的 Spark 程序都遵循相同的结构:- 从 input 创建 RDD;
- 通过 transformations, 从这些 RDD 派生出新的 RDD;
- 执行 action 操作收集或者保存数据;
一个 Spark 程序会隐式地创建一个所有操作的 有向无环图(DAG)。driver 会将这个逻辑图转换成物理执行计划。
Spark 会做一些优化操作,比如将 map pipeline 到一起并合并,然后把执行图转换为一些 stage,在每个 stage 中都包含一些 tasks。任务被打包到一起并且被发送到集群中执行。
在 executor 上调度 task 的执行
当 executors 被启动时,它们会向 Driver 注册,因此在整个生命周期中 driver 都掌握着 executor 的全貌。每个 executor 代表着一个有能运行 task 并保存 RDD 数据的进程。
driver 会根据掌握的数据分布情况将不同的任务分发到不同的 executor 上。
当 task 执行时,可能会有缓存数据存在于 executor 中,driver 也会掌握这些缓存数据以备后续的使用。
Executors
Executors 是负责运行具体 task 的 worker 进程。Executor 在 Spark 应用启动之初也随之启动。Executor 也有两个角色:
- 运行被分配的任务,并且向 driver 返回结果;
- 提供 RDD 的内存缓存之处。由于 RDD 被直接缓存在 executor 中,因此 task 可以伴随缓存数据执行。
集群管理器
Spark 依赖一个集群管理器来启动所有的 executor,在一些情况下,也会启动 driver。
总结
在集群中运行 Spark 应用的步骤:
- 用户使用 spark-submit 提交应用;
- spark-submit 启动 driver 程序并调用 main() 方法;
- driver 向 cluster manager 申请资源来启动 executors;
- cluster manager 启动 executor;
- driver 在整个应用执行期间一直运行,基于 RDD action 和 transformation,driver 将 task 发送给 executors.
- task 在 executor 上运行并保存结果;
- 如果 driver 上的
main()
方法退出了或者调用了SparkContext.stop()
方法,那么 driver 会终止 executors 并且通知 cluster manager 释放资源。
使用 spark-submit
部署应用
使用方法:
bin/spark-submit [options] <app jar | python file> [app options]
[options]
是一组 spark-submit
使用的选项,这些选项大致上可以被分为两类:
- 调度信息。比如这个 job 需要的资源
- 运行时依赖。比如需要部署到所有 executor 上的依赖库。
重点说一下下面的选项:
--deploy-mode
当使用client
的部署模式时,driver 进程就在调用 spark-submit 的机器上;
当使用cluster
部署模式时,driver 进程在 YARN 集群上的摸一个 worker node 上;--jars
一些会被上传并放在应用的classpath
的 JAR 文件,如果应用依赖少量的 JAR 包,可以用这样的方式。--files
一些会被放置在应用运行的文件夹的文件,如果你有想要分不到所有节点上的数据文件,可以用这样的方式。
一些小坑
当发布 uber-jar 到集群上时,有可能会遇到你的应用所依赖的某些包和 Spark 依赖的包有冲突的情况。解决办法是使用 shading 技术。(比如使用 maven-shade-plugin
)
资源竞争
Spark 程序依赖 Cluster manager 提供的类似 queue 的概念来解决不同 Spark 应用间的资源竞争的问题。