Learning Spark笔记14-通过集群运行

集群方式运行


Spark运行时框架


在我们深入了解在集群上运行Spark的细节之前,理解Spark的分布式模式架构是非常有用的。在分布式模式,Spark使用master/slave的架构,那就是一个中央协调者和多个分布式的工作者。中央的协调者称为driver。driver可能与数量非常大的分布式工作者进行沟通,他们被称为executors。driver运行在自己的java进程中,每个executor是独立的java进程。一个driver和它的executors在一起被称为一个Spark应用程序。


Spark应用程序通过在一组机器上的一个被称为集群管理者的外部服务启动。如前所述,Spark有一个内建集群管理器叫做
独立的集群管理器。Spark也可以与Hadoop YARN和Apache Mesos两个开源的集群管理器一起工作。


Driver


driver是你的程序main()方法运行的进程。该进程运行用户代码,创建一个SparkContext,创建RDDs和执行转换、动作。当你启动一个Spark shell,你会创建一个driver程序(如果你记的话,Spark shell会预装SparkContext称为sc)。一旦driver技术,应用程序也会完成。


当driver运行的时候,它有两个职责:


1.转换一个用户程序为任务:Spark driver负责转换将一个用户程序转换为物理上执行的单元称为tasks。在高层次上,所有的Spark程序都有相同的结构:他们从输入创建RDDs,使用转换派生新的RDD,执行收集或保存数据的动作。Spark程序会隐式的为操作创建一个逻辑的DAG。当driver运行时候,它转换逻辑图为物理执行计划。SPark执行几个优化,例如“流水线”映射转换在一起以合并它们,然后将执行的图转换为一组stages。反过来,每个stage由多个task组成。这些task是捆绑的,准备发送给集群。Task是Spark中最小的工作单元;一个通常的用户程序可以启动成百上千独立的tasks。


2.计划执行tasks:一个物理的执行计划,一个Spark driver必须协调独立的task的计划。当执行者启动后,他们会向driver进行注册,随时都能看到应用的执行者。每个执行者代表一个进程,该进程包括运行的tasks和RDD数据。


Spark driver会看着当前执行者组,然后它会基于数据放置的位置来尝试在一个适当的位置上计划每个task。当tasks执行时,他们有缓存数据的副作用。driver还会跟踪数据的位置,并计划安排未来task访问的数据。


运行的Spark应用程序,通过driver的web接口来发布信息,默认的端口是4040。例如,在本地模式,UI是http://localhost:4040。




Executors


Spark executors负责在给定Spark作业中运行单独tasks的工作进程。Executors在Spark应用程序开始时启动一次,通常在应用程序的整个生命周期中运行,如果executors失败Spark应用程序会继续执行。Executors有两个规则。首先,他们运行构成应用程序的任务并将结果返回给driver。其次,它们为用户程序缓存的RDD提供内存中的存储,通过一个名为Block Manager的服务,该服务位于每个executor中。因为RDD直接缓存到executors中,task可以与缓存数据一起运行。


Cluster Manager


到现在为止,我们以抽象的方式来讨论了drivers和executors。但是drivers和executor最初处理方式如何启动?Spark依赖cluster manager启动executor,在某种情况下,还可以启动driver。cluster manager是Spark中可插拔的组件。


这允许Spark在不同的外部管理器(如YARN和Mesos)以及其内置的独立集群管理器之间运行。


启动程序


无论您使用哪个集群管理器,Spark都提供了一个脚本,您可以使用该脚本将您的程序提交给它,名为spark-submit。通过各种选项,sparksubmit可以连接到不同的集群管理器,并控制您的应用程序获得多少资源。对于某些集群管理器,spark-submit可以在集群中运行驱动程序(例如,在YARN工作节点上),而对于其他集群管理器,它可以仅在本地计算机上运行。


使用spark-submit发布应用程序


Example 7-1. Submitting a Python application
bin/spark-submit my_script.py


当spark-submit被调用时,只有脚本或JAR的名称,它只是简单的在本地运行提供的Spark程序。我们提供额外的参数,包括集群的地址和每个executor进程的大小。


--master是要连接的集群地址;在这种情况下,spark://URL意味着一个集群使用Spark的独立模式。


除了集群URL,spark-submit为你的应用提供更详细的控制。这有两大类,第一类是计划信息,像是你想要为你的job请求的资源的数量。第二类是你的应用运行的依赖,像是发布到所用工作节点的类库或文件。


spark-submit的通常格式


bin/spark-submit [options] <app jar | python file> [app options]


[options]
可以通过--help查看
<app jar | python file>
指的是JAR或Python脚本,其中包含您的应用程序中的入口点
[app options]
main()方法的参数,但不是spark-submit的参数


Example 7-4. Using spark-submit with various options
# Submitting a Java application to Standalone cluster mode
$ ./bin/spark-submit \
 --master spark://hostname:7077 \
 --deploy-mode cluster \
 --class com.databricks.examples.SparkExample \
 --name "Example Program" \
 --jars dep1.jar,dep2.jar,dep3.jar \
 --total-executor-cores 300 \
 --executor-memory 10g \
 myApp.jar "options" "to your application" "go here"
# Submitting a Python application in YARN client mode
$ export HADOP_CONF_DIR=/opt/hadoop/conf
$ ./bin/spark-submit \
 --master yarn \
 --py-files somelib-1.2.egg,otherlib-4.4.zip,other-file.py \
 --deploy-mode client \
 --name "Example Program" \
 --queue exampleQueue \
 --num-executors 40 \
 --executor-memory 10g \
 my_script.py "options" "to your application" "go here"


包装你的代码和依赖


通常来说,用户的程序依赖第三方包。如果您的程序导入任何不在org.apache.spark包或语言库中的库,那么您需要确保所有的依赖项都存在于Spark应用程序的运行时。


对于Python的用户,有一些方法安装第三方类库。因为PySpark使用工作节点上的Python安装包,你可以使用Python的包管理(pip或者easy_install)安装依赖,或者通过手动安装到site-packages下。另外,你可以使用--py-files参数提交个人的类库给spark-submit,然后他们会被添加到Python解释器路径中。


Java和Scala用户,可以使用--jar来提交个人的jar文件给spark-submit。如果你只是依赖一个或两个类库上面的方法很简单。但是如果如果依赖很多,当提交一个应用给Spark时,它会将整个依赖的图发送给集群。这样就不仅仅包括你直接的依赖包,还包括这些依赖包的依赖,以此类推。手动跟踪和提交这组JAR文件将是非常麻烦的。 相反,通常的做法是依靠构建工具来生成单个大JAR包含应用程序的整个传递依赖图。这通常被称为uber JAR或组件JAR,大多数Java或Scala构建工具可以产生这种类型的工件。Java和Scala最流行的构建工具是Maven和sbt(Scala构建工具)。任何一种工具都可以使用任何语言,但是Maven更常用于JavaScala项目和sbt项目。


Maven建立Java Spark应用程序


让我们看一个多依赖Java工程生成uber JAR的例子。Maven的pom.xml文件包括建立的定义。Maven建立Spark应用程序
<project>
<modelVersion>4.0.0</modelVersion>
<!-- Information about your project -->
<groupId>com.databricks</groupId>
<artifactId>example-build</artifactId>
<name>Simple Project</name>
<packaging>jar</packaging>
<version>1.0</version>
<dependencies>
<!-- Spark dependency -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.2.0</version>
<scope>provided</scope>
</dependency>
<!-- Third-party library -->
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
<version>4.3</version>
</dependency>
<!-- Third-party library -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven shade plug-in that creates uber JARs -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
该项目声明了两个传递依赖:jopt-simple,用于执行选项解析的Java库,以及joda-time,一个具有时间和日期转换实用程序的库。Spark也有依赖,但是Spark标记为provided保证Spark不会与应用程序组件一起打包。建立的时候包括maven-shade-plugin创建一个uber JAR包括所有的依赖。你可以通过每次package发生的阶段来执行plug-in的shade目标。根据上面的配置,uber JAR会在mvn package运行时自动创建。


Example 7-6. Packaging a Spark application built with Maven
$ mvn package
# In the target directory, we'll see an uber JAR and the original package JAR
$ ls target/
example-build-1.0.jar
original-example-build-1.0.jar
# Listing the uber JAR will reveal classes from dependency libraries
$ jar tf target/example-build-1.0.jar
...
joptsimple/HelpFormatter.class
...
org/joda/time/tz/UTCProvider.class
...
# An uber JAR can be passed directly to spark-submit
$ /path/to/spark/bin/spark-submit --master local ... target/example-build-1.0.jar


sbt建立Scala Spark应用程序


sbt是新的Scala建立工具。sbt假设与Maven类似的布局。项目的根路径下创建一个建立文件build.sbt,源代码在src/main/scala中。sbt建立文件由配置语言编写,可以为键指定特定的值。例如,name包括工程名字,libraryDependecies包括工程的依赖。下面的例子是一个简单sbt建立文件的应用程序依赖于几个第三方的依赖库。


Example 7-7. build.sbt file for a Spark application built with sbt 0.13


import AssemblyKeys._
name := "Simple Project"
version := "1.0"
organization := "com.databricks"
scalaVersion := "2.10.3"
libraryDependencies ++= Seq(
// Spark dependency
"org.apache.spark" % "spark-core_2.10" % "1.2.0" % "provided",
// Third-party libraries
"net.sf.jopt-simple" % "jopt-simple" % "4.3",
"joda-time" % "joda-time" % "2.0"
)
// This statement includes the assembly plug-in capabilities
assemblySettings
// Configure JAR used with the assembly plug-in
jarName in assembly := "my-project-assembly.jar"
// A special option to exclude Scala itself form our assembly JAR, since Spark
// already bundles Scala.
assemblyOption in assembly :=
(assemblyOption in assembly).value.copy(includeScala = false)


此构建文件中的第一行从支持创建项目组合JAR的sbt构建插件导入一些功能。为了让插件可用,我们的工程下必须有assembly.sbt这个文件


Example 7-8. Adding the assembly plug-in to an sbt project build
# Display contents of project/assembly.sbt
$ cat project/assembly.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")


Example 7-9. Packaging a Spark application built with sbt
$ sbt assembly
# In the target directory, we'll see an assembly JAR
$ ls target/scala-2.10/
my-project-assembly.jar
# Listing the assembly JAR will reveal classes from dependency libraries
$ jar tf target/scala-2.10/my-project-assembly.jar
...
joptsimple/HelpFormatter.class
...
org/joda/time/tz/UTCProvider.class
...
# An assembly JAR can be passed directly to spark-submit
$ /path/to/spark/bin/spark-submit --master local ...
 target/scala-2.10/my-project-assembly.jar


 依赖冲突


 一个意外的最糟糕的事情就是你的应用依赖包与Spark依赖包冲突。虽然这种情况较少,但是一旦出现就会让人非常头痛。通常情况下,当在执行Spark作业期间抛出与类加载相关的NoSuchMethodError,ClassNotFoundException或某些其他JVM异常。解决这种问题有两种办法。第一种是将你依赖库的版本修改为与Spark使用版本一致。第二种是使用通常称为“shading”的过程来修改应用程序的打包。Shading允许你生成一个冲突包的备份,在不同的命名空间中重写你的应用程序的代码使用重新命名的版本。这种有点强力的技术在解决运行时依赖冲突方面非常有效。


 在Spark应用程序之间安排计划


 之前的例子都是单个用户提交作业给集群。实际上,很多集群都是在多个用户之间共享的。共享的环境的挑战就是:如果两个用户启动Spark应用程序都会请求整个集群的资源该怎么办?计划策略保证资源不会不堪重负,通过工作负载的优先级控制。


 Spark主要依赖于集群管理器在Spark应用程序之间共享资源。当一个Spark应用程序从集群管理器上请求executors的时候,它会根据集群上可用、争用的情况得到或多或少的executors。集群管理器提供了定义不同优先级或容量限制的队列,Spark会提交作业到这样的队列中。


 其中一个特列就是那些运行时间很长的应用程序。像是JDBC,当JDBC服务器启动时,它从集群管理器获取一组executors,然后作为用户提交的SQL查询的永久网关。Spark内部公平计划器为长时间运行的应用程序定义一个队列来优先处理任务调度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺菲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值