Spark环境搭建

https://spark.apache.org/docs/latest/index.html

前置环境

  1. JDK:确保安装的JDK版本与Spark兼容(通常建议使用Java 8或Java 11)

  2. winutils:如果打算在Windows上运行Spark,尤其是使用Hadoop相关功能,建议下载WinUtils。
    将winutils.exe放入%HADOOP_HOME%\bin目录下

  3. 安装Scala

https://www.scala-lang.org/download/all.html

直接下载对应的离线包进行安装即可

# centos
rpm -ivh ./scala-2.13.2.rpm

下载安装 Spark

兼容性

scala版本和spark版本的对应关系
Spark 2.x 系列支持Scala 2.11 和Scala 2.12。

Spark 3.x 系列支持Scala 2.12 和Scala 2.13。 因此,如果您要使用Spark 3.2.1,则需要使用Scala 2.12 或Scala 2.13。
Spark2.3对应的Scala版本是Scala2.11

[ERROR] Terminal initialization failed; falling back to unsupported
java.lang.NoClassDefFoundError: Could not initialize class scala.tools.fusesource_embedded.jansi.internal.Kernel32
可能是Spark版本和Scala版本不兼容,因为我Scala版本是2.13,而安装的Spark版本是2.3
参考:
https://issues.apache.org/jira/browse/SPARK-13710?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&focusedCommentId=15208529

安装spark-without-hadoop,执行spark-shell报错:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/Logger

https://stackoverflow.com/questions/42307471/spark-without-hadoop-failed-to-launch

https://spark.apache.org/docs/latest/hadoop-provided.html

下载Spark:https://spark.apache.org/downloads.html
我们选择历史版本Spark3.3.1:https://archive.apache.org/dist/spark/ 下载.tgz结尾的安装包(注.tgz格式和.tar.gz格式是一样的),然后解压缩到本地某个目录

一些镜像地址:

  1. 华为云:https://mirrors.huaweicloud.com/apache/spark/

https://spark.apache.org/docs/latest/api/R/reference/install.spark.html

添加SPARK_HOME环境变量:D:\Tools\spark-3.3.1
Path添加:%SPARK_HOME%\bin
测试是否安装成功:打开cmd命令行,输入spark-shell

如果执行spark-shell命令报错:Failed to find Spark jars directory.
You need to build Spark before running this program.
参考:https://stackoverflow.com/questions/27618843/why-does-spark-submit-and-spark-shell-fail-with-failed-to-find-spark-assembly-j
可能有以下几个原因:

  1. Windows上如果Spark安装在一个带有空格的目录,安装会失败,换个安装目录
  2. 刚才安装的Spark安装包没有包含编译后的代码,也就是二进制包,可能下载错了,应该下载二进制包,而不是源码包,因为我下载的是spark-2.3.0.tgz这个包进行安装的。这种情况下可以重新下载二进制包,或者在安装目录下执行:mvn -DskipTests clean package对源码进行编译(需要提前安装好maven),其实看刚才的解压目录下的根目录就有一个pom.xml文件

安装Hadoop

如果要搭建Spark On Yarn或者是需要去hdfs取数据的话,应该先装hadoop。关于spark和hadoop的关系,可以参考这篇博客:

https://blog.csdn.net/hongxingabc/article/details/81565174

如果安装Hadoop,根据前面安装的spark3.3.1,安装hadoop3.x版本,这里我安装3.3.0版本,安装过程移步:Hadoop安装与部署

Spark With Hadoop

执行spark-shell,出现下面的结果就是安装成功了

image

搭建本地开发环境

本地调试是使用本地编写的代码,通过引入的Spark的相关jar包来运行spark程序,将Spark程序提交到本地spark运行,本地并不需要安装Spark。下面是获取SparkContext的代码:

val conf = new SparkConf().setAppName("WordCount").setMaster("local")
val sc = new SparkContext(config)

也可以通过VM options上添加-Dspark.master=local -Dspark.app.name=test来进行设置

相关版本:Spark 3.3.1,Scala 2.13,Intellij IDEA 2025.2.1

Java + Maven

Spark 相关依赖的坐标:https://mvnrepository.com/artifact/org.apache.spark

创建Maven项目,pom.xml中添加如下配置

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spark.version>3.3.1</spark.version>
    <scala.version>2.13</scala.version>
    <scala.binary.version>2.13</scala.binary.version>
</properties>

<dependencies>
    <!-- 如果使用spark-submit的话可以考虑指定作用范围为provided: <scope>provided</scope> -->
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_${scala.version}</artifactId>
        <version>${spark.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-streaming_${scala.version}</artifactId>
        <version>${spark.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_${scala.version}</artifactId>
        <version>${spark.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-hive_${scala.version}</artifactId>
        <version>${spark.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-mllib_${scala.version}</artifactId>
        <version>${spark.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-client</artifactId>
        <version>2.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-server</artifactId>
        <version>2.4.9</version> 
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>3.3.1</version> 
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.3.1</version> 
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>3.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-protocol</artifactId>
        <version>2.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-shaded-client</artifactId>
        <version>2.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>3.3.1</version>
    </dependency>
</dependencies>

如何知道Spark与Hadoop等其他组件的兼容版本

安装与Spark相关的其他组件的时候,例如JDK,Hadoop,Yarn,Hive,Kafka等,要考虑到这些组件和Spark的版本兼容关系。这个对应关系可以在Spark源代码的pom.xml文件中查看。在Github上的Spark仓库( https://github.com/apache/spark ) 。选择一个版本,例如选择3.3

img

将下载的源代码压缩包解压后,打开里面的pom.xml文件,查看properties标签内各配置项,里面有列出其他组件的兼容版本信息

创建 SparkSession

我这里Yarn,HDFS,Spark WEB UI 都装在本地,所以相关地址都固定为 localhost 了,和我不一样的话自行修改

SparkSession spark = SparkSession.builder()
  .appName("SparkApplication")
  .master("local[*]")  // 或者: yarn
  .config("spark.eventLog.enabled", "true")  // 开启事件日志
  .config("spark.eventLog.dir", "hdfs://localhost:8020/user/spark/eventLogs")  // 事件日志存放目录
  .config("spark.history.fs.logDirectory", "hdfs://localhost:8020/user/spark/eventLogs")
  .config("spark.yarn.historyServer.address", "http://localhost:18080") // 指定HistoryServer地址
  .config("spark.sql.adaptive.enabled", "true")
  .config("spark.sql.adaptive.coalescePartitions.enabled", "true")
  .getOrCreate();

spark-submit

如果要用 spark-submit 的话,还要配置打包,使用 maven-shade-plugin

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.4.1</version>
    <executions>
      <execution>
        <phase>package</phase>
        <configuration>
          <createDependencyReducedPom>false</createDependencyReducedPom>
          <filters>
            <filter>
              <artifact>*:*</artifact>
              <excludes>
                <exclude>META-INF/*.SF</exclude>
                <exclude>META-INF/*.DSA</exclude>
                <exclude>META-INF/*.RSA</exclude>
              </excludes>
            </filter>
          </filters>

          <transformers>
            <!-- 合并META-INF/services文件 -->
            <transformer
              implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>

            <!-- 合并Spring相关配置(如果有使用Spring) -->
            <transformer
              implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
              <resource>META-INF/spring.handlers</resource>
            </transformer>
            <transformer
              implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
              <resource>META-INF/spring.schemas</resource>
            </transformer>
            <!-- 设置主类 -->
            <transformer
              implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
              <mainClass>com.yourcompany.YourMainClass</mainClass>
            </transformer>
          </transformers>

          <!-- 排除不需要的依赖 -->
          <artifactSet>
            <excludes>
              <!-- 排除Spark相关依赖,因为它们已经在集群中提供了 -->
              <exclude>org.apache.spark:spark-*</exclude>
              <exclude>org.scala-lang:scala-*</exclude>
              <exclude>org.apache.hadoop:hadoop-*</exclude>
              <!-- 排除日志实现,使用集群提供的 -->
              <exclude>org.slf4j:slf4j-*</exclude>
              <exclude>log4j:log4j</exclude>
              <exclude>org.apache.logging.log4j:log4j-*</exclude>
            </excludes>
          </artifactSet>
        </configuration>
      </execution>
    </executions>
  </plugin>

脚本

下面是一个脚本,直接执行打包及提交操作

@echo off
setlocal enabledelayedexpansion
chcp 65001
set MAIN_CLASS_NAME=%1

:: 检查主类名
IF "%MAIN_CLASS_NAME%"=="" (
@REM 第一个参数
    if not "%~1"=="" (
        set "MAIN_CLASS_NAME=%~1"
    ) else (
        echo ERROR: mainClassName is not set
        echo.
        echo Solutions:
        echo   1. set MAIN_CLASS_NAME=your.package.MainClass
        echo   2. %~nx0 your.package.MainClass
        exit /b 1
    )
)

REM 可配置参数
set PROJECT_ROOT=%~dp0
set MAVEN_OPTS=-DskipTests
set SPARK_MASTER=yarn
@REM 支持传参指定运行驱动类
set MAIN_CLASS=spark.SparkApplication
set APP_NAME=%MAIN_CLASS_NAME%

REM Spark History Server 配置
set SPARK_HISTORY_SERVER_ENABLED=true
set SPARK_EVENTLOG_ENABLED=true
set SPARK_EVENTLOG_DIR=hdfs://localhost:8020/spark-logs
set SPARK_HISTORY_FS_LOGDIR=hdfs://localhost:8020/spark-logs
set SPARK_HISTORY_UI_PORT=18080

REM Spark 其他配置
set SPARK_DRIVER_MEMORY=2g
set SPARK_EXECUTOR_MEMORY=2g
set SPARK_EXECUTOR_CORES=2
set SPARK_NUM_EXECUTORS=2

REM 显示配置信息
echo 配置信息:
echo   项目目录: %PROJECT_ROOT%
echo   Spark Master: %SPARK_MASTER%
echo   主类: %MAIN_CLASS%
echo   应用名称: %APP_NAME%
echo   History Server: %SPARK_HISTORY_SERVER_ENABLED%
echo   事件日志目录: %SPARK_EVENTLOG_DIR%
echo.

REM 切换到项目目录
cd /d "%PROJECT_ROOT%"

REM 执行Maven打包
echo [步骤1] 执行Maven打包...
call mvn clean package %MAVEN_OPTS%

if %errorlevel% neq 0 (
    echo [错误] Maven打包失败,退出代码: %errorlevel%
    goto :error
)

REM 查找最新的jar包(排除sources和original包)
echo.
echo [步骤2] 查找生成的jar包...
set JAR_FILE=
for /f "delims=" %%i in ('dir /s /b /o-d target\*.jar') do (
    echo 找到文件: %%i
    echo %%i | findstr /v "sources" | findstr /v "original" >nul
    if !errorlevel! equ 0 (
        if not defined JAR_FILE (
            set JAR_FILE=%%i
            echo [选定] 使用jar包: !JAR_FILE!
        )
    )
)

if "%JAR_FILE%"=="" (
    echo [错误] 未找到可用的jar包!
    goto :error
)

REM 显示jar包信息
for %%I in ("%JAR_FILE%") do (
    set JAR_SIZE=%%~zI
    set JAR_DIR=%%~dpI
)
echo Jar包大小: %JAR_SIZE% 字节
echo Jar包目录: %JAR_DIR%

REM 构建Spark提交命令
echo.
echo [Step3] 构建Spark提交命令...

set SPARK_SUBMIT_CMD=spark-submit
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --master %SPARK_MASTER%
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --class %MAIN_CLASS%
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --name "%APP_NAME%"

REM 添加资源配置
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --driver-memory %SPARK_DRIVER_MEMORY%
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --executor-memory %SPARK_EXECUTOR_MEMORY%
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --executor-cores %SPARK_EXECUTOR_CORES%
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --num-executors %SPARK_NUM_EXECUTORS%

REM 添加History Server配置
if "%SPARK_HISTORY_SERVER_ENABLED%"=="true" (
    echo [信息] 启用Spark History Server配置

    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.eventLog.enabled=%SPARK_EVENTLOG_ENABLED%
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.eventLog.dir=%SPARK_EVENTLOG_DIR%
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.fs.logDirectory=%SPARK_HISTORY_FS_LOGDIR%

    REM 其他有用的History Server相关配置
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.ui.port=%SPARK_HISTORY_UI_PORT%
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.fs.update.interval=10s
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.retainedApplications=50
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.fs.cleaner.enabled=true
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.fs.cleaner.interval=1d
    set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! --conf spark.history.fs.cleaner.maxAge=7d

    echo [信息] History Server UI地址: http://history-server-host:%SPARK_HISTORY_UI_PORT%
)

REM 添加JAR文件
set SPARK_SUBMIT_CMD=!SPARK_SUBMIT_CMD! "%JAR_FILE%" %MAIN_CLASS_NAME%

REM 显示完整命令
echo.
echo 完整Spark提交命令:
echo !SPARK_SUBMIT_CMD!
echo.

REM 提交Spark任务
echo [步骤4] 提交Spark任务...
!SPARK_SUBMIT_CMD!

if %errorlevel% neq 0 (
    echo [错误] Spark任务提交失败,退出代码: %errorlevel%
    goto :error
)

REM 显示History Server相关信息
if "%SPARK_HISTORY_SERVER_ENABLED%"=="true" (
    echo ========================================
    echo Spark History Server 信息
    echo ========================================
    echo 任务提交成功!您可以在以下地址查看任务历史:
    echo UI地址: http://localhost:%SPARK_HISTORY_UI_PORT%
    echo 事件日志目录: %SPARK_EVENTLOG_DIR%
)

echo.
echo [成功] 所有步骤执行完成!
goto :end

:error
echo.
echo [失败] 执行过程中出现错误
pause
exit /b 1

:end
pause

Scala

Idea 安装Scala插件

前置要求:Idea 安装Scala插件,有以下几种方式:

  1. 通过插件市场直接搜索Scala安装即可
  2. 下载离线安装包:https://plugins.jetbrains.com/plugin/1347-scala/versions/stable,
  3. Github地址,只有源码,需要自己构建:https://github.com/JetBrains/intellij-scala/tags

建议直接创建Maven项目,不然创建普通的scala项目极大可能会出现jar包依赖缺失的问题

image

点击选择使用的scala版本

image

注意,这里创建项目里Location里填的最后一级目录就是项目名称,上面的Name输入框其实是没用的

在src目录上右键,选择New -> Scala Class,创建一个Scala Object,内容如下。

import org.apache.spark.{SparkContext, SparkConf}

object HelloWorld {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("GroupSort")
    val sc = new SparkContext(conf)
    val test = List(("key1", "123", 12, 2, 0.13), ("key1", "123", 12, 3, 0.18), ("key2", "234", 12, 1, 0.09), ("key1", "345", 12, 8, 0.75), ("key2", "456", 12, 5, 0.45))
    val rdd = sc.parallelize(test)

    val rdd1 = rdd.map(l => (l._1, (l._2, l._3, l._4, l._5))).groupByKey()
      .flatMap(line => {
        val topItem = line._2.toArray.sortBy(_._4)(Ordering[Double].reverse)
        topItem.map(f => (line._1, f._1, f._4)).toList
      })
    rdd1.foreach(println)

    sc.stop()
  }
}

整个步骤和简单的java工程是一样的,

image

然后还需要引入Spark相关的依赖

Scala 版本的选择是根据你的Spark版本选择的,通过Spark官网下载页面可知相匹配的版本

Scala + SBT

直接基于 Idea 的Scala项目创建向导创建项目即可,构建系统选择 sbt

ThisBuild / version := "1.0.0"
ThisBuild / scalaVersion := "2.13.10"
ThisBuild / organization := "com.yourcompany"

lazy val root = (project in file("."))
  .settings(
    name := "spark-project"
  )

// Java 版本设置
javacOptions ++= Seq("-source", "1.8", "-target", "1.8"),
scalacOptions += "-target:jvm-1.8",

val sparkVersion = "3.3.1"
val hadoopVersion = "3.3.4"
// Spark 核心依赖
libraryDependencies ++= Seq(
    // Provided 作用域表示这些依赖在运行时由集群提供
  "org.apache.spark" %% "spark-core" % sparkVersion % Provided,
  "org.apache.spark" %% "spark-sql" % sparkVersion % Provided,
  "org.apache.spark" %% "spark-mllib" % sparkVersion % Provided,
  "org.apache.spark" %% "spark-streaming" % sparkVersion % Provided,

  // Hadoop 相关依赖
  "org.apache.hadoop" % "hadoop-common" % hadoopVersion % Provided,
  "org.apache.hadoop" % "hadoop-client" % hadoopVersion % Provided,
  "org.apache.hadoop" % "hadoop-hdfs" % hadoopVersion % Provided,
  "org.apache.hadoop" % "hadoop-aws" % hadoopVersion,

  // 其他常用依赖
  "org.scala-lang" % "scala-library" % scalaVersion.value,

  // 测试依赖
  "org.apache.spark" %% "spark-core" % sparkVersion % Test classifier "tests",
  "org.apache.spark" %% "spark-sql" % sparkVersion % Test classifier "tests",
  "org.scalatest" %% "scalatest" % "3.2.15" % Test
),

// 仓库设置
resolvers ++= Seq(
  "Apache Repository" at "https://repository.apache.org/content/repositories/releases/",
  "Maven Central" at "https://repo1.maven.org/maven2/",
  "Cloudera Repository" at "https://repository.cloudera.com/artifactory/cloudera-repos/"
)

如果需要 Assembly 插件来创建 fat JAR,在 project/plugins.sbt 中添加:

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0")

然后再build.sbt中添加Assembly 插件配置

// 打包设置
assembly / assemblyJarName := s"${name.value}-${version.value}.jar",
assembly / assemblyMergeStrategy := {
  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
  case x => MergeStrategy.first
},

// 并行执行设置
Test / parallelExecution := false,

// 内存设置
run / fork := true,
run / javaOptions ++= Seq(
  "-Xms512M",
  "-Xmx2G",
  "-XX:MaxPermSize=512M"
)

打包命令

sbt assembly
# 跳过测试
sbt "set test in assembly := {}" assembly

下面是 Assembly 插件相关配置

// Assembly 配置
ThisBuild / assemblyMergeStrategy := {
  case PathList("META-INF", "services", xs @ _*) => MergeStrategy.filterDistinctLines
  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
  case "application.conf" => MergeStrategy.concat
  case "reference.conf" => MergeStrategy.concat
  case "module-info.class" => MergeStrategy.discard
  case x => MergeStrategy.first
}

assembly / assemblyJarName := s"${name.value}-${version.value}-assembly.jar"
assembly / assemblyExcludedJars := {
  val cp = (assembly / fullClasspath).value
  cp filter {_.data.getName.contains("slf4j-api")}
}

// 可选:跳过测试
assembly / test := {}

如果遇到重复类错误,可以排除特定依赖:

libraryDependencies += "some.library" % "name" % "version" excludeAll(
  ExclusionRule(organization = "org.slf4j"),
  ExclusionRule(organization = "log4j")
)

Maven + Java + Scala 混编

在 Maven 项目中配置 Scala 编译和打包,需要在 pom.xml 文件中添加 Scala 插件(如 scala-maven-plugin)和 Scala 依赖,并创建相应的源码目录。通过这种方式,Maven 就能自动识别和编译 Scala 代码,并将其打包成可执行 JAR 文件。

<dependencies>
  <dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-library</artifactId>
    <version>2.13.0</version> <!-- 根据你的项目需求选择版本 -->
  </dependency>
</dependencies>

配置maven的scala插件

<build>
  <plugins>
    <plugin>
      <groupId>net.alchim31.maven.plugins</groupId>
      <artifactId>scala-maven-plugin</artifactId>
      <version>4.5.1</version> <!-- 根据你的项目需求选择版本 -->
      <executions>
        <execution>
          <id>compile</id>
          <phase>compile</phase>
          <goals>
            <goal>compile</goal>
          </goals>
        </execution>
        <execution>
          <id>test-compile</id>
          <phase>test-compile</phase>
          <goals>
            <goal>test-compile</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

创建 Scala 源码目录:在 src/main/scala 目录下创建 Scala 代码文件。如果该目录不存在,需要手动创建。

搭建Spark环境

https://spark.apache.org/docs/latest/cluster-overview.html

Standalone

Standalone模式是Spark自带的资源调动引擎,构建一个由Master + Slave构成的Spark集群,Spark运行在集群中。这个要和Hadoop中的Standalone区别开来。这里的Standalone是指只用Spark来搭建一个集群,不需要借助其他的框架。是相对于Yarn和Mesos来说的。

Master和Worker是Spark的守护进程,即Spark在特定模式下正常运行所必须的进程。Driver和Executor是临时程序,当有具体任务提交到Spark集群才会开启的程序。

Spark on Yarn

参考:https://spark.apache.org/docs/latest/running-on-yarn.html

需提前安装好hadoop,下载:https://archive.apache.org/dist/hadoop/common/

配置Yarn地址
关于yarn的一些配置信息都是在安装hadoop的时候自带,如果想利用yarn来启动spark,需要告知spark关于yarn的配置信息

打开%SPARK_HOME%/conf/目录,刚开始该目录下只有一些.template结尾的文件,这些文件是表示一些配置模板,可以在这些文件的基础上进行配置。

fairscheduler.xml.template
log4j2.properties.template
metrics.properties.template
spark-defaults.conf.template
spark-env.sh.template
workers.template

我们要修改的是spark-env.sh.template文件,事实上spark-env.sh.template文件全部都是注释。将spark-env.sh.template复制一份,改名为spark-env.sh。(注意:如果在Windows上操作,需要改为spark-env.cmd文件,同时要将该文件的语法改为cmd文件的语法,将行首的#!/usr/bin/env bash改为@echo off,以及将shell脚本中的注释语法#使用cmd中@rem进行替代)

添加一行,注意将HADOOP_HOME替换为实际的路径

YARN_CONF_DIR=$HADOOP_HOME/etc/hadoop

如果是Windows上

SET YARN_CONF_DIR=%HADOOP_HOME%/etc/hadoop

添加配置后,启动HDFS及YARN集群

下面提交一个示例程序

# 切换到SPARK_HOME目录下
# cd %SPARK_HOME%

# 具体执行的jar包名称以自己下载的spark版本为准
./bin/spark-submit.cmd --class org.apache.spark.examples.SparkPi --master yarn .\examples\jars\spark-examples_2.13-3.3.4.jar

正常情况下会如下图所示:

image

如果已经添加了set YARN_CONF_DIR=xxx还是报下面的错,可以直接将YARN_CONF_DIR配置在系统环境变量

➜ ./bin/spark-submit.cmd --class org.apache.spark.examples.SparkPi --master yarn .\examples\jars\spark-examples_2.13-3.3.4.jar
Exception in thread "main" java.lang.Exception: When running with master 'yarn' either HADOOP_CONF_DIR or YARN_CONF_DIR must be set in the environment.
        at org.apache.spark.deploy.SparkSubmitArguments.validateSubmitArguments(SparkSubmitArguments.scala:286)
        ......
        at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)

Client/Cluster模式

添加--deploy-mode参数指定部署模式:有client和cluseter两种

# 不指定--deploy-mode参数时,默认就是client
./bin/spark-submit.cmd --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode client .\examples\jars\spark-examples_2.13-3.3.4.jar

# cluster模式
./bin/spark-submit.cmd --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster .\examples\jars\spark-examples_2.13-3.3.4.jar

观察2种模式的JPS进程情况

Spark有yarn-client和yarn-cluster两种模式,主要区别在于:Driver程序的运行节点

yarn-client: Driver程序运行在客户端,适用于交互,调试

Client模式

在 Client 模式下,Spark Driver 运行在提交任务的客户端节点上(即运行 spark-submit 命令的机器)。Driver 负责执行应用程序的调度和监控,而 Executor 则在集群的工作节点上启动和运行任务。
在 Client 模式中,机器的 CPU 负担主要集中在 Driver 进程上,因为 Driver 负责调度和监控整个应用程序的运行。Client 模式适用于开发、调试和交互式操作,对于小型数据集和快速迭代的任务有效,希望立即看到app的输出

Cluster模式

在 Cluster 模式下,Spark Driver 运行在集群中的某个由 ResourceManager 启动的 APPMaster节点上,并且与其他 Executor 并行运行。客户端只负责提交应用程序,并不参与应用程序的实际运行。
在 Cluster 模式中,机器的 CPU 负担在整个集群中分布,因为 Driver 和 Executor 都在各自的节点上运行。
Cluster 模式适用于生产环境,用于处理大规模数据集和长时间运行的任务。
总体而言,Client 模式下对机器的 CPU 影响较大,因为 Driver 运行在客户端节点上,而 Cluster 模式下对机器的 CPU 影响相对均匀,因为任务在整个集群中运行。在选择提交模式时,需考虑任务的规模、数据量和计算资源情况,以及是否需要实时监控和交互式操作等因素

Spark Web UI

https://spark.apache.org/docs/latest/web-ui.html

Spark UI是反映一个Spark作业执行情况的web页面,用户可以通过Spark UI观察Spark作业的执行状态,分析可能存在的问题

当一个Spark Application运行起来后,可以通过访问hostname:4040端口来访问UI界面。hostname是提交任务的Spark客户端ip地址,端口号由参数spark.ui.port(默认值4040,如果被占用则顺序往后探查)来确定。由于启动一个Application就会生成一个对应的UI界面,所以如果启动时默认的4040端口号被占用,则尝试4041端口,如果还是被占用则尝试4042,一直找到一个可用端口号为止。

如下图所示,启动过程中会打印出tracking URL,如果访问有问题可以将域名改为ip试试

image

如果Spark Application运行完后,这个界面就不可访问了

配置历史服务

默认情况下,Spark程序运行完毕关闭窗口之后,就无法再查看运行记录的Web UI(4040)了,但通过 HistoryServer 可以提供一个服务, 通过读取日志文件, 使得我们可以在程序运行结束后, 依然能够查看运行过程。有助于理解应用程序的执行流程、识别性能瓶颈并解决问题。
参考:https://spark.apache.org/docs/latest/monitoring.html#viewing-after-the-fact

事件日志配置

每个 Spark 应用的 Job 在运行过程中如果开启了事件日志记录功能,会将日志保存到文件,这个文件可以选择保存到本地文件系统或者HDFS上。Spark Web UI 支持读取这些日志文件,并通过 UI 界面进行展示

  1. 修改Spark的配置文件
mv spark-defaults.conf.template spark-defaults.conf
vi spark-defaults.conf
cd %SPARK_HOME%/conf

添加下面2行配置

# 开启记录事件日志
spark.eventLog.enabled true
# 指定事件日志存放的位置,这里需要替换实际的IP端口: 例如hdfs://localhost:9870/spark-logs
# 默认: file:///tmp/spark-events
spark.eventLog.dir hdfs://${namenode}/spark-logs
# 是否压缩事件日志
spark.eventLog.compress true

# 详细配置参考:https://spark.apache.org/docs/latest/configuration.html#spark-ui

其中spark.eventLog.dir可以不配置,默认是存放在本地文件系统/tmp/spark-events目录下

如果配置为HDFS路径,则需要提前先在Hadoop上创建用于存放Spark日志的存储路径

hadoop fs -mkdir /spark-logs

如果运行在yarn模式下,还需要在spark-defaults.conf添加yarn相关的配置

spark.yarn.historyServer.address=192.168.58.130:18080
spark.history.ui.port=18080
spark.history.fs.logDirectory=hdfs://192.168.58.130:8020/spark-logs

spark.eventLog.dir和spark.history.fs.logDirectory的区别:

spark.eventLog.dir是指定应用日志写入保存的位置,而spark.history.fs.logDirectory是指定Spark Web UI从哪里读取日志

修改 spark-env.sh 文件, 添加日志配置
在配置文件中配置了历史服务器的参数,在此处可以不配置

# spark.history.ui.port: WEB UI 访问的端口号为 18080
# spark.history.fs.logDirectory: 指定历史服务器日志存储路径
# spark.history.retainedApplications: 指定保存 Application 历史记录的个数,如果超过这个值,旧的应用程序
# 信息将被删除,这个是内存中的应用数,而不是页面上显示的应用数。
export SPARK_HISTORY_OPTS="
-Dspark.history.ui.port=18080
-Dspark.history.fs.logDirectory=hdfs://192.168.58.130:8020/spark_logs
-Dspark.history.retainedApplications=30"

启动历史服务

如果在Linux上使用,只需要执行Spark安装目录下../sbin/start-history-server.sh这个脚本即可

如果在Windows上使用Spark,因为Spark安装包中sbin目录下只有sh脚本,没有对应的cmd脚本,可以通过以下方式启动

cd $SPARK_HOME
java -cp ./jars/* org.apache.spark.deploy.history.HistoryServer
# 或者通过spark-class.cmd这个脚本来执行,这个脚本是Spark自带的
bin\spark-class.cmd org.apache.spark.deploy.history.HistoryServer

读取日志文件的路径可以通过spark-defaults.conf中配置,或者通过命令行参数传入

# 也可以指定HDFS路径
spark.history.fs.logDirectory   file:///c:/logs/dir/path

现在提交一个应用来测试是否可以看到历史日志

spark-submit \
  --master yarn \
  --deploy-mode cluster \
  --class org.apache.spark.examples.SparkPi \
  $SPARK_HOME/examples/jars/spark-examples_2.13-3.3.1.jar \
  1000

查看历史服务:http://localhost:18080/

问题记录

  1. 运行示例代码时报错java.lang.NoSuchMethodError: scala.Predef$.refArrayOps

可能是使用的Scala版本不对,使用的Spark是2.3.0,使用的Scala版本应该是2.11.x版本

  1. ????????????? org.apache.spark.deploy.yarn.ExecutorLauncher

    Spark-submit错误信息乱码,导致这个错误信息看不出来什么原因,也不方便搜索

每个Container都会有自己的日志,根据Container的ID找到存在本地磁盘上的日志文件stderr,用文本编辑器打开调整下编码就可以看到不乱码的错误信息了

img

  1. Container exited with a non-zero exit code 13. Error file: prelaunch.err.
  2. 查看yarn日志:yarn logs -applicationId application_1753803383891_0002

https://blog.csdn.net/appleyuchi/article/details/105846962

报错可能是因为spark-submit中指定的集群模式和工程文件中的设定master的语句冲突,修改如下:

SparkSessionspark = SparkSession.builder.appName("AppName").getOrCreate();
  1. 启动历史服务时报错:RPC response exceeds maximum data length

25/09/30 22:16:53 INFO HistoryServer: Bound HistoryServer to 0.0.0.0, and started at http://DESKTOP-8O91IHA:18080
Exception in thread "main" org.apache.hadoop.ipc.RpcException: RPC response exceeds maximum data length
at org.apache.hadoop.ipc.Client$IpcStreams.readResponse(Client.java:1936)
at org.apache.hadoop.ipc.Client$Connection.receiveRpcResponse(Client.java:1238)
at org.apache.hadoop.ipc.Client$Connection.run(Client.java:1134)

可能是spark.eventLog.dir和spark.history.fs.logDirectory端口配置错误

访问http://localhost:9870/dfshealth.html#tab-overview,下图的这里显示的端口进行配置即可

spark.eventLog.dir 	hdfs://localhost:19000/spark-logs
spark.history.fs.logDirectory=hdfs://localhost:19000/spark-logs

image-20250930222204329

posted @ 2025-07-05 20:46  vonlinee  阅读(79)  评论(0)    收藏  举报