本教程源于2016年3月出版书籍《Spark原理、机制及应用》 ,在此以知识共享为初衷公开部分内容,如有兴趣,请支持正版书籍。
Spark为使用者提供了大量的工具和脚本文件,使得其部署与开发变得十分方便快捷,本章将会分别从运行(含集群部署)、开发以及源码编译三个角度,来介绍Spark相关环境的具体配置流程。对于初次接触Spark的读者,建议仅需阅读运行环境部署和开发环境部署两节内容,如果后期有源码编译或者源码学习需求,再回头来阅读相关章节。
2.1 Spark运行环境配置
Spark能够运行在Windows或者Unix Like(如 GUN/Linux、OS
X等)平台上。一般情况下,Windows仅作为 Spark的开发平台,读者如果希望把 Spark应用于生产环境,尤其是需要部署集群时,更建议选择Linux平台,本章后续所有内容也都基于Linux发行版Ubuntu
14.04 LTS,其他Linux发行版的操作与之类似。
2.1.1 先决条件
Spark使用Scala语言编写,运行在Java虚拟机(JVM)当中,因此需要在系统中安装Java
6或者更高版本,考虑到后期的开发需求,建议读者直接从Oracle官网上(http://www.oracle.com/technetwork/java/javase/downloads/index.html)下载合适的JDK版本安装,并将JAVA_HOME环境变量的值设置为Java的安装目录,PATH环境变量添加Java安装目录下的bin目录。安装完毕之后执行如下命令检查JDK是否已经被正确安装,命令返回结果会因安装JDK版本而异。
$ javac -version
javac 1.7.0_79
如果读者希望能够使用R语言编写Spark程序,那么还需要在本地安装R解释器,在Ubuntu下可以通过执行如下命令安装:
sudo apt-get update && sudo apt-get install r-base
Spark 控制脚本依赖安全外壳协议(Secure Shell,SSH) 来执行对整个集群的操作,如启动、停止整个集群。如搭建集群,需要在主节点上安装好 SSH 客户端,所有节点上安装好 SSH 服务端。Ubuntu 环境下,可以通过以下命令安装 OpenSSH
Client和OpenSSH Server(OpenSSH是SSH的一种开源实现)。
$ sudo apt-get update && sudo apt-get install openssh-client openssh-server
2.1.2 下载与运行 Spark
1. 下载Spark安装包
Spark官方(http://spark.apache.org/downloads.html)提供了Spark二进制包和源码的下载,页面如图2-1所示,如无特殊需求,此处建议读者直接下载预先编译好的二进制包。此外,Spark支持读写HDFS中的文件,读者如果希望后期使用HDFS来做数据存储,需要根据Hadoop的版本选择合适的预构建版本,若未能找到对应自己Hadoop版本的Spark安装包,可以考虑自己手动编译源码,本书会在第2.3节中具体介绍 Spark 源码编译的流程。
下载压缩包到本地目录中,执行如下命令进行解压缩,其中1.x.y为Spark版本号,z.w为Hadoop版本号。
tar -zxvf spark-1.x.y-bin-hadoopz.w.tgz
图2-1 Spark下载页面
2. 安装与运行测试应用
进入解压缩得到的Spark安装目录,执行命令“./bin/run-example SparkPi 10”运行Spark提供的Example程序,该程序用于计算π值。执行结果如下图2-2所示,可以看到Spark计算得到的PI值约等于3.139516。
图2-2 Example程序运行结果
运行Example程序时候如果系统报错提示找不到 java 文件,说明JDK未能被正确安装,或者环境变量没有正确配置。
3. 设置环境变量
在使用 Spark之前,建议读者将Spark的bin目录加入到系统PATH环境变量当中,这样以后在执行bin目录下的程序时,可以直接使用程序名,例如“spark-shell”,而无需指定程序的路径,如“./bin/spark-shell”,具体设置步骤如下:
1)以管理员权限编辑文件/etc/profile,在文件尾部加入如下语句,保存并退出。其中/path/to/spark需要替换成用户自己完整的Spark安装路径。
export SPARK_HOME="/path/to/spark/"
export PATH="$SPARK_HOME/bin:$PATH"
2)执行指令“source /etc/profile”,完成后在任意目录下执行命令“spark-shell –help”,若没有提示命令找不到,则说明设置成功。
3. 设置环境变量
在使用 Spark之前,建议读者将Spark的bin目录加入到系统PATH环境变量当中,这样以后在执行bin目录下的程序时,可以直接使用程序名,例如“spark-shell”,而无需指定程序的路径,如“./bin/spark-shell”,具体设置步骤如下:
1)以管理员权限编辑文件/etc/profile,在文件尾部加入如下语句,保存并退出。其中/path/to/spark需要替换成用户自己完整的Spark安装路径。
export SPARK_HOME="/path/to/spark/"
export PATH="$SPARK_HOME/bin:$PATH"
2)执行指令“source /etc/profile”,完成后在任意目录下执行命令“spark-shell –help”,若没有提示命令找不到,则说明设置成功。
2.1.3 使用交互式 Shell
Spark提供了基于Scala、Python以及R语言的三种交互式Shell。执行命令“spark-shell”可启动Spark
Scala Shell,其界面如图2-3所示。
图2-3 Spark Scala Shell启动界面
如果启动Shell时系统报错提示“java_home is not set”,说明JAVA_HOME环境变量未被正确配置。
读者可以尝试在Shell中运行一个简单程序,该程序用于输出/etc/passwd这一文件的行数。输入完一行回车就能立即看到执行结果。输出结果可能会因不同的系统环境而异。
>>> val lines = sc.textFile("/etc/passwd")
15/08/05 15:55:14 INFO MemoryStore: ensureFreeSpace(88456) called with curMem=0, maxMem=280248975
// 省略大部分日志输出
15/08/05 15:55:14 INFO SparkContext: Created broadcast 0 from textFile at <console>:21
>>> lines.count
// 省略大部分日志输出
res0 Long = 25
执行命令“pyspark”可启动Spark Python Shell,其界面如图2-4所示。
图2-4 Spark Python Shell启动界面
行数统计程序的Python版本如下:
>>> lines = sc.textFile("/etc/passwd")
15/08/05 15:59:43 INFO MemoryStore: ensureFreeSpace(157248) called with curMem=0, maxMem=280248975
# 省略大部分日志输出
15/08/05 15:59:43 INFO SparkContext: Created broadcast 0 from textFile at NativeMethodAccessorImpl.java:-2
>>> lines.count()
# 省略大部分日志输出
25
执行命令“sparkR”可以启动Spark R Shell,其界面如图2-5所示。
图2-5 Spark R Shell启动界面
行数统计程序的 R 语言版本如下:
> lines <- SparkR:::textFile(sc, "/etc/passwd")
# 15/08/07 13:10:42 INFO MemoryStore: ensureFreeSpace(157288) called with curMem=343050, maxMem=280248975
# 省略大部分日志输出
# 15/08/07 13:10:42 INFO SparkContext: Created broadcast 2 from textFile at NativeMethodAccessorImpl.java:-2
> count(lines)
# 省略大部分日志输出
[1] 25
所有交互式Shell在无输入字符情况下按下<Ctrl+D>(表示EOF,Windows下使用<Ctrl+Z>),可以退出Shell界面。Scala
Shell、Python Shell输入指令“exit()”,R
Shell输入“q()”,之后回车,具有同样的效果。
2.1.4 搭建Spark Standalone集群
Spark支持三种集群资源管理器,分别是Standalone、Yarn和Mesos,其中Standalone为Spark自带,无需用户再额外安装和部署其他的资源管理器。本章会以Standalone为例,介绍如何搭建和管理一个Spark集群,对Yarn和Mesos感兴趣的读者可以参考官方提供的文档。
1. SSH配置
为了实现无缝工作,安装SSH的客户端与服务端完毕之后,需要允许master节点能够无需键入密码即可登录集群内的所有机器,可以通过创建公钥和私钥对来实现。在master节点上,执行命令“ssh-keygen
-t dsa”生成SSH私钥,如果生成过程中需要输入,点击回车键即可。执行结果如图2-6所示。
图2-6 生成SSH私钥
接下来使用ssh-copy-id命令将私钥加入到所有节点(包括master节点本身)的 .ssh/authorized_keys文件中,例如可以使用命令“ssh-copy-id
[email protected]”实现到slave1节点上Spark用户的无密码登录,执行结果如图2-7所示。
图2-7 实现无密码登入
2. Spark配置文件
master节点上,进入Spark安装目录下的conf目录,使用如下命令得到slaves和spark-env.sh文件。
$ cp slaves.template slaves
$ cp spark-env.sh.template spark-env.sh
编辑spark-env.sh文件,在文件末尾加入“export JAVA_HOME="/path/to/java”行,其中读者需要把“/path/to/java”替换成自己实际的Java安装目录。继续编辑slaves文件,加入所有工作节点的主机名,每个节点占据一行。保存所有编辑过的文件。
3. 拷贝Spark目录
接下来把配置好的Spark目录拷贝到所有节点上,目录所存放的位置在所有节点上都必须是一致的。可以使用scp命令来完成此项工作,下面所示为一条示例命令,用于master节点上的spark-1.4.1-bin-hadoop2.6目录拷贝到slave1节点上的/home/spark/spark目录下,读者可以根据实际情况来修改。如果节点数目比较多的话,建议读者编写一个Shell脚本来完成此项任务。
$ scp -r spark-1.4.1-bin-hadoop2.6 [email protected]:/home/spark/spark/
1. 运行与关闭Standalone集群
回到Spark的安装目录,执行命令“sbin/start-all”启动集群上的所有节点即可。集群启动完毕后,打开浏览器,访问http://masternode:8080,其中masternode需要替换成自己master节点的真实IP或者主机名,可以看到如下Web界面,该页面会显示集群中的所有节点的信息,以及曾经运行、正在运行的应用程序的相关情况。
图2-8 Web监控界面
读者如果想关闭集群,切换到Spark的安装目录,执行命令“sbin/stop-all.sh”即可,Spark会自动关闭正在运行的所有节点。
5. 运行Example程序
执行如下命令可以向搭建好的Spark集群提交之前曾经运行过的Example程序,其中masternode需要替换成读者自己master节点的主机名。spark-submit命令的使用会在下一节中具体介绍。
$ spark-submit --master spark://masternode:7077 examples/src/main/python/pi.py
此时刷新Spark Web界面,可以看到程序的运行情况,如图2-9所示。
图2-9 程序运行情况界面
2.2 Spark开发环境配置
在上一节中介绍了如何使用Spark Shell来执行Spark语句,可以感受到,由于交互式Shell能够立即返回语句执行的结果,因此非常适合用于做即席查询等数据分析任务,此外, Spark还支持让开发者去编写独立的应用程序, Spark提供了基于Scala、Java、Python以及R等编程语言的API,本节会介绍如何调用这些接口去编写程序,如何编译和构建项目,以及如何提交应用程序给Spark去运行。
2.2.1 Spark独立应用程序
Spark项目的构建不依赖于用户的操作系统中是否已经安装了Spark,开发者一般会采用项目构建工具来辅助完成项目的构建、源码的编译、依赖的解决等工作,通常情况下开发者会采用Java + Maven,Scala + SBT的搭配组合(Maven基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。SBT是对scala或Java语言进行编译的一个工具,类似于Maven)。Python和R本身是解释型语言,不需要使用项目构建工具来辅助生成二进制文件或者字节码文件,因此直接将脚本文件提交给Spark运行即可。
不同语言编写的Spark应用所依赖的编译器 / 解释器 / 项目构建工具如表2-1所示,读者可根据自己需求自行去官方网站下载,并按照官方文档指示安装应用,在此不做过多介绍。
表2-1 构建不同语言的Spark独立应用所依赖工具
语言 |
编译器 / 解释器 |
项目构建工具 |
Scala |
Scala 2.10.x |
SBT 0.13.0+ |
Java |
Java 6+ |
Apache Maven 3.0.4+ |
Python |
Python 2.6+ |
|
R |
R 3.1+ / Java 6+ |
在上述的工作安装完毕之后,就可以利用其中的工具开始编写不同语言的Spark独立应用。
1. spark-submit工具
spark-submit命令用于部署一个应用程序到Spark集群当中运行,在后面还会使用该命令提交Java、Scala项目导出的Jar包和Python项目的脚本文件,R脚本文件则使用单独的sparkR命令提交,后面会在编写R语言程序部分单独进行介绍。
spark-submit命令的格式如下所示。
./bin/spark-submit \
--class <main-class>
--master <master-url> \
--deploy-mode <deploy-mode> \
--conf <key>=<value> \
... # other options
<application-jar> \
[application-arguments]
其中,常用的Option及其含义如表2-2所示,更为具体选项请参考“spark-submit --help”命令的输出结果,在此不一一列出。
表2-2 spark-submit命令常用的Option选项及其含义
Option |
含义 |
--classs |
入口类名,对于Java和Scala程序是包含 main() 函数的类的名字,Python程序则无需指定该选项 |
--master |
集群master节点的地址,该选项的具体可选值会在表2-3中详细说明 |
--name |
应用程序名,该值会在Spark Web页面和Spark日志文件中被用于标识该应用程序 |
--deploy-mode |
程序部署模式,可选值为“client”和“cluster”,“client”为默认值。若设置为“client”,Apache Spark会在执行spark-submit命令的机器上启动Driver程序。若设置为“cluster”,则Spark会在其中一台工作节点上运行Driver程序 |
-jars |
指定第三方依赖 Jar 包,如有多个 Jar 包则以英文逗号分隔开,文件会被上传到每一个节点的 CLASS_PATH 路径下 |
--files |
指定需要被上传的文件,如果多个文件则以英文逗号分隔开,文件会被上传到每一个节点的程序运行目录下 |
--executor-memory |
指定为 executor 进程分配的内存大小(如 1000m,2g 等),默认值为 1g |
--driver-memory |
指定为 Driver 进程进行分配的内存大小(如 1000m,2g 等),默认值为 512m |
--py-files |
类似于 --jars 参数,--py-files 用于上传依赖的 .py、.zip 和 .egg 文件。如有多个文件则以逗号隔开,有多个 py 文件则建议打包成 .zip 或者 .egg 文件 |
--master 选项可选值如表2-3所示。
表2-3 spark-submit命令 –master选项可选参数值
Option |
含义 |
local |
以本地单线程方式运行 Spark,由于只有一个线程,无法实现任务的并行处理 |
local[N] |
以本地 N 个线程方式运行 Spark,可用于模拟集群 |
local[*] |
以本地模式运行,线程数等于机器内核数(core number) |
spark://host:port |
连接到指定的 Standalone 集群来运行 Spark 程序 |
yarn-client |
连接到Yarn集群,以Client模式来运行Spark程序。集群的相关信息,可以通过 HADOOP_CONF_DIR环境变量所指向目录(即Apache Hadoop配置目录)内的配置文件中找到 |
yarn-cluster |
连接到Yarn集群,以Cluster模式来运行Spark程序。集群的相关信息,可以通过 HADOOP_CONF_DIR环境变量所指向目录(即Apache Hadoop配置目录)内的配置文件中找到 |
mesos://host:port |
连接到 Mesos 集群来运行 Spark 程序 |
Apache Spark程序内部通过SparkConf配置的属性值会拥有最高的优先级,其次才传递给spark-submit命令参数中的配置项,在后面的示例程序中会看到如何通过SparkConf来配置配置项。
2. Scala独立应用程序
读者可以使用自己喜欢的编辑器,编写如下代码,保存成LineCount.scala文件,将之前在Shell中执行的Line
Count程序重新改写成独立应用程序,代码如下。
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.SparkContext._
object LineCount{
def main(args: Array[String]) {
/* 创建 SparkContext 实例 */
val conf = new SparkConf().setAppName("Line Count Program")
val sc = new SparkContext(conf)
/* 统计行数 */
val lines = sc.textFile("/etc/passwd");
println(lines.count)
}
}
接下来编写build.sbt文件,用于定义项目,代码如下。
name := "Line Count"
version := "1.0"
scalaVersion := "2.10.4"
libraryDependencies += "org.apache.spark" %% "spark-core" % "1.4.1"
为了保证SBT能够正常运作,需要把LineCount.scala与build.sbt文件放在如下项目目录结构内。
.
├── build.sbt
└── src
└── main
└── scala
└── LineCount.scala
进入项目根目录,执行命令“sbt clean package”开始构建项目,SBT会开始构建项目,并将src/main/scala编译出来的class文件打包成一个Jar文件,期间,SBT会自动从仓库中下载编译与运行程序所依赖的Jar包,所用时间可能会比较长。构建完成后会在 target/scala-2.10/ 目录下自动生成所需要的Jar文件。
通过spark-submit命令,将刚刚生成的Jar包提交给Spark运行。
$ spark-submit \
--class LineCount \
--master local \
./target/scala-2.10/line-count_2.10-1.0.jar
运行结果如图2-10所示。
图2-10 运行结果图
3. Java独立应用程序
编写如下代码,保存成LineCount.java文件。
import org.apache.spark.api.java.*;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.Function;
public class LineCount {
public static void main(String[] args) {
/* 创建 SparkContext 实例 */
SparkConf conf = new SparkConf().setAppName("Line Count Program Java Version").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
/* 统计文件行数 */
JavaRDD<String> lines = sc.textFile("/etc/passwd");
System.out.println(lines.count());
}
}
编写pom.xml文件,该文件的作用类似于之前的build.sbt文件,代码如下。
<project>
<groupId>me.ihainan</groupId>
<artifactId>line-count</artifactId>
<modelVersion>4.0.0</modelVersion>
<name>Line Count</name>
<packaging>jar</packaging>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
</project>
把LineCount.java与pom.xml文件放在如下文件目录结构内,与SBT类似,在项目根目录执行指令“mvn
clean package”构建项目,项目构建完毕后会自动在target目录下生成对应的Jar包。
.
├── pom.xml
└── src
└── main
└── java
└── LineCount.java
执行下面所示命令,将生成的Jar包提交给Spark执行。
spark-submit \
--class "LineCount" \
--master local \
target/line-count-1.0.jar
4. Python独立应用程序
编写如下所示代码,保存成LineCount.py文件。
# -*- coding:utf-8 -*-
from pyspark import SparkContext
# 创建 SparkContext 实例
sc = SparkContext("local", "LineCount")
# 统计文件行数
lines = sc.textFile("/etc/passwd")
print lines.count()
Python脚本无需编译,因此只需要切换到 LineCount.py 文件所在目录,使用spark-submit来提交程序。
$ spark-submit \
--master local \
LineCount.py
执行结果如图2-11所示。
图2-11 Python提交程序运行结果
5. R独立应用程序
编写如下所示代码,保存成LineCount.R文件。
library(SparkR)
sc <- sparkR.init(appName="Line Count")
lines <- SparkR:::textFile(sc, "/etc/passwd")
print(count(lines))
sparkR.stop
切换到LineCount.R文件所在目录,执行如下命令提交R程序。
sparkR LineCount.R local
执行结果如图2-12所示。
图2-12 R程序执行结果图
2.2.2 构建IDE开发环境
在实际的项目开发过程中,建议读者使用一个自己熟悉的IDE(集成开发环境)来管理与构建项目,JetBrains公司推出的IntelliJ
IDEA(简称 IDEA)是一个很不错的选择。IDEA跨平台,可原生运行在Windows、Linux以及OS
X等操作系统之上;社区版免费提供给开发者使用,足以对于一般的需求;内置对 Maven的支持;通过安装插件可实现对Scala、Python、SBT的额外支持。IDEA的诸多特性能够极大提高Spark程序员的开发效率。本节的最后会介绍如何使用IDEA来构建与运行一个基于Scala语言编写的项目。
1. 下载与安装 IntelliJ IDEA
IDEA提供旗舰版和社区版两个版本给用户下载,如图2-13所示。其中旗舰版售价为499美元,社区版则免费向开发者供应。社区版所提供的功能基本能够满足 Spark的开发需求,在此建议读者下此版本,并按照官方说明安装好IDEA。
图2-13 IDEA下载界面
2. 安装Scala插件
打开 IDEA,依次点击菜单栏的File → Setting,打开IDEA的设置界面。点击左侧的Plugins标签,按下左下角的Install
JetBrains plugin按钮,在新窗口上方的搜索栏输入Scala,单击出现的搜索结果,并点击右侧的Install plugin按钮,如下图所示。等待IDEA自动下载和安装Scala插件,安装完毕后重启IDEA即可。
图2-14 安装Scala插件
3. 创建SBT项目
重新启动IDEA后,菜单栏File → New → Project新建项目,相比安装Scala插件之前,会多出创建Scala项目选项。点击Scala标签,选择创建SBT项目,点击下一步,设置向导页面填写项目名和项目路径,Scala
Version选择2.10.x,勾选中Use auto-import选项,点击 Finish按钮。如图2-15和2-16所示。
图2-15 创建SBT项目
图2-16 配置新项目信息
创建项目完毕后,IDEA会自动加载项目并下载相关依赖包,右下角会提示“1 process running”,此时项目窗口显示的项目目录内容需等待项目加载完毕之后才会完整显示。
4. 构建项目
编辑项目根目录下的build.sbt文件,在底部加上语句“libraryDependencies += "org.apache.spark" %% "spark-core" % "1.4.1"”,保存。由于之前创建项目时勾选了Use
auto-import选项,IDEA会自动下载相关依赖包,如图2-17所示,此时等待依赖下载和解析完毕。
图2-17 构建项目
接下来创建一个Scala程序。右键src → main → scala目录,弹出来的选单中选择 New
→ Scala Class,如图2-18所示。输入类名,选择创建 Object,回车,在新出来的文本编辑窗口中编写 Scala 代码。
图2-18 创建Scala程序
编写完毕后,右键单击main函数,弹出来选单中选择Run ‘LineCount’,如图2-19所示,IDEA会自动创建一个应用,把main函数当做应用入口,并运行程序。
图2-19 运行Scala程序
程序执行结果如图2-20所示。
图2-20 Scala程序运行结果
5. 导出Jar包
菜单栏点击File → Project Structure,弹出窗口的侧边栏中点击Artifaces,点击加号,选中From
modules with dependencies,弹出窗口中选择自己编写的类,点击确定,选择输出Jar包的位置,回车即可。详细操作如图2-21、图2-22和图2-23所示。完成后菜单栏依次点击Build
→ Build Artifaces,编译生成Jar包,此后就能使用spark-submit来提交生成的Jar包。
图2-21 输出Jar包
图2-22 配置输出信息
图2-23 配置输出信息
2.3 Spark编译环境配置
后续更新
本教程源于2016年3月出版书籍《Spark原理、机制及应用》 ,在此以知识共享为初衷公开部分内容,如有兴趣,请支持正版书籍。