在Mesos上运行Spark
Spark可以在由Apache Mesos 管理的硬件集群中运行。
在Mesos集群中使用Spark的主要优势有:
- 可以在Spark以及其他框架(frameworks)之间动态划分资源。
- 可以同时部署多个Spark实例,且各个实例间的资源分配可以调整。
工作原理
在独立部署的Spark集群中,下图里的Cluster Manager代表Spark master。然而,在Mesos集群中,Mesos master将取代Spark master在下图中的地位。
如果一个Spark驱动程序(driver)提交一个作业(job)并开始分发调度作业相关的任务(task),将会由Mesos来决定每个任务分发到哪台机器上。Mesos在调度短期任务的时候,会根据提交任务的账号来动态分配资源,而不需要依赖于静态的资源划分。
按以下所示的步骤,来安装Mesos,以及向Mesos提交Spark作业。
安装Mesos
Spark 1.6.0使用Mesos 0.21.0的时候,不需要任何特殊的Mesos补丁包。
如果你已经有一个Mesos集群,可以忽略Mesos安装这一步。
为Spark安装的Mesos和你为其他框架安装的Mesos没有什么不同。既可以从源代码安装,也可以从预编译好的包安装。
使用源代码安装
按以下步骤可以使用源代码安装Apache Mesos:
- 从镜像(mirror)下载一个Mesos发布版本
- 按Mesos的开始页(Getting Started)所示的步骤来编译和安装Mesos
注意:如果你不想把Mesos安装到系统默认路径下(比如,你没有管理员权限),可以通过configure脚本的–prefix参数来指定安装路径,例如,configure –prefix=/home/me/mesos。默认的安装路径是/usr/local。
使用第三方包安装
Apache Mesos项目本身只发布源码包,但你可以从一些第三方项目中找到Mesos的二进制发布包,二进制包在安装时相对更方便一些。
其中一个第三方项目是Mesosphere。按以下步骤可以安装Mesosphere提供的二进制发布包:
- 从下载页面(downloads page)下载Mesos安装包
- 按页面上所提供的安装指令即可安装和配置Mesos
Mesosphere的安装文档中建议使用Zookeeper来设置Mesos master的容错备份,但其实Mesos并不依赖于Zookeeper,可以以单master方式运行。
验证
接下来,需要通过浏览Mesos master的Web UI(其端口为5050)来确认一下,是否页面上slave tab显示了所有集群中的slave机器。
Spark连接Mesos
要在Mesos上运行Spark,你需要在Mesos能访问到的地方部署Spark的二进制包,并且需要配置一下Spark驱动程序(driver)以便使其可以连接到Mesos。
当然,你也可以把Spark安装到Mesos slave机器上和Mesos相同的目录下,然后配置一下spark.mesos.executor.home(默认等于${SPAKR_HOME}),使其指向这个目录。
上传Spark包
Mesos在某个slave机器上首次运行Spark任务的时候,slave机器上需要一个运行Spark Mesos executor backend的二进制包,这个Spark包可以放在任何能够以Hadoop兼容URL访问到的地方,包括HTTP( http://
, Amazon Simple Storage Service)、s3n(s3n://)、
HDFS (hdfs://
)
要使用预编译好的二进制包:
- 从这里(download page)下载Spark二进制包
- 将Spark二进制包上传到可访问的的存储:hdfs/http/s3
要存到HDFS上,可以使用Hadoop fs put 命令:hadoop fs -put spark-1.6.0.tar.gz /path/to/spark-1.6.0.tar.gz
或者,你也可以编译一个自定义Spark版本,可以使用Spark源码中tarball/checkout下的 make-distribution.sh脚本来打包:
- 下载并编译Spark(参考这里here)
- 创建一个二进制包:make-distribution.sh –tgz
- 将Spark二进制包上传到可访问的存储:hdfs/http/s3
Mesos Master URL
Mesos Master URL有两种形式:
- 单master为 mesos://host:5050
- 基于Zookeeper的多master为 mesos://zk://host:2181
客户端模式
客户端模式下,客户端机器上将会启动一个Spark Mesos框架,并且会等待驱动(driver)的输出。
驱动需要spark-env.sh中的一些配置项,以便和Mesos交互操作:
- 在Spark-env.sh中设置一些环境变量:
- export MESOS_NATIVE_JAVA_LIBRARY=<path to libmesos.so>。一般情况下,这个路径是 <prefix>/lib/libmesos.so,其中prefix的默认值是/usr/local。见前面的Mesos安装所述。在Mac OS X系统中,libmesos.so的名字变为libmesos.dylib。
- export SPARK_EXECUTOR_URI=<上文所述的上传spark-1.6.0.tar.gz对应的URL>
- 同样,spark.executor.uri 也需要设成<上文所述的上传spark-1.6.0.tar.gz对应的URL>
然后,你就可以向这个Mesos集群提交Spark应用了,当然,你需要把Mesos Master URL(mesos:// )传给SparkContext,例如:
val conf = new SparkConf()
.setMaster("mesos://HOST:5050")
.setAppName("My app")
.set("spark.executor.uri", "<path to spark-1.6.0.tar.gz uploaded above>");
val sc =newSparkContext(conf);
(你也可以在conf/spark-defaults.conf文件中配置spark.executor.uri,然后通过 spark-submit
脚本来提交Spark应用)
如果是在Spark shell中,spark.executor.uri参数值是从SPARK_EXECUTOR_URI继承而来的,所以不需要额外再传一个系统属性。
./bin/spark-shell --master mesos://host:5050
集群模式
Mesos同样也支持Spark以集群模式提交作业,这种模式下,驱动器将在集群中某一台机器上启动,其运行结果可以在Mesos Web UI上看到。
要使用集群模式,你首先需要利用 sbin/start-mesos-dispatcher.sh脚本启动 MesosClusterDispatcher,并且将Mesos Master URL(如:mesos://host:5050)传给该脚本。MesosClusterDispatcher启动后会以后台服务的形式运行在本机。
如果你想以Marathon框架运行MesosClusterDispatcher,那么就需要在前台运行MesosClusterDispatcher(如:bin/spark-class org.apache.spark.deploy.mesos.MesosClusterDispatcher)
客户机现在可以向Mesos集群提交任务了,如下示例,你可以用spark-submit脚本,并将master URL指定为MesosClusterDispatcher的URL(如:mesos://dispatcher:7077)。然后,你可以在Spark集群Web UI上查看驱动器程序的状态。
./bin/spark-submit --class org.apache.spark.examples.SparkPi --master mesos://207.184.161.138:7077 --deploy-mode cluster --supervise --executor-memory 20G --total-executor-cores 100 http://path/to/examples.jar 1000
注意,spark-submit中所涉及的jar包或python文件必须传到Mesos可以以URI形式访问到的位置,Spark驱动是不会自动上传任何本地jar包或python文件的。
Mesos运行模式
Spark可以在Mesos的两种模式下运行:“粗粒度”模式(默认)和“细粒度”模式。
“粗粒度”模式下,Mesos在每台机器上只启动一个长期运行的Spark任务,而Spark任务则会作为其内部的“mini-tasks”来动态调度。这样做的好处是,启动延迟会比较低,但同时,也会增加一定的资源消耗,因为Mesos需要在整个生命周期内为这些长期运行的Spark任务保留其所需的资源。
粗粒度是Mesos的默认模式。你也可以显示地在 SparkConf 中设置spark.mesos.coarse属性为true来启用该模式:
conf.set("spark.mesos.coarse","true")
另外,在粗粒度模式下,你可以控制Spark所使用资源的上限。默认情况下,Spark会申请集群中所有的CPU(当然是Mesos所能提供的),这其实并不合理,除非你每次只启动一个应用。你可以通过SparkConf设置个帽子(上限),如:conf.set(“spark.cores.max”, “10)
在细粒度模式下,每个Spark任务都作为独立的Mesos任务运行。这使得多个Spark实例(或者其他计算框架)可以比较细粒度地共享机器资源,每个应用所获得的机器资源也会随着应用的启动和关闭而增加或减少,但同时每个任务的启动也会有相应的延迟。这种模式可能不适用于一些低延迟的场景,如:交互式查询,响应web请求等。
要使用细粒度模式,可以在SparkConf 中将 spark.mesos.coarse 属性设为false:
conf.set("spark.mesos.coarse","false")
你还可以用spark.mesos.constraints属性来设置mesos资源分配约束条件。默认是没有约束的,也就是所有的资源分配都是可接受的。
conf.set("spark.mesos.constraints","tachyon:true;us-east-1:false")
例如,我们把spark.mesos.constraints设为tachyon:true;us-east-1:false,接下来,资源分配将会检查新分配的资源(机器)是否符合这两项约束,只有符合约束的机器才会启动新的Spark执行器(executor)。
Mesos对Docker的支持
Spark可以使用Mesos Docker集装箱,你需要在 SparkConf 中设置spark.mesos.executor.docker.image属性。
所使用的Docker镜像必须已经包含特定的Spark版本,或者也可以使用Mesos的惯用方法下载Spark。
支持Docker需要Mesos 0.20.1及以上版本。
和Hadoop一起运行
你可以将Spark和Mesos作为机器上独立服务,和已有的Hadoop集群部署在一起。要从Spark访问Hadoop数据,需要一个完整的hdfs:// URL(通常是 hdfs://<namenode>:9000/path,你可以在Hadoop Namenode Web UI上看到这个URL)。
另外,你也可以在Mesos上运行Hadoop MapReduce,以便达到Mesos和Hadoop之间更好的资源隔离和共享。这种场景下,Mesos就是一个全局唯一的调度器,同时为Hadoop和Spark分配CPU,而不再是利用Linux调度器来共享CPU资源。更详细的请参考Hadoop on Mesos。
但是不管是上面哪种场景,HDFS始终是独立于Hadoop MapReduce的,也不需要Mesos的调度。
Mesos中的动态资源分配
Mesos在粗粒度模式下,可以支持动态分配,Mesos会基于应用的统计信息调整执行器(executor)的个数。粗粒度模式下,由于Mesos被设计成在每个slave上只启动一个执行器,所以Mesos的粗粒度调度器本身只支持减少资源分配,而动态分配同时支持减少和增加执行器的个数。所以在粗粒度调度器减少执行器个数后,如果Spark表示需要更多执行器的时候,Mesos又会重新把执行器个数提升为原值。
由于Mesos目前还不支持对其他框架作业终止的通知,所以想要使用动态分配这个特性的用户,需要开启Mesos Shuffle Service,来清理shuffle产生的临时数据。可以使用sbin/start-mesos-shuffle-service.sh 和 sbin/stop-mesos-shuffle-service.sh 来启动和停止Mesos Shuffle Service。
Shuffle Service需要在每个运行Spark执行器(executor)的节点上启动。最简单的实现方式就是在Mesos上使用Marathon启动Shuffle Service,并添加一个主机唯一的限制。
配置
Spark更详细的配置可参考这里:configuration page 。以下为Spark on Mesos的相应配置:
Spark Properties(Spark属性)
属性名 | 默认值 | 含义 |
---|---|---|
spark.mesos.coarse |
false | 如果设为true,则运行于Mesos集群粗粒度模式下(“coarse-grained” sharing mode),这种模式下,Spark需要运行一个长期Mesos任务。这样可以有效减少小型查询的延迟,但同时也会增加整个Spark作业生命周期内对集群资源的占用。 |
spark.mesos.extra.cores |
0 |
每一个task的额外CPU资源请求。仅在粗粒度模式下生效。每个task所申请的CPU个数 = 集群提供的个数 + 该配置所设定的个数。注意,执行器锁申请的总CPU个数不会超过 spark.cores.max |
spark.mesos.mesosExecutor.cores |
1.0 |
每个Mesos执行器的CPU个数(仅细粒度模式有效)。这不包括用于执行Spark tasks的CPU。换句话说,即使没有任何Spark任务在运行,每个Mesos执行器也会占用这么多个CPU。注意,该值可以是一个浮点数。 |
spark.mesos.executor.docker.image |
(none) | 容纳Spark执行器的docker镜像名称。这个镜像中必须包含Spark以及兼容的Mesos库。镜像中的Spark安装路径可以由spark.mesos.executor.home指定;Mesos库的安装路径可以由 spark.executor.Env.MESOS_NATIVE_JAVA_LIBRARY指定。 |
spark.mesos.executor.docker.volumes |
(none) | 设置挂载到docker镜像中的卷标(docker镜像由spark.mesos.executor.docker.image设置)。该属性值为逗号分隔的卷标路径映射列表,其格式同docker run -v所需参数,如下:
[host_path:]container_path[:ro|:rw] |
spark.mesos.executor.docker.portmaps |
(none) | 设置docker镜像的接入端口映射列表(docker镜像由spark.mesos.executor.docker.image设置)。格式为逗号分隔的端口映射列表,如下所示:
host_port:container_port[:tcp|:udp] |
spark.mesos.executor.home |
driver side SPARK_HOME |
设置Mesos执行节点上的Spark安装目录。默认情况下,会使用驱动器(driver)所在节点的${SPARK_HOME}路径。注意,如果设置了spark.executor.uri指定Spark二进制包的位置,那么本设置将无效。 |
spark.mesos.executor.memoryOverhead |
执行器内存 * 0.10 或384MB中较大者 | 每个执行器(executor)分配的额外内存总量(MB)。其默认值为 384MB或者 spark.executor.memory * 10% 二者中较大者。 |
spark.mesos.uris |
(none) | 驱动器(driver)或执行器(executor)启动时下载到沙箱中的URI列表。粗粒度和细粒度模式均适用。 |
spark.mesos.principal |
(none) | 设置Spark框架在Mesos集群上的认证身份。 |
spark.mesos.secret |
(none) | 设置Spark框架在Mesos集群上的认证密码。 |
spark.mesos.role |
* |
设置Spark框架在Mesos集群上的认证角色。角色是Mesos用于保留和分配资源的权重因子。 |
spark.mesos.constraints |
(none) | 基于资源特性的资源分配约束条件。默认,所有分配的资源都是可以接受的。更详细的资源特性请参考这里:Mesos Attributes & Resources
|
排错和调试
调试时需要关注的地方:
Mesos master(端口:5050)
- Slave节点应该全部显示在slaves tab页上
- Spark应用应该显示在frameworks tab页上
- Spark任务应该显示在framework detail中
- 检查失败任务沙箱的标准输出和标准错误
- Mesos日志
- 默认,master和slave的日志都保存在/var/log/mesos目录下
常见陷阱:
- Spark程序集不可访问
- Slave机器必须能够通过你指定的http://, hdfs:// 或者 s3n:// 等URL访问并下载Spark的二进制包
- 防火墙阻断网络通信
- 检查是否有连接失败消息
- 临时关闭防火墙以便调试,后续再专门放开几个端口