在 Azure HDInsight 中安装和使用 Spark
Spark本身用Scala语言编写,运行于Java虚拟机(JVM)。只要在安装了Java 6以上版本的便携式计算机或者集群上都可以运行spark。如果您想使用Python API需要安装Python解释器(2.6或者更高版本),请注意Spark暂不支持Python 3。
下载Spark
首先下载Spark并解压,我们从下载预编译版本的Spark开始。在浏览器中访问 http://spark.apache.org/down loads.html 选择"Pre-built for Hadoop 2.4 and later"安装包,点击"Direct Download"下载名称为spark-1.2.0-bin-hadoop2.4.tgz 的压缩包。
Windows用户安装时可能会遇到文件夹名称中包含空格的问题,建议Spark的安装目录的文件夹中不包含空格,比如C:\spark 。
您不需要安装Hadoop即可运行Spark,但是如果您已有Hadoop集群或者HDFS则需要下载对应的Spark版本。您可在 http:// spark.apache.org/downloads.html 选择不同的安装包,这些安装包的文件名会有所不同。也可以将Spark源码重新编译,您可在 Github 下载最新的Spark源代码。
大多数Unix和Linux操作系统,包括Mac OS X,都包含tar命令行解压工具。如果您的操作系统没有安装tar的命令行工具,请在互联网搜索免费的解压缩工具。比如在Windows系统中您可以使用7-Zip。
现在我们将已下载的Spark解压缩,看看默认的Spark分布式。打开终端,切换至下载Spark的目录下将其解压缩。执行下面的代码将创建一个与压缩文件同名的新目录。
cd ~
tar -xf spark-1.2.0-bin-hadoop2.4.tgz
cd spark-1.2.0-bin-hadoop2.4
ls
在包含tar的执行命令中,x表示解压缩,f表示指定tar包名称。ls 命令将列出Spark目录下的所有文件。让我们简要介绍下Spark目录中的重要文件。
README.md
包含Spark入门的简要说明。
bin
包含与Spark交互的可执行文件(比如在本章后面介绍的Spark Shell)
core, streaming, python, …
包含Spark工程主要组件的源码
examples
包含可在Spark单机版运行的作业,您可从中了解Spark API。
您不必对Spark工程中包含的如此多的目录和文件所困扰,本书后续章节会涵盖其中的大部分技术内容。现在,让我们深入Spark的Python和Scala 交互式shell。我们将从运行Spark官方示例开始,然后编写和运行自己的Spark作业。
本章中的Spark作业运行于单机模式,即在本地计算机运行的非分布式的模式。Spark可在不同模式不同环境中运行。除了单机模式,Spark还可运行于Mesos和YARN,以及Spark分布式下的独立调度。我们将在第七章中详细介绍各种部署模式。
在 HDInsight 中安装Spark
We will use a Script Action custom script to install Spark on an HDInsight cluster. This script can install Spark 1.2.0 or Spark 1.0.2 depending on the version of the HDInsight cluster you provision.
-
If you use the script while provisioning an HDInsight 3.2 cluster, it installs Spark 1.2.0.
-
If you use the script while provisioning an HDInsight 3.1 cluster, it installs Spark 1.0.2.
You can modify this script or create your own script to install other versions of Spark.
A sample script to install Spark on an HDInsight cluster is available from a read-only Azure storage blob at https://hdiconfigactions.blob.core.windows.net/sparkconfigactionv03/spark-installer-v03.ps1. This section provides instructions on how to use the sample script while provisioning the cluster by using the Azure portal.
NOTE:
The sample script works only with HDInsight 3.1 and 3.2 clusters. For more information on HDInsight cluster versions, see HDInsight cluster versions.
-
Start provisioning a cluster by using the CUSTOM CREATE option, as described at Provisioning a cluster using custom options. Pick the cluster version depending on the following:
-
If you want to install Spark 1.2.0, provision an HDInsight 3.2 cluster.
-
If you want to install Spark 1.0.2, provision an HDInsight 3.1 cluster.
-
On the Script Actions page of the wizard, click add script action to provide details about the script action, as shown below:
roperty
Value
Name
Specify a name for the script action. For example, Install Spark.
Script URI
Specify the Uniform Resource Identifier (URI) to the script that is invoked to customize the cluster. For example, https://hdiconfigactions.blob.core.windows.net/sparkconfigactionv03/spark-installer-v03.ps1
Node Type
Specify the nodes on which the customization script is run. You can choose All nodes, Head nodes only, or Worker nodes only.
Parameters
Specify the parameters, if required by the script. The script to install Spark does not require any parameters so you can leave this blank.
You can add more than one script action to install multiple components on the cluster. After you have added the scripts, click the checkmark to start provisioning the cluster.
You can also use the script to install Spark on HDInsight by using Azure PowerShell or the HDInsight .NET SDK.
Spark的 Python 和 Scala 交互式Shell
Spark 的 交 互式shell支持可执行的数据分析。如果您使用其他的shell编程,那么您将会对Spark shell感觉很亲切。比如R、Python和Scala shell,以及批处理的操作系统编程或者Windows命令提示符。
与其他的Shell编程只能操作单台计算机的磁盘和内存不同的是,Spark Shell支持跨多台计算机的分布式磁盘和内存计算,并且Spark会自动执行分布式作业处理。
因为Spark将数据加载至工作节点内存中,绝大多数分布式计算甚至处理TB级的数据也仅需几秒钟。这使得Spark适合处理迭代排序、随机和未知分析。Spark的Python和Scala的shell均支持集群连接。
让我们用一个简单的数据分析的例子来感受一下spark shell的强大,按照Spark官方文档的快速入门的步骤:
第一步是打开Spark交互式shell。若要打开Python版本的Spark shell,即PySpark shell,在Spark目录中输入如下指令:
bin/pyspark
(或者在Windows中输入bin\pyspark)
打开Scala版本的shell,输入:
bin/spark-shell
shell提示符应在几秒钟后出现。当shell启动时,您会注意到有大量的日志消息提示。您可按下Enter键清除日志输出,图2-1显示的是打开PySpark shell的显示界面。
图 2-1 PySpark shell的默认日志输出
在shell中您可以看到打印的日志信息,您也可以控制日志的详细程度。在conf 目录中创建名称为log4j.properties 的文件,Spark提供了该文件的模板log4j.properties.template 。若不需要输出那么冗长的日志,您可以复制该模板并将其改名为log4j.properties,在模板的复制文件中找到下面的代码:
log4j.rootCategory=INFO, console
降低日志的级别只显示警告信息,将上面的代码修改如下:
log4j.rootCategory=WARN, console
重新打开shell,您可以看见输出信息减少了。
图2-2. PySpark shell 输出信息减少
使用IPython
IPython是颇受python使用者追捧的增强版Python shell,提供诸如tab键完成功能。更多信息请查看 http://ipython.org 。将 IPYTHON的环境变量设置为1即可在Spark中使用IPython。
IPYTHON=1 ./bin/pyspark
To use the IPython Notebook, which is a web-browser-based version of IPython, use:
若要使用基于浏览器的IPython Notebook,请使用如下指令:
IPYTHON_OPTS="notebook" ./bin/pyspark
在Windows中设置变量的方法如下:
set IPYTHON=1 bin\pyspark
在Spark中我们通过操作集群的分布式集合进行自动化并行计算,这些集合被称为弹性分布式数据集,或者RDDs。RDDs是Spark做分布式数据和计算的基础抽象。
在我们说更多的RDD之前,让我们创建一个shell程序读取本地文本文件并计算简单的特定分析。下面的示例2-1是Python语音,示例2-2是Scala语言。
示例2-1. Python line count
>>> lines = sc.textFile("README.md") # Create an RDD called lines
>>> lines.count() # Count the number of items in this RDD
127
>>> lines.first() # First item in this RDD, i.e. first line of README.md u'# Apache Spark'
示例2-2. Scala line count
scala> val lines = sc.textFile("README.md") // Create an RDD called lines lines: spark.RDD[String] = MappedRDD[...]
scala> lines.count() // Count the number of items in this RDD res0: Long = 127
scala> lines.first() // First item in this RDD, i.e. first line of README.md res1: String = # Apache Spark
若要退出shell,按下Ctrl-D。
您会注意到一条信息: INFO SparkUI: Started SparkUI at http://[ipaddress]:4040 。您可以通过此Spark UI看见更多任务和集群的信息。
在示例2-1和2-2中,变量 lines 为RDD,它是在本地机器中读取文本文件后被创建的。我们可以对此RDD运行各种并行操作,比如在数据集(这里指文件中文本的行数)中统计元素的数量,或者打印元素。在后面的章节中我们将深入讨论RDD,在这个之前我们花点时间介绍Spark的基本概念。
Spark核心概念
现在您已经在shell中运行了第一个Spark代码,是时候开始学习更深入的编程了。
每一个Spark应用程序都包含一个在集群上运行各种并行操作的驱动程序,驱动程序包含应用程序的主函数和定义在集群上的分布式数据集。在前面的示例中,驱动程序就是Spark shell本身,您只需输入您想要执行的操作即可。
驱动程序通过一个链接到计算集群上的 SparkContext 对象访问Spark计算集群,在shell中,SparkContext被自动创建为名称是sc的变量,在示例2-3中我们输入sc,则shell显示其类型。
Example 2-3. Examining the sc variable
>>> sc
<pyspark.context.SparkContext object at 0x1025b8f90>
在创建了SparkContext对象之后,您就可创建RDD了。在示例2-1和示例2-2中,我们调用 sc.textFile() 创建一个代表文件中文本行数的RDD。然后,我们就可以在这些行上进行各种操作,例如count()
若要运行这些操作,驱动程序通常管理者多个拥有 executor的工作节点。比如,我们在集群中执行count()操作,不同的机器可能计算lines变量不同的部分。我们只在本地运行Spark shell,则它被执行在单机中,如果我们将shell连接至集群它也可并行的分析数据。示例2-3展示了Spark如何在集群上执行。
图2-3. Components for distributed execution in Spark
Spark 的 API 很大程度上依靠在驱动程序里传递函数到集群上运行。比如,我们扩展上面的README示例,筛选文本中包含的特定关键词"Python"的行,代码如示例2-4(Python),示例2-5(Scala)。
示例2-4 Python filtering example
>>> lines = sc.textFile("README.md")
>>> pythonLines = lines.filter(lambda line: "Python" in line)
>>> pythonLines.first() u'## Interactive Python Shell'
Example 2-5. Scala filtering example
scala> val lines = sc.textFile("README.md") // Create an RDD called lines lines: spark.RDD[String] = MappedRDD[...]
scala> val pythonLines = lines.filter(line => line.contains("Python")) pythonLines: spark.RDD[String] = FilteredRDD[...]
scala> pythonLines.first() res0: String = ## Interactive Python Shell
Spark传递函数 如果您不熟悉示例2-4和2-5中的 lambda表达式 或者 => 语法,那么在此说明其实它是在Python和Scala中的定义内联函数的简短写法。如果您在Spark中使用这些语言,您可定义函数然后将其名称传递给Spark。比如,在Python语言中: def hasPython(line): return "Python" in line pythonLines = lines.filter(hasPython)
Spark传递函数也支持Java语言,但在此情况下传递函数被定义为类,实现调用函数的接口。比如: JavaRDD<String> pythonLines = lines.filter( new Function<String, Boolean>() { Boolean call(String line) { return line.contains("Python"); } } ); Java 8 中介绍了调用了lambda的的简短写法,与Python和Scala很类似。 JavaRDD<String> pythonLines = lines.filter(line -> line.contains("Python"));
We discuss passing functions further in "Passing Functions to Spark" on page 30. 我们将在30页的"Spark传递函数"中深入讨论传递函数。 |
Spark API包含许多魅力无穷的基于函数的操作可基于集群并行计算,比如筛选(filter)操作,我们在后面的章节详细介绍。Spark自动将您的函数传递给执行(executor)节点。因此,您可在单独的驱动程序中编写代码,它会自动的在多个节点中运行。本书第三章涵盖了 RDD API的详细介绍。
独立(Standalone )应用程序
Spark快速入门教程中缺少如何在独立(Standalone)应用程序中使用Spark,其实Spark除了可以交互式shell运行,还可以在Java、Scala和Python的独立应用程序中依赖Spark运行。唯一与shell不同的是,独立应用程序中需要初始化SparkContext,除此之外所有的API都是相同的。
在独立应用程序中依赖Spark的方法因语言而异。在Java和Scala中,您可在设置Spark核心的Maven依赖。随着本书版本的书写,最新的spark版本为1.2.0,相应的Maven依赖设置为:
groupId = org.apache.spark artifactId = spark-core_2.10 version = 1.2.0
Maven是最受欢迎的基于Java语言的包管理工具,可以链接至公共的资源库。您可以使用Maven创建自己的应用程序,也可以其他的工具比如Scala的sbt或者Gradle创建。流行的集成开发环境如Eclipse允许直接添加Maven依赖至工程中。
在Python中,您可编写Python脚本的应用程序,然后使用bin/spark-submit提交脚本至Spark运行。在spark-submit脚本中包含供Python使用的Spark依赖,在此脚本中设置Spark的Python API的运行环境。
示例2-6 运行Python脚本
Example 2-6. Running a Python script
bin/spark-submit my_script.py
(请注意在Windows中使用反斜杠\替代正斜杠/。)
初始化SparkContext
如果您将应用程序链接至Spark,则需在应用程序中引入Spark包并创建SparkContext。首先创建SparkConf对象配置应用程序,然后实例化SparkContext。示例2-7至2-9以三种语言展示初始化SparkContext的方法。
Example 2-7. Initializing Spark in Python
from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local").setAppName("My App")
sc = SparkContext(conf = conf)
Example 2-8. Initializing Spark in Scala
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
val conf = new SparkConf().setMaster("local").setAppName("My App")
val sc = new SparkContext(conf)
Example 2-9. Initializing Spark in Java
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
SparkConf conf = new SparkConf().setMaster("local").setAppName("My App");
JavaSparkContext sc = new JavaSparkContext(conf);
这些示例展示最简单的初始化SparkContext的方法,其中传递了两个参数:
-
集群URL参数,代表Spark连接到集群的方式,本例中设定为local,表示Spark线程仅运行于本地机器而非连接至集群。
-
应用程序名称参数,本例中被定义为My App,如果您连接至集群,可在集群管理的UI界面中通过应用的名称找到您自己的应用程序。
关于应用程序执行或者提交至集群的附加参数配置,将在本书后面的章节中介绍。
在您初始化SparkContext之后,即可调用我们之前展示给您的所有方法来创建RDD(比如从文本文件读取)并操纵他们。
最后,您可调用stop() 方法关闭Spark,或者简单的退出该应用程序(比如System.exit(0)或者sys.exit())
以上足以让您在笔记本电脑上运行一个单机(Standalone)的Spark应用程序。对于更高级的配置,第七章中将介绍如何将应用程序连接至集群,以及如何将应用程序打包以便代码自动提交至工作节点。目前,我们还是参照Spark官方文档的快速入门。
创建独立(Standalone)应用程序
如果本章没有字数统计的示例,那么就不是完整大数据图书的导论章节。在单机中运行字数统计的程序很简单,但是在分布式框架中它却是一个常见的示例,因为他需要在众多的工作节点中读取和合并数据。接下来我们分别以sbt和Maven的方式创建和打包简单的字数统计的示例。我们所有的示例本都可以一起编译,但是为了说明这种最小依赖的精简编译方式,我们将其分解为多个小的程序,代码示例在目录learning-sparkexamples/mini-complete-example下,您可参阅示例2-10(Java)和2-11(Scala)。
Example 2-10. Word count Java application—don't worry about the details yet
// Create a Java Spark Context
SparkConf conf = new SparkConf().setAppName("wordCount");
JavaSparkContext sc = new JavaSparkContext(conf);
// Load our input data.
JavaRDD<String> input = sc.textFile(inputFile);
// Split up into words.
JavaRDD<String> words = input.flatMap(
new FlatMapFunction<String, String>() {
public Iterable<String> call(String x) {
return Arrays.asList(x.split(" "));
}});
// Transform into pairs and count.
JavaPairRDD<String, Integer> counts = words.mapToPair(
new PairFunction<String, String, Integer>(){
public Tuple2<String, Integer> call(String x){
return new Tuple2(x, 1);
}}).reduceByKey(new Function2<Integer, Integer, Integer>(){
public Integer call(Integer x, Integer y){ return x + y;}});
// Save the word count back out to a text file, causing evaluation. counts.saveAsTextFile(outputFile);
Example 2-11. Word count Scala application—don't worry about the details yet
// Create a Scala Spark Context. val conf = new SparkConf().setAppName("wordCount")
val sc = new SparkContext(conf)
// Load our input data.
val input = sc.textFile(inputFile)
// Split it up into words.
val words = input.flatMap(line => line.split(" "))
// Transform into pairs and count.
val counts = words.map(word => (word, 1)).reduceByKey{case (x, y) => x + y}
// Save the word count back out to a text file, causing evaluation. counts.saveAsTextFile(outputFile)
我们可以使用非常简单的编译文件比如sbt(示例2-12)示例2-12和Maven(示例2-13)创建应用程序。我们以provided标签标记了Spark的核心依赖,以便在稍后的编程中我们可以使用该程序集,而不必导入spark-coreJAR包。
Example 2-12. sbt build file
name := "learning-spark-mini-example"
version := "0.0.1"
scalaVersion := "2.10.4"
// additional libraries
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-core" % "1.2.0" % "provided"
)
Example 2-13. Maven build file
<project>
<groupId>com.oreilly.learningsparkexamples.mini</groupId> <artifactId>learning-spark-mini-example</artifactId>
<modelVersion>4.0.0</modelVersion>
<name>example</name>
<packaging>jar</packaging>
<version>0.0.1</version>
<dependencies>
<dependency> <!-- Spark dependency -->
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.6</java.version>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
spark-core包已经被标记为provided,在应用程序打包时将自动引入该JAR包。更详细的内容在第七章中介绍。
一旦有了自己的编译定义文件,我们可以轻松的将应用程序打包并使用bin/spark-submit脚本运行。bin/spark-submit脚本包含设置Spark运行的环境变量参数。在目录中我们可以编译Scala(示例2-14)和Java(示例2-15)应用。
Example 2-14. Scala build and run
sbt clean package
$SPARK_HOME/bin/spark-submit \
--class com.oreilly.learningsparkexamples.mini.scala.WordCount \
./target/...(as above) \
./README.md ./wordcounts Example 2-15. Maven build and run
mvn clean && mvn compile && mvn package
$SPARK_HOME/bin/spark-submit \ --class com.oreilly.learningsparkexamples.mini.java.WordCount \ ./target/learning-spark-mini-example-0.0.1.jar \
./README.md ./wordcounts
更详细的Spark应用程序的示例请参阅Spark官方文档的快速入门。