3.4 master为集群的MasterURL+部署模式为Cluster时
CLUSTER部署模式时,Driver在某个节点提交,但却是在集群调度分配的节点上运行。
此时,可以将Driver看成是特殊的Executor,同样由分配的节点运行JVM进程,但对应进程的classpath配置信息(补充说明下,看配置属性的extra名,应该可以知道是附加的,或新增的classpath内容,而不是全部)由各自对应的配置属性进行设置。
CLUSTER部署模式在Drive分配到节点,并在节点上启动,相关属性配置及其作用等和CLIENT部署模式基本一致。这里仅基于配置属性方式针对Driver进行解析。
3.4.1 应用程序提交方式
下面给出两种形式的提交命令:
一、基于REST服务提交方式
$SPARK_HOME/bin/spark-submit --master spark://nodemaster:6066 \
--deploy-mode cluster \
--driver-memory 2g \
--driver-cores 1 \
--total-executor-cores 2 \
--executor-memory 4g \
--conf "spark.ui.port"=4081 \
--conf "spark.executor.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar"\
--conf "spark.driver.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" \
--classcom.mb.TestJarwithOracle \
hdfs://nodemaster:8020/tmp/sptest/Spark15.jar
该方式采用REST服务作为masterurl提交应用程序。对应的值参考8080(默认)监控界面,如下所示:

日志:
Running Spark using the REST applicationsubmission protocol.
16/04/26 13:15:02 INFOrest.RestSubmissionClient: Submitting a request to launch an application inspark://nodemaster:7078.
16/04/26 13:15:03 WARNrest.RestSubmissionClient: Unable to connect to server spark://nodemaster:7078.
Warning: Master endpoint spark://nodemaster:7078was not a REST server. Falling back to legacy submission gateway instead.
16/04/26 13:15:04 WARN util.NativeCodeLoader:Unable to load native-hadoop library for your platform... using builtin-javaclasses where applicable
当REST 服务方式提交尝试失败后,会退回到传统方式进行提交。
二、传统提交方式
$SPARK_HOME/bin/spark-submit --master spark://nodemaster:7078 \
--deploy-mode cluster \
--driver-memory 2g \
--driver-cores 1 \
--total-executor-cores 2 \
--executor-memory 4g \
--conf "spark.ui.port"=4081 \
--conf "spark.executor.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar"\
--conf "spark.driver.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" \
--classcom.mb.TestJarwithOracle \
hdfs://nodemaster:8020/tmp/sptest/Spark15.jar
日志:
Running Spark using the REST applicationsubmission protocol.
16/04/26 13:20:58 INFOrest.RestSubmissionClient: Submitting a request to launch an application inspark://nodemaster:6066.
16/04/26 13:20:58 INFOrest.RestSubmissionClient: Submission successfully created asdriver-20160426132058-0010. Polling submission state...
16/04/26 13:20:58 INFOrest.RestSubmissionClient: Submitting a request for the status of submissiondriver-20160426132058-0010 in spark://nodemaster:6066.
16/04/26 13:20:58 INFO rest.RestSubmissionClient:State of driver driver-20160426132058-0010 is now RUNNING.
16/04/26 13:20:58 INFOrest.RestSubmissionClient: Driver is running on workerworker-20160418110627-192.168.149.95-45661 at 192.168.149.95:45661.
16/04/26 13:20:58 INFO rest.RestSubmissionClient:Server responded with CreateSubmissionResponse:
{
"action" : "CreateSubmissionResponse",
"message" : "Driver successfully submitted asdriver-20160426132058-0010",
"serverSparkVersion" : "1.5.2",
"submissionId" : "driver-20160426132058-0010",
"success" : true
}
3.4.2 提交应用之后Driver的分析
两种方式都可以成功提交应用程序,对应在界面会分别增加一个Driver,同时Driver启动后,会和之前Client部署模式的流程一样,提交一个Application(这里的概念对应Executor),对应界面(8080默认端口界面-最下面)有:

两个driver提交的应用如下(8080默认端口界面):

对应下面的Application,配置及其影响和前面是一样的,只是当前的Driver在分配的节点上运行(所有相关路径等概念都改为基于该执行节点)。因此下面仅分析Driver的相关内容。
信息获取相关操作:
1. 点击Driver行所在的Worker,跳转到该Worker监控页面,到最下面,查找与Driver的SubmissionID相同的Driver信息,界面如下所示:

2. 点击对应的stderr日志信息,可以看到Driver的启动命令及其执行日志
3. 查看Driver的启动命令
a) 对应传统提交方式的日志如下所示:
LaunchCommand: "/usr/java/jdk1.7.0_71/bin/java" "-cp""hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar:$SPARK_HOME/sbin/../conf/:$SPARK_HOME/lib/spark-assembly-1.5.2-hadoop2.6.0.jar:$SPARK_HOME/lib/datanucleus-api-jdo-3.2.6.jar:$SPARK_HOME/lib/datanucleus-core-3.2.10.jar:$SPARK_HOME/lib/datanucleus-rdbms-3.2.9.jar:/etc/hadoop/conf/""-Xms2048M" "-Xmx2048M""-Dspark.deploy.defaultCores=4""-Dspark.eventLog.enabled=true" "-Dakka.loglevel=WARNING""-Dspark.history.fs.cleaner.maxAge=7d""-Dspark.submit.deployMode=cluster" "-Dspark.executor.memory=4g""-Dspark.executor.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar""-Dspark.executor.extraJavaOptions=-XX:+PrintGCDetails""-Dspark.jars=hdfs://nodemaster:8020/tmp/sptest/Spark15.jar""-Dspark.history.fs.cleaner.enabled=true""-Dspark.master=spark://nodemaster:7078" "-Dspark.driver.supervise=false""-Dspark.driver.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar""-Dspark.app.name=com.mb.TestJarwithOracle""-Dspark.history.fs.logDirectory=hdfs://nodemaster:8020/user/hdfs/sparklogs""-Dspark.driver.memory=2g" "-Dspark.cores.max=2""-Dspark.rpc.askTimeout=10" "-Dspark.eventLog.dir=hdfs://nodemaster:8020/user/hdfs/sparklogs""-Dspark.ui.port=4081""-Dspark.history.fs.cleaner.interval=1d""-Dspark.driver.cores=1" "-XX:MaxPermSize=256m""org.apache.spark.deploy.worker.DriverWrapper""akka.tcp://sparkWorker@192.168.149.95:45661/user/Worker" "$SPARK_HOME/work/driver-20160426131505-0009/Spark15.jar""com.mb.TestJarwithOracle"
b) 对应REST提交方式的日志如下所示:
Launch Command:"/usr/java/jdk1.7.0_71/bin/java" "-cp" "hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar:$SPARK_HOME/sbin/../conf/:$SPARK_HOME/lib/spark-assembly-1.5.2-hadoop2.6.0.jar:$SPARK_HOME/lib/datanucleus-api-jdo-3.2.6.jar:$SPARK_HOME/lib/datanucleus-core-3.2.10.jar:$SPARK_HOME/lib/datanucleus-rdbms-3.2.9.jar""-Xms2048M" "-Xmx2048M""-Dspark.deploy.defaultCores=4""-Dspark.eventLog.enabled=true""-Dspark.history.fs.cleaner.maxAge=7d""-Dspark.submit.deployMode=cluster" "-Dspark.executor.memory=4g""-Dspark.executor.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar""-Dspark.executor.extraJavaOptions=-XX:+PrintGCDetails" "-Dspark.jars=hdfs://nodemaster:8020/tmp/sptest/Spark15.jar""-Dspark.history.fs.cleaner.enabled=true""-Dspark.master=spark://nodemaster:7078""-Dspark.driver.supervise=false" "-Dspark.driver.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar""-Dspark.app.name=com.mb.TestJarwithOracle" "-Dspark.history.fs.logDirectory=hdfs://nodemaster:8020/user/hdfs/sparklogs""-Dspark.driver.memory=2g" "-Dspark.cores.max=2""-Dspark.eventLog.dir=hdfs://nodemaster:8020/user/hdfs/sparklogs""-Dspark.ui.port=4081""-Dspark.history.fs.cleaner.interval=1d" "-Dspark.driver.cores=1""-XX:MaxPermSize=256m" "org.apache.spark.deploy.worker.DriverWrapper""akka.tcp://sparkWorker@192.168.149.95:45661/user/Worker" "$SPARK_HOME/work/driver-20160426132058-0010/Spark15.jar""com.mb.TestJarwithOracle"
(暂时不考虑两者的差异,仅关注Driver进程相关的内容。)
下面对其中比较重要的几个部分进行分析:
1. 其中,启动的JVM进程主类为"org.apache.spark.deploy.worker.DriverWrapper",在"-cp"后加入了hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar,这里对应的是提交命令中的--conf"spark.driver.extraClassPath"配置属性(可通过修改设置的路径进行验证)。
在该启动命令(提交命令的配置参数)中,ojdbc14.jar路径为hdfs,因此无法识别,在driver部分的逻辑代码执行时会抛出异常。对应日志在该LaunchCommand:命令后面。因此"spark.driver.extraClassPath"配置属性中设置的路径应该对应当前节点(由于是调度分配的,对应就意味着应该是在集群中各个节点都进行部署)的路径,并且路径下有所需jar包。
2. 对应的spark.driver.extraClassPath、spark.executor.extraClassPath等会继续作为Driver的参数传入(和Client部署模式下直接执行Driver一样)
3. "-Dspark.jars=hdfs://nodemaster:8020/tmp/sptest/Spark15.jar":对应提交时的主资源。
4. "$SPARK_HOME/work/driver-20160426132058-0010/Spark15.jar":work是默认的工作目录,driver-20160426132058-0010是当前Driver对应的Submission ID,在该目录中会下载Driver执行所需的Jar包,这里对应主资源Spark15.jar。
此时,需要注意的是,当CLUSTER部署模式时,如果使用的主资源是本地路径,如以下命令:
$SPARK_HOME/bin/spark-submit --master spark://nodemaster:6066 \
--deploy-mode cluster \
--driver-memory 2g \
--driver-cores 1 \
--total-executor-cores 2 \
--executor-memory 4g \
--conf "spark.ui.port"=4081 \
--conf "spark.executor.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar"\
--conf "spark.driver.extraClassPath"="$SPARK_HOME/thirdlib/ojdbc14.jar" \
--classcom.mb.TestJarwithOracle \
/tmp/test/Spark15.jar
其中红色部分对应为主资源jar包,采用本地文件系统的路径。执行时,Driver端输出的错误信息如下所示(对应界面的Driver状态为Error):
java.io.FileNotFoundException:/tmp/test/Spark15.jar (No such file or directory)
java.io.FileInputStream.open(NativeMethod)
java.io.FileInputStream.<init>(FileInputStream.java:146)
org.spark-project.guava.io.Files$FileByteSource.openStream(Files.java:124)
org.spark-project.guava.io.Files$FileByteSource.openStream(Files.java:114)
org.spark-project.guava.io.ByteSource.copyTo(ByteSource.java:202)
org.spark-project.guava.io.Files.copy(Files.java:436)
org.apache.spark.util.Utils$.org$apache$spark$util$Utils$$copyRecursive(Utils.scala:514)
org.apache.spark.util.Utils$.copyFile(Utils.scala:485)
org.apache.spark.util.Utils$.doFetchFile(Utils.scala:562)
org.apache.spark.util.Utils$.fetchFile(Utils.scala:369)
org.apache.spark.deploy.worker.DriverRunner.org$apache$spark$deploy$worker$DriverRunner$$downloadUserJar(DriverRunner.scala:150)
org.apache.spark.deploy.worker.DriverRunner$$anon$1.run(DriverRunner.scala:79)
在DriverRunner启动后,调用downloadUserJar,下载所需jar包,但此时使用本地文件系统的路径,对应的就需要在Driver当前执行节点上的该路径下存在该jar包(当前未部署),因此报异常:java.io.FileNotFoundException:/tmp/test/Spark15.jar。
因此,对应在CLUSTER部署模式时,需要注意提交应用程序对应的主资源的配置:
1. 将主资源类似于其他的第三方jar包(如Oracle驱动类库的jar包)部署到集群中;
2. 使用HDFS这类文件系统,可以下载到本地工作目录。
4总结
通过配置classpath,为JVM加载类时提供搜索路径。
在分布式计算集群中,需要注意JVM进程是在哪台节点上启动,对应节点上的classpath下是否部署了所需的jar包(针对jar包以本地路径的形式,即与具体节点相关的路径)。
因此总结起来有以下两点:
1. 是否为对应JVM进程指定了classpath;
2. 在各个进程的classpath路径下是否放置了所需的jar包。放置的方式可以有两种:
a) 一种是Spark框架提供的自动放置到classpath的方式;
b) 一种手动在集群中部署的方式;
这里指的路径,表示的是该JVM进程能够读取的路径,比如本地文件系统路径、hdfs路径,其中本地文件系统路径是针对本节点的,这点在分布式集群中尤其要注意。





