spark4 集群安装
准备工作
创建安装目录
bash
mkdir -p ~/opt
cd ~/opt
下载scala
bash
wget -P ~/opt https://github.com/scala/scala/releases/download/v2.13.18/scala-2.13.18.tgz
解压scala
bash
tar -zxvf ~/opt/scala-2.13.18.tgz -C ~/opt
修改scala目录名称
bash
mv ~/opt/scala-2.13.18 ~/opt/scala-2
下载 JDK 21
Spark 4.1.2 要求 JDK 17 或 21,不支持 JDK 25。
bash
wget -P ~/opt https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz
解压 JDK 21
bash
tar -zxvf ~/opt/jdk-21_linux-x64_bin.tar.gz -C ~/opt
解压后目录名通常为 jdk-21.0.x,重命名为统一名称:
bash
mv ~/opt/jdk-21.* ~/opt/jdk-21
下载spark
bash
wget -P ~/opt https://dlcdn.apache.org/spark/spark-4.1.2/spark-4.1.2-bin-hadoop3-connect.tgz
解压spark
bash
tar -zxvf ~/opt/spark-4.1.2-bin-hadoop3-connect.tgz -C ~/opt
修改目录名称
bash
mv ~/opt/spark-4.1.2-bin-hadoop3-connect ~/opt/spark-4
修改环境遍历
bash
vim ~/.profile
bash
export SCALA_HOME=$HOME/opt/scala-2
export SPARK_HOME=$HOME/opt/spark-4
export SPARKPYTHON=$HOME/opt/spark-4/python
export PATH=$PATH:$SCALA_HOME/bin:$SPARK_HOME/bin:$SPARK_HOME/sbin:$SPARKPYTHON
完整
bash
export NODE_HOME=$HOME/opt/node-v24
export JAVA_HOME=$HOME/opt/jdk-25
export MAVEN_HOME=$HOME/opt/maven
export SCALA_HOME=$HOME/opt/scala-2
export ZOOKEEPER_HOME=$HOME/opt/zookeeper-3
export HDFS_NAMENODE_USER=lhz
export HDFS_SECONDARYNAMENODE_USER=lhz
export HDFS_DATANODE_USER=lhz
export HDFS_ZKFC_USER=lhz
export HDFS_JOURNALNODE_USER=lhz
export HADOOP_SHELL_EXECNAME=lhz
export YARN_RESOURCEMANAGER_USER=lhz
export YARN_NODEMANAGER_USER=lhz
export HADOOP_HOME=$HOME/opt/hadoop-3
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
export JAVA_LIBRARY_PATH=$HADOOP_HOME/lib/native
export HIVE_HOME=/home/lhz/opt/hive-4
export HCATALOG_HOME=/home/lhz/opt/hive-4/hcatalog
export HBASE_HOME=$HOME/opt/hbase-2
export PHOENIX_HOME=$HOME/opt/phoenix
export KAFKA_HOME=$HOME/opt/kafka
export SPARK_HOME=$HOME/opt/spark-4
export SPARKPYTHON=$HOME/opt/spark-4/python
export PATH=$PATH:$NODE_HOME/bin:$JAVA_HOME/bin:$MAVEN_HOME/bin:$SCALA_HOME/bin:$ZOOKEEPER_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HIVE_HOME/bin:$HCATALOG_HOME/bin:$HCATALOG_HOME/sbin:$HBASE_HOME/bin:$KAFKA_HOME/bin:$SPARK_HOME/bin:$SPARK_HOME/sbin:$SPARKPYTHON
bash
source ~/.profile
Hadoop 集群(前置依赖)
本文档的 Standalone / HA 模式都用到了 Hadoop 的 HDFS 存储 Event Log,并提供了 YARN 模式提交作业。本节统一说明 Hadoop 集群架构与运维方式,便于初学者理解 Spark 与 Hadoop 的集成关系。
Hadoop 集群架构
当前环境使用 3 节点 Hadoop 完全分布式 + HA 集群:
┌─────────────────────────────────────────────────────────────┐
│ Hadoop 集群 (3 节点完全分布式 + HA) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │lihaozhe01│ │lihaozhe02│ │lihaozhe03│ │
│ │ │ │ │ │ │ │
│ │ NameNode │◄───►│ NameNode │ │ │ │
│ │ (nn1) │ ZK │ (nn2) │ │ │ │
│ │ active │ Fail│ standby │ │ │ │
│ │ │ over │ │ │ │
│ │ ZKFailoverController │ │ │ │
│ │ DFSZKFailoverController │ │ │ │
│ │ ResourceManager (rm1) active│ │ │ │
│ │ │ │ResourceManager (rm2) standby │
│ │ NodeManager │NodeManager │NodeManager │
│ │ DataNode │DataNode │DataNode │
│ │ JournalNode │JournalNode │JournalNode │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ZooKeeper 集群 (3 节点) - 用于 HDFS HA / YARN HA 选举 │
│ ZK 节点: lihaozhe01:2181, lihaozhe02:2181, lihaozhe03:2181 │
└─────────────────────────────────────────────────────────────┘
核心组件清单:
| 组件 | 角色 | 节点 | 备注 |
|---|---|---|---|
| NameNode | HDFS 元数据管理 | 01 (active) / 02 (standby) | 通过 ZK 选举 |
| DFSZKFailoverController | NameNode 健康监测与故障切换 | 01 / 02 | 每个 NameNode 配一个 |
| JournalNode | EditLog 共享存储(HA 同步) | 01 / 02 / 03 | 3 节点多数派 |
| DataNode | HDFS 数据块存储 | 01 / 02 / 03 | 实际数据节点 |
| ResourceManager | YARN 资源调度 | 01 (active) / 02 (standby) | 通过 ZK 选举 |
| NodeManager | YARN 单节点代理 | 01 / 02 / 03 | 容器管理 |
| ZooKeeper | 分布式协调服务(选举) | 01 / 02 / 03 | quorum 服务 |
Hadoop 启停命令
为避免与 Spark 启停脚本冲突,建议将 Hadoop 启停脚本重命名为 start-hadoop.sh / stop-hadoop.sh:
bash
cd /home/lhz/opt/hadoop-3/sbin
mv start-all.sh start-hadoop.sh
mv stop-all.sh stop-hadoop.sh
本文使用
start-hadoop.sh/stop-hadoop.sh,可与 Spark 的start-spark.sh/stop-spark.sh区分。
单独启停 HDFS
bash
# 启动 HDFS(NameNode + DataNode + JournalNode + ZKFC)
start-hadoop.sh
# 仅启动 DataNode(在已部署的集群扩容新节点)
hdfs --daemon start datanode
单独启停 YARN
bash
# 启动 YARN(ResourceManager + NodeManager)
start-yarn.sh
# 仅启动 NodeManager
yarn --daemon start nodemanager
完整启停(推荐)
bash
# 启动顺序:ZK → HDFS → YARN
zkServer.sh start # 三节点分别执行
start-hadoop.sh # HDFS HA 集群
start-yarn.sh # YARN HA 集群
# 停止顺序:YARN → HDFS → ZK
stop-yarn.sh
stop-hadoop.sh
zkServer.sh stop # 三节点分别执行
Hadoop 服务状态查看
HDFS HA 状态
bash
# 查看 NN1/NN2 状态
hdfs haadmin -getServiceState nn1
hdfs haadmin -getServiceState nn2
# 输出示例:
# active
# standby
# 查看集群健康
hdfs dfsadmin -report
# 查看 HDFS 容量
hdfs dfs -df -h
YARN HA 状态
bash
# 查看 RM1/RM2 状态
yarn rmadmin -getServiceState rm1
yarn rmadmin -getServiceState rm2
# 查看集群节点
yarn node -list
# 查看集群资源
yarn top
ZooKeeper 状态
bash
# 三节点分别查看
zkServer.sh status
HDFS 关键路径
| 路径 | 用途 |
|---|---|
hdfs://lihaozhe:8020/ |
HDFS 根目录(HA 命名服务) |
hdfs://lihaozhe:8020/spark-log |
Spark Event Log 存储目录 |
hdfs://lihaozhe:8020/tmp |
临时文件目录 |
hdfs://lihaozhe:8020/user |
用户数据目录 |
hdfs://lihaozhe:8020/wordcount |
示例数据(已存在) |
Spark 与 Hadoop 集成的 4 个关键点
1. Event Log 写到 HDFS
$SPARK_HOME/conf/spark-defaults.conf 中:
properties
spark.eventLog.enabled true
spark.eventLog.dir hdfs://lihaozhe/spark-log
spark.history.fs.logDirectory hdfs://lihaozhe/spark-log
Spark 通过内置 hadoop-client 写日志到 HDFS(无需额外配置)。
2. Spark 客户端读 HDFS 配置
Spark 4.x 自带 hadoop-client,能从 core-site.xml / hdfs-site.xml 自动解析 HDFS HA。如果访问失败,在 spark-defaults.conf 中显式指定:
properties
spark.hadoop.fs.defaultFS=hdfs://lihaozhe
3. YARN 模式提交
将作业提交到 YARN 集群(而非 Spark Standalone / HA 集群):
bash
spark-submit --master yarn --deploy-mode client \
--class org.apache.spark.examples.SparkPi \
$SPARK_HOME/examples/jars/spark-examples_2.13-4.1.2.jar \
100
此时 Driver 在客户端机器,Executor 在 YARN Container 中。
4. 共享 Hadoop 配置文件(可选)
如果 Spark 任务需要访问 Hive Metastore 或访问远端 HDFS,建议将 Hadoop 配置软链到 Spark 目录:
bash
ln -s $HADOOP_HOME/etc/hadoop $SPARK_HOME/conf/hadoop-conf
这样 Spark 能自动加载 Hadoop 的所有配置。
Hadoop 配置示例(参考)
$HADOOP_HOME/etc/hadoop/core-site.xml
xml
<configuration>
<!-- HDFS 命名服务(HA 入口)-->
<property>
<name>fs.defaultFS</name>
<value>hdfs://lihaozhe:8020</value>
</property>
<!-- ZK 集群(用于 NameNode HA)-->
<property>
<name>ha.zookeeper.quorum</name>
<value>lihaozhe01:2181,lihaozhe02:2181,lihaozhe03:2181</value>
</property>
<!-- 临时目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/home/lhz/opt/hadoop-3/data/tmp</value>
</property>
</configuration>
$HADOOP_HOME/etc/hadoop/hdfs-site.xml(HA 关键配置)
xml
<configuration>
<!-- NameNode 数据目录 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///home/lhz/opt/hadoop-3/data/dfs/name</value>
</property>
<!-- DataNode 数据目录 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///home/lhz/opt/hadoop-3/data/dfs/data</value>
</property>
<!-- HA 命名服务 -->
<property>
<name>dfs.nameservices</name>
<value>lihaozhe</value>
</property>
<!-- 命名服务下的 NameNode ID -->
<property>
<name>dfs.ha.namenodes.lihaozhe</name>
<value>nn1,nn2</value>
</property>
<!-- nn1 RPC 地址 -->
<property>
<name>dfs.namenode.rpc-address.lihaozhe.nn1</name>
<value>lihaozhe01:8020</value>
</property>
<!-- nn2 RPC 地址 -->
<property>
<name>dfs.namenode.rpc-address.lihaozhe.nn2</name>
<value>lihaozhe02:8020</value>
</property>
<!-- NameNode HTTP 页面 -->
<property>
<name>dfs.namenode.http-address.lihaozhe.nn1</name>
<value>lihaozhe01:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.lihaozhe.nn2</name>
<value>lihaozhe02:9870</value>
</property>
<!-- JournalNode 集群(共享 EditLog)-->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://lihaozhe01:8485;lihaozhe02:8485;lihaozhe03:8485/lihaozhe</value>
</property>
<!-- HA 自动切换代理 -->
<property>
<name>dfs.client.failover.proxy.provider.lihaozhe</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 启用自动故障切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- JournalNode 数据目录 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/lhz/opt/hadoop-3/data/journalnode</value>
</property>
</configuration>
$HADOOP_HOME/etc/hadoop/yarn-site.xml(HA 关键配置)
xml
<configuration>
<!-- YARN HA 启用 -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- YARN 命名服务 -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>yarn-ha</value>
</property>
<!-- RM 列表 -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- rm1 主机 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>lihaozhe01</value>
</property>
<!-- rm2 主机 -->
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>lihaozhe02</value>
</property>
<!-- ZK 集群 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>lihaozhe01:2181,lihaozhe02:2181,lihaozhe03:2181</value>
</property>
<!-- NodeManager 本地目录 -->
<property>
<name>yarn.nodemanager.local-dirs</name>
<value>/home/lhz/opt/hadoop-3/data/nm-local</value>
</property>
<!-- 日志目录 -->
<property>
<name>yarn.nodemanager.log-dirs</name>
<value>/home/lhz/opt/hadoop-3/logs/yarn/userlogs</value>
</property>
</configuration>
常见问题
HDFS 进入 Safe Mode
bash
# 查看 Safe Mode 状态
hdfs dfsadmin -safemode get
# 强制离开 Safe Mode(数据不一致时慎用)
hdfs dfsadmin -safemode leave
HDFS 磁盘空间不足
bash
# 查看各 DataNode 磁盘使用
hdfs dfsadmin -report
# 删除无用文件
hdfs dfs -rm -r /path/to/unwanted/data
YARN Container 启动失败
bash
# 查看 NM 日志
yarn logs -applicationId <application_id>
# 或直接在 NM 节点查看
tail -f $HADOOP_HOME/logs/yarn/yarn-lhz-nodemanager-*.log
配置 spark-env.sh
Spark 4.1.2 要求 JDK 17 或 21,需在 conf/spark-env.sh 中指定 JDK 路径并设置 Netty 内存分配器参数。
bash
cd $SPARK_HOME/conf
cp spark-env.sh.template spark-env.sh
vim spark-env.sh
在文件末尾添加以下内容:
bash
# ---- JDK ----
export JAVA_HOME=$HOME/opt/jdk-21
# ---- JVM 参数(关键)----
export SPARK_DAEMON_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS -Dio.netty.allocator.type=unpooled"
export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Dio.netty.allocator.type=unpooled"
SPARK_DAEMON_JAVA_OPTS对 Spark 守护进程(包括 Connect Server)生效,SPARK_SUBMIT_OPTS对通过spark-submit提交的客户端生效。
本地开发模式(Spark Connect)
注:Spark 4.1.2 已将传统
local[*]单机模式整合进 Spark Connect 架构,本节内容对应本地启动 Connect Server + 远程客户端的开发方式。如需传统单机模式(无 Server,进程内运行 Spark),可通过spark-submit --master 'local[*]'直接提交,或使用 YARN / Standalone 集群模式。
Spark 4.1.2 提供了 Spark Connect 安装包(spark-4.1.2-bin-hadoop3-connect.tgz),该包采用 Server-Client 架构:
- Server 端:启动一个常驻的 Spark Connect Server,负责执行计算任务
- Client 端 :通过远程协议(
sc://)连接 Server,提交代码,无需本地 SparkContext
这种模式适合本地开发测试,客户端只需引入轻量的 Connect 依赖,无需完整的 Spark 安装。
启动 Spark Connect Server
bash
cd $SPARK_HOME
./sbin/start-connect-server.sh --master 'local[*]' --conf spark.connect.grpc.binding.port=15002
--master 'local[*]':使用本地模式,使用所有可用的 CPU 核心spark.connect.grpc.binding.port=15002:指定 gRPC 端口(默认 15002)- 启动后日志位于
$SPARK_HOME/logs/目录下
验证 Server 是否启动成功:
bash
tail -f $SPARK_HOME/logs/spark-*.out
日志末尾应看到类似 INFO SparkConnectServer: SparkConnectServer started. 的信息。
停止 Spark Connect Server
bash
cd $SPARK_HOME
./sbin/spark-daemon.sh stop org.apache.spark.sql.connect.service.SparkConnectServer 1
Python 项目开发与测试
创建项目目录
bash
mkdir -p ~/opt/code/demo-python/src
cd ~/opt/code/demo-python
创建虚拟环境
bash
python3 -m venv venv
source venv/bin/activate
安装依赖
bash
pip install pyspark pyarrow pandas zstandard
注:不指定版本号,自动安装最新兼容版本。
编写客户端代码
创建 src/app.py:
python
from pyspark.sql import SparkSession
def main():
remote = "sc://localhost:15002"
print(f"[INFO] 连接 Spark Connect Server: {remote}")
spark = SparkSession.builder.remote(remote).getOrCreate()
# 验证 1: 执行 SQL
print("\n[TEST] spark.range(5).show()")
spark.range(0, 5).show()
# 验证 2: DataFrame 操作
print("\n[TEST] spark.sql('SELECT 1+1 AS result')")
spark.sql("SELECT 1+1 AS result").show(truncate=False)
# 验证 3: 创建 DataFrame
print("\n[TEST] spark.createDataFrame([(1,'a'),(2,'b')], ['id','val'])")
df = spark.createDataFrame([(1, "a"), (2, "b")], ["id", "val"])
df.show()
# 验证 4: 版本信息
print(f"\n[INFO] Spark version: {spark.version}")
spark.stop()
print("\n[OK] 全部测试通过!")
if __name__ == "__main__":
main()
使用
SparkSession.builder.remote("sc://localhost:15002")通过 Spark Connect 协议连接 Server,无需本地 SparkContext。
运行 Python 客户端
bash
cd ~/opt/code/demo-python
source venv/bin/activate
python src/app.py
预期输出:
[INFO] 连接 Spark Connect Server: sc://localhost:15002
[TEST] spark.range(5).show()
+---+
| id|
+---+
| 0|
| 1|
| 2|
| 3|
| 4|
+---+
[TEST] spark.sql('SELECT 1+1 AS result')
+------+
|result|
+------+
|2 |
+------+
[TEST] spark.createDataFrame([(1,'a'),(2,'b')], ['id','val'])
+---+---+
| id|val|
+---+---+
| 1| a|
+---+---+
[INFO] Spark version: 4.1.2
[OK] 全部测试通过!
Scala / Maven 项目开发与测试
创建项目目录
bash
mkdir -p ~/opt/code/demo-maven/src/main/scala/demo
cd ~/opt/code/demo-maven
编写 pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo.lhz</groupId>
<artifactId>demo-maven</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>demo-maven</name>
<description>Spark 4.1.2 Connect Client demo (Scala 2.13.18)</description>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<scala.version>2.13.18</scala.version>
<scala.binary.version>2.13</scala.binary.version>
<spark.version>4.1.2</spark.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-connect-client-jvm_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>demo-maven-1.0</finalName>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>4.9.10</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals><goal>compile</goal></goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals><goal>testCompile</goal></goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
<scalaCompatVersion>${scala.binary.version}</scalaCompatVersion>
<args>
<arg>-feature</arg>
<arg>-deprecation</arg>
</args>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<release>${maven.compiler.release}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<archive>
<manifest>
<mainClass>demo.Hello</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
关键依赖:
spark-connect-client-jvm_2.13是 Spark Connect 的 Scala 客户端库,scope设为provided,运行时由 Server 端提供。
编写 Scala 客户端代码
创建 src/main/scala/demo/Hello.scala:
scala
package demo
import org.apache.spark.sql.SparkSession
object Hello {
def main(args: Array[String]): Unit = {
val remote = "sc://localhost:15002"
val spark = SparkSession.builder().remote(remote).getOrCreate()
spark.range(0, 5).show()
spark.sql("SELECT 1+1 AS result").show(truncate = false)
import spark.implicits._
val df = Seq((1, "a"), (2, "b")).toDF("id", "val")
df.show()
println(s"[INFO] Spark version: ${spark.version}")
spark.stop()
}
}
编译打包
bash
cd ~/opt/code/demo-maven
mvn clean package
编译成功后会在 target/ 目录下生成 demo-maven-1.0.jar。
提交运行
bash
spark-submit --remote "sc://localhost:15002" --class demo.Hello target/demo-maven-1.0.jar
预期输出:
+---+
| id|
+---+
| 0|
| 1|
| 2|
| 3|
| 4|
+---+
+------+
|result|
+------+
|2 |
+------+
+---+---+
| id|val|
+---+---+
| 1| a|
| 2| b|
+---+---+
[INFO] Spark version: 4.1.2
小结
Spark Connect 本地模式的工作流程:
┌─────────────────┐ sc://localhost:15002 ┌──────────────────┐
│ Client (Python) │ ──────────────────────────▶ │ Spark Connect │
│ or (Scala/Java) │ │ Server │
│ │ ◀────────────────────────── │ (local[*]) │
│ spark-submit │ 结果返回 │ Spark 4.1.2 │
└─────────────────┘ └──────────────────┘
- Server:启动一次,常驻运行,负责执行所有计算任务
- Client :通过
sc://协议远程提交代码,无需本地 Spark 环境 - 适合本地开发测试,代码与集群模式完全兼容,迁移到生产环境只需修改连接地址
Standalone模式
Spark 4.x Standalone 模式配置文件名为
workers(原 SPARK 3.x 的slaves已弃用)。本文以三节点为例:lihaozhe01(Master + Worker)、lihaozhe02(Worker)、lihaozhe03(Worker)。前提:三节点已部署 JDK 21、Scala 2.13.18、Hadoop 3.x,且 HDFS 已启动(用于 Event Log),可通过ssh lhz@lihaozhe02/ssh lhz@lihaozhe03免密钥互访。
重命名 sbin 脚本(区分 Hadoop 启停)
为了避免与 Hadoop 的 start-all.sh / stop-all.sh 冲突,将 Spark 启停脚本重命名:
bash
cd $SPARK_HOME/sbin
mv start-all.sh start-spark.sh
mv stop-all.sh stop-spark.sh
进入配置目录
bash
cd $SPARK_HOME/conf
编写 spark-env.sh
bash
cp spark-env.sh.template spark-env.sh
vim spark-env.sh
在文件末尾追加:
bash
# ---- JDK 21(Spark 4.x 不支持 JDK 25)----
export JAVA_HOME=/home/lhz/opt/jdk-21
# ---- Netty 内存分配器(JDK 17+ 关键参数)----
export SPARK_DAEMON_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS -Dio.netty.allocator.type=unpooled"
export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Dio.netty.allocator.type=unpooled"
# ---- Standalone Master / Worker ----
export SPARK_MASTER_HOST=lihaozhe01
export SPARK_MASTER_PORT=7077
export SPARK_MASTER_WEBUI_PORT=8080
export SPARK_WORKER_MEMORY=2g
export SPARK_WORKER_CORES=2
export SPARK_WORKER_WEBUI_PORT=8081
# ---- History Server ----
export SPARK_HISTORY_OPTS="
-Dspark.history.ui.port=18080
-Dspark.history.retainedApplications=30
-Dspark.history.fs.logDirectory=hdfs://lihaozhe/spark-log"
SPARK_WORKER_MEMORY/SPARK_WORKER_CORES限制的是该 Worker 节点可分配给 Executors 的总内存与核数,按本机资源调整。
编辑 workers
bash
cp workers.template workers
vim workers
bash
lihaozhe01
lihaozhe02
lihaozhe03
配置 spark-defaults.conf
bash
cp spark-defaults.conf.template spark-defaults.conf
vim spark-defaults.conf
bash
# ---- 启用 Event Log ----
spark.eventLog.enabled true
spark.eventLog.dir hdfs://lihaozhe/spark-log
spark.eventLog.compress.codec snappy
# ---- History Server 日志目录 ----
spark.history.fs.logDirectory hdfs://lihaozhe/spark-log
创建 HDFS 日志目录
bash
hdfs dfs -mkdir -p /spark-log
hdfs dfs -chmod 1777 /spark-log
分发到其他节点
使用 rsync 同步(排除 logs/、work/、pids/):
bash
rsync -a --delete \
--exclude='logs/*' --exclude='work/*' --exclude='pids/*' \
/home/lhz/opt/spark-4/ lhz@lihaozhe02:/home/lhz/opt/spark-4/
rsync -a --delete \
--exclude='logs/*' --exclude='work/*' --exclude='pids/*' \
/home/lhz/opt/spark-4/ lhz@lihaozhe03:/home/lhz/opt/spark-4/
验证配置已同步:
bash
ssh lhz@lihaozhe02 "ls /home/lhz/opt/spark-4/sbin/start-spark.sh /home/lhz/opt/spark-4/conf/workers"
ssh lhz@lihaozhe03 "ls /home/lhz/opt/spark-4/sbin/start-spark.sh /home/lhz/opt/spark-4/conf/workers"
启动集群
确保 HDFS 已启动(通过 start-hadoop.sh),然后:
bash
$SPARK_HOME/sbin/start-spark.sh
$SPARK_HOME/sbin/start-history-server.sh
启动日志输出示例:
starting org.apache.spark.deploy.master.Master, logging to /home/lhz/opt/spark-4/logs/spark-lhz-org.apache.spark.deploy.master.Master-1-lihaozhe01.out
lihaozhe03: starting org.apache.spark.deploy.worker.Worker, ...
lihaozhe02: starting org.apache.spark.deploy.worker.Worker, ...
lihaozhe01: starting org.apache.spark.deploy.worker.Worker, ...
starting org.apache.spark.deploy.history.HistoryServer, logging to ...
验证集群状态
通过 curl 检查所有 UI:
bash
curl -s -o /dev/null -w "Master UI: %{http_code}\n" http://lihaozhe01:8080/
curl -s -o /dev/null -w "Worker 01 UI: %{http_code}\n" http://lihaozhe01:8081/
curl -s -o /dev/null -w "Worker 02 UI: %{http_code}\n" http://lihaozhe02:8081/
curl -s -o /dev/null -w "Worker 03 UI: %{http_code}\n" http://lihaozhe03:8081/
curl -s -o /dev/null -w "History UI: %{http_code}\n" http://lihaozhe01:18080/
全部应返回 200。
通过 Master REST API 查看集群状态:
bash
curl -s http://lihaozhe01:8080/json/ | python3 -c "
import json, sys
d = json.load(sys.stdin)
for w in d.get('workers', []):
print(f\" {w['host']} state={w['state']} cores={w['cores']} mem={w['memory']}MB\")
"
Web UI
- Master UI:http://lihaozhe01:8080
- Worker UI:http://lihaozhe01:8081、http://lihaozhe02:8081、http://lihaozhe03:8081
- History Server UI:http://lihaozhe01:18080
提交作业(三种方式验证集群)
方式一:spark-submit 内置示例(SparkPi)
关键:Spark 4.1.2 的 connect 包中
spark-submit默认走 Connect 模式(SPARK_CONNECT_MODE=1),需设置SPARK_CONNECT_MODE=0强制走集群模式。
bash
cd $SPARK_HOME
export SPARK_CONNECT_MODE=0
spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://lihaozhe01:7077 \
--deploy-mode client \
./examples/jars/spark-examples_2.13-4.1.2.jar \
100
预期输出末尾:
Pi is roughly 3.1415339141533916
方式二:Scala 项目(demo-maven)
bash
cd ~/opt/code/demo-maven
mvn clean package -DskipTests
export SPARK_CONNECT_MODE=0
spark-submit \
--class demo.Hello \
--master spark://lihaozhe01:7077 \
--deploy-mode client \
target/demo-maven-1.0.jar
预期输出:
+---+
| id|
+---+
| 0|
| 1|
| 2|
| 3|
| 4|
+---+
+------+
|result|
+------+
|2 |
+------+
+---+---+
| id|val|
+---+---+
| 1| a|
| 2| b|
+---+---+
[INFO] Spark version: 4.1.2
[OK] 全部测试通过!
方式三:Spark Connect Server + Python 客户端
启动 Connect Server(连接到 Standalone 集群):
bash
$SPARK_HOME/sbin/start-connect-server.sh \
--master spark://lihaozhe01:7077 \
--conf spark.connect.grpc.binding.port=15002
激活 venv 并运行 Python 客户端:
bash
cd ~/opt/code/demo-python
source venv/bin/activate
python src/app.py
预期输出:
[INFO] 连接 Spark Connect Server: sc://localhost:15002
[TEST] spark.range(5).show()
+---+
| id|
+---+
| 0|
| 1|
| 2|
| 3|
| 4|
+---+
[INFO] Spark version: 4.1.2
[OK] 全部测试通过!
提交作业到 Standalone 集群(cluster 模式)
bash
cd $SPARK_HOME
export SPARK_CONNECT_MODE=0
spark-submit \
--master spark://lihaozhe01:7077 \
--deploy-mode cluster \
--class org.apache.spark.examples.SparkPi \
./examples/jars/spark-examples_2.13-4.1.2.jar \
10
停止集群
bash
$SPARK_HOME/sbin/stop-spark.sh
$SPARK_HOME/sbin/stop-history-server.sh
# 停止 Connect Server
$SPARK_HOME/sbin/spark-daemon.sh stop org.apache.spark.sql.connect.service.SparkConnectServer 1
集群状态实测
完成上述三种方式测试后,Master UI 显示:
Workers:
- 192.168.10.101 state=ALIVE cores=2 mem=2048MB
- 192.168.10.102 state=ALIVE cores=2 mem=2048MB
- 192.168.10.103 state=ALIVE cores=2 mem=2048MB
Active Apps:
- Spark Connect server cores=6 # 占用了 3 worker × 2 cores
关于 SPARK_CONNECT_MODE
Spark 4.1.2 spark-4.1.2-bin-hadoop3-connect.tgz 包中的 spark-submit 脚本开头为:
bash
export SPARK_CONNECT_MODE=${SPARK_CONNECT_MODE:-1}
这意味着:
- 默认值
1:使用 Connect 模式(启动一个临时 Connect Server,客户端走 gRPC),此时不能与--master同时使用 - 设置为
0:使用传统模式(driver 端直连集群),可与--master同时使用
如需省略每次都输入 export SPARK_CONNECT_MODE=0,可在 ~/.bashrc 中添加:
bash
export SPARK_CONNECT_MODE=0
或者针对 Spark Connect 包彻底改回传统行为(不推荐,丧失 Connect 能力)。
HA模式(基于 ZooKeeper)
Spark 4.x HA 模式通过 ZooKeeper 实现 Master 主备切换。所有 Master 节点共享同一份 ZooKeeper 集群配置,任意时刻只有一个 Active Master,其余为 Standby。本文以三节点为例:
lihaozhe01/lihaozhe02/lihaozhe03均部署 Master 和 Worker。前提:ZooKeeper 集群(3 节点)已启动并通过zkServer.sh status验证一主两备。前置环境 :HDFS 集群(
start-hadoop.sh启动) + ZooKeeper 集群(zkServer.sh start三节点启动)。
完整配置文件
HA 模式复用 Standalone 模式的 workers、spark-defaults.conf,仅需修改 spark-env.sh 加入 ZK HA 配置。下列三个文件可直接复制使用。
配置文件 1:$SPARK_HOME/conf/spark-env.sh(完整内容)
bash
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This file is sourced when running various Spark programs.
# Copy it as spark-env.sh and edit that to configure Spark for your site.
# Options read when launching programs locally with
# ./bin/run-example or ./bin/spark-submit
# - HADOOP_CONF_DIR, to point Spark towards Hadoop configuration files
# - SPARK_LOCAL_IP, to set the IP address Spark binds to on this node
# - SPARK_PUBLIC_DNS, to set the public dns name of the driver program
# Options read by executors and drivers running inside the cluster
# - SPARK_LOCAL_IP, to set the IP address Spark binds to on this node
# - SPARK_PUBLIC_DNS, to set the public DNS name of the driver program
# - SPARK_LOCAL_DIRS, storage directories to use on this node for shuffle and RDD data
# Options read in any mode
# - SPARK_CONF_DIR, Alternate conf dir. (Default: ${SPARK_HOME}/conf)
# - SPARK_EXECUTOR_CORES, Number of cores for the executors (Default: 1).
# - SPARK_EXECUTOR_MEMORY, Memory per Executor (e.g. 1000M, 2G) (Default: 1G)
# - SPARK_DRIVER_MEMORY, Memory for Driver (e.g. 1000M, 2G) (Default: 1G)
# Options read in any cluster manager using HDFS
# - HADOOP_CONF_DIR, to point Spark towards Hadoop configuration files
# Options read in YARN client/cluster mode
# - YARN_CONF_DIR, to point Spark towards YARN configuration files when you use YARN
# Options for the daemons used in the standalone deploy mode
# - SPARK_MASTER_HOST, to bind the master to a different IP address or hostname
# - SPARK_MASTER_PORT / SPARK_MASTER_WEBUI_PORT, to use non-default ports for the master
# - SPARK_MASTER_OPTS, to set config properties only for the master (e.g. "-Dx=y")
# - SPARK_WORKER_CORES, to set the number of cores to use on this machine
# - SPARK_WORKER_MEMORY, to set how much total memory workers have to give executors (e.g. 1000m, 2g)
# - SPARK_WORKER_PORT / SPARK_WORKER_WEBUI_PORT, to use non-default ports for the worker
# - SPARK_WORKER_DIR, to set the working directory of worker processes
# - SPARK_WORKER_OPTS, to set config properties only for the worker (e.g. "-Dx=y")
# - SPARK_DAEMON_MEMORY, to allocate to the master, worker and history server themselves (default: 1g).
# - SPARK_HISTORY_OPTS, to set config properties only for the history server (e.g. "-Dx=y")
# - SPARK_SHUFFLE_OPTS, to set config properties only for the external shuffle service (e.g. "-Dx=y")
# - SPARK_DAEMON_JAVA_OPTS, to set config properties for all daemons (e.g. "-Dx=y")
# - SPARK_DAEMON_CLASSPATH, to set the classpath for all daemons
# - SPARK_PUBLIC_DNS, to set the public dns name of the master or workers
# Options for launcher
# - SPARK_LAUNCHER_OPTS, to set config properties and Java options for the launcher (e.g. "-Dx=y")
# Generic options for the daemons used in the standalone deploy mode
# - SPARK_CONF_DIR Alternate conf dir. (Default: ${SPARK_HOME}/conf)
# - SPARK_LOG_DIR Where log files are stored. (Default: ${SPARK_HOME}/logs)
# - SPARK_LOG_MAX_FILES Max log files of Spark daemons can rotate to. Default is 5.
# - SPARK_PID_DIR Where the pid file is stored. (Default: /tmp)
# - SPARK_IDENT_STRING A string representing this instance of spark. (Default: $USER)
# - SPARK_NICENESS The scheduling priority for daemons. (Default: 0)
# - SPARK_NO_DAEMONIZE Run the proposed command in the foreground. It will not output a PID file.
# Options for native BLAS, like Intel MKL, OpenBLAS, and so on.
# You might get better performance to enable these options if using native BLAS (see SPARK-21305).
# - MKL_NUM_THREADS=1 Disable multi-threading of Intel MKL
# - OPENBLAS_NUM_THREADS=1 Disable multi-threading of OpenBLAS
# Options for beeline
# - SPARK_BEELINE_OPTS, to set config properties only for the beeline cli (e.g. "-Dx=y")
# - SPARK_BEELINE_MEMORY, Memory for beeline (e.g. 1000M, 2G) (Default: 1G)
# ======================================================================
# ---- Custom: HA 模式完整配置(基于 Spark 4.1.2-bin-hadoop3-connect)----
# ======================================================================
# ---- JDK 21(Spark 4.x 不支持 JDK 25)----
export JAVA_HOME=/home/lhz/opt/jdk-21
# ---- Netty 内存分配器(JDK 17+ 关键参数)----
export SPARK_DAEMON_JAVA_OPTS="-Dio.netty.allocator.type=unpooled"
export SPARK_SUBMIT_OPTS="-Dio.netty.allocator.type=unpooled"
# ---- Standalone Master / Worker ----
# HA 模式下 SPARK_MASTER_HOST 不生效,Master 通过 ZK 选举产生
export SPARK_MASTER_PORT=7077
export SPARK_MASTER_WEBUI_PORT=8080
export SPARK_WORKER_MEMORY=2g
export SPARK_WORKER_CORES=2
export SPARK_WORKER_WEBUI_PORT=8081
# ---- History Server ----
export SPARK_HISTORY_OPTS="
-Dspark.history.ui.port=18080
-Dspark.history.retainedApplications=30
-Dspark.history.fs.logDirectory=hdfs://lihaozhe/spark-log"
# ---- ZooKeeper HA ----
# SPARK_DAEMON_JAVA_OPTS 包含所有 Spark 守护进程(Master/Worker/HistoryServer)的 JVM 参数
export SPARK_DAEMON_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS \
-Dspark.deploy.recoveryMode=ZOOKEEPER \
-Dspark.deploy.zookeeper.url=lihaozhe01:2181,lihaozhe02:2181,lihaozhe03:2181 \
-Dspark.deploy.zookeeper.dir=/spark-ha"
配置文件 2:$SPARK_HOME/conf/workers(完整内容)
lihaozhe01
lihaozhe02
lihaozhe03
配置文件 3:$SPARK_HOME/conf/spark-defaults.conf(完整内容)
properties
# ---- 启用 Event Log ----
spark.eventLog.enabled true
spark.eventLog.dir hdfs://lihaozhe/spark-log
spark.eventLog.compress.codec snappy
# ---- History Server 日志目录 ----
spark.history.fs.logDirectory hdfs://lihaozhe/spark-log
一次性分发到 02/03
bash
rsync -a /home/lhz/opt/spark-4/conf/spark-env.sh lhz@lihaozhe02:/home/lhz/opt/spark-4/conf/spark-env.sh
rsync -a /home/lhz/opt/spark-4/conf/spark-env.sh lhz@lihaozhe03:/home/lhz/opt/spark-4/conf/spark-env.sh
rsync -a /home/lhz/opt/spark-4/conf/workers lhz@lihaozhe02:/home/lhz/opt/spark-4/conf/workers
rsync -a /home/lhz/opt/spark-4/conf/workers lhz@lihaozhe03:/home/lhz/opt/spark-4/conf/workers
rsync -a /home/lhz/opt/spark-4/conf/spark-defaults.conf lhz@lihaozhe02:/home/lhz/opt/spark-4/conf/spark-defaults.conf
rsync -a /home/lhz/opt/spark-4/conf/spark-defaults.conf lhz@lihaozhe03:/home/lhz/opt/spark-4/conf/spark-defaults.conf
启动 HA 集群
1. 启动 ZooKeeper 集群(如果未启动)
三节点分别执行:
bash
zkServer.sh start
zkServer.sh status
验证选举结果:
bash
for h in lihaozhe01 lihaozhe02 lihaozhe03; do
echo "$h: $(ssh lhz@$h 'echo srvr | nc localhost 2181' | grep Mode)"
done
应看到类似:
lihaozhe01: Mode: follower
lihaozhe02: Mode: follower
lihaozhe03: Mode: leader
2. 在三个节点分别启动 Master
关键:HA 模式下不能用
start-spark.sh(原start-all.sh),它只在本节点启动 Master,其他 Master 需要手动启动。
bash
# 在 lihaozhe01
$SPARK_HOME/sbin/start-master.sh
# 在 lihaozhe02
ssh lhz@lihaozhe02 "$HOME/opt/spark-4/sbin/start-master.sh"
# 在 lihaozhe03
ssh lhz@lihaozhe03 "$HOME/opt/spark-4/sbin/start-master.sh"
3. 验证 ZK 选举结果
bash
for h in lihaozhe01 lihaozhe02 lihaozhe03; do
s=$(curl -s "http://$h:8080/json/" | python3 -c "import json,sys; print(json.load(sys.stdin).get('status','?'))")
echo " $h: status=$s"
done
预期输出(任一台为 ALIVE,其余为 STANDBY):
lihaozhe01: status=ALIVE
lihaozhe02: status=STANDBY
lihaozhe03: status=STANDBY
4. 在三个节点分别启动 Worker
bash
for h in lihaozhe01 lihaozhe02 lihaozhe03; do
ssh lhz@$h "$HOME/opt/spark-4/sbin/start-worker.sh spark://lihaozhe01:7077,lihaozhe02:7077,lihaozhe03:7077"
done
验证集群状态:
bash
curl -s "http://lihaozhe01:8080/json/" | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f'Master: {d[\"status\"]}')
for w in d.get('workers', []):
print(f' Worker: {w[\"host\"]} cores={w[\"cores\"]} mem={w[\"memory\"]}MB')
"
5. 启动 History Server
bash
$SPARK_HOME/sbin/start-history-server.sh
Web UI
- 任一 Master UI:http://lihaozhe01:8080、http://lihaozhe02:8080、http://lihaozhe03:8080
- Worker UI:http://lihaozhe01:8081、http://lihaozhe02:8081、http://lihaozhe03:8081
- History Server UI:http://lihaozhe01:18080
提交作业(三种方式验证)
关键:HA 模式下
spark-submit必须使用多 Master URL,客户端会自动尝试所有 Master,找到 Active Master 后提交。
方式一:spark-submit 内置示例(SparkPi)
bash
cd $SPARK_HOME
export SPARK_CONNECT_MODE=0
spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://lihaozhe01:7077,lihaozhe02:7077,lihaozhe03:7077 \
--deploy-mode client \
./examples/jars/spark-examples_2.13-4.1.2.jar \
100
预期输出末尾:
Pi is roughly 3.14...
方式二:Scala 项目(demo-maven)
bash
cd ~/opt/code/demo-maven
mvn clean package -DskipTests
export SPARK_CONNECT_MODE=0
spark-submit \
--class demo.Hello \
--master spark://lihaozhe01:7077,lihaozhe02:7077,lihaozhe03:7077 \
--deploy-mode client \
target/demo-maven-1.0.jar
预期输出末尾:[OK] 全部测试通过!
方式三:Spark Connect Server + Python 客户端
启动 Connect Server(连接到 HA 集群):
bash
$SPARK_HOME/sbin/start-connect-server.sh \
--master spark://lihaozhe01:7077,lihaozhe02:7077,lihaozhe03:7077 \
--conf spark.connect.grpc.binding.port=15002
激活 venv 并运行 Python 客户端:
bash
cd ~/opt/code/demo-python
source venv/bin/activate
python src/app.py
预期输出末尾:[OK] 全部测试通过!
验证 HA 切换(实测通过)
测试 1:杀 Active Master,观察自动切换
初始状态:
lihaozhe01: status=ALIVE workers=3
lihaozhe02: status=STANDBY
lihaozhe03: status=STANDBY
Kill 掉 lihaozhe01 上的 Active Master:
bash
MASTER_PID=$(ssh lhz@lihaozhe01 "ps -ef | grep -E 'java.*deploy.master.Master' | grep -v grep | awk '{print \$2}'")
ssh lhz@lihaozhe01 "kill $MASTER_PID"
等待约 30 秒后查看选举状态:
bash
sleep 30
for h in lihaozhe01 lihaozhe02 lihaozhe03; do
resp=$(curl -s "http://$h:8080/json/" 2>/dev/null)
s=$(echo "$resp" | python3 -c "import json,sys; print(json.load(sys.stdin).get('status','no-resp'))" 2>/dev/null)
workers=$(echo "$resp" | python3 -c "import json,sys; print(len(json.load(sys.stdin).get('workers',[])))" 2>/dev/null)
echo " $h: status=$s workers=$workers"
done
切换后状态:
lihaozhe01: status= (进程已死)
lihaozhe02: status=ALIVE workers=3 # 自动接管
lihaozhe03: status=STANDBY
测试 2:故障切换后提交新作业
lihaozhe01 Master 死掉后,把它的 Master 重启回到 STANDBY,然后用 Python + Connect 验证集群仍可用:
bash
# 重启 01 Master(回到集群,重新选举为 STANDBY)
ssh lhz@lihaozhe01 "$HOME/opt/spark-4/sbin/start-master.sh"
# 重启 Connect Server(绑定到当前 ALIVE Master,避免旧 session 卡住)
$HOME/opt/spark-4/sbin/spark-daemon.sh stop org.apache.spark.sql.connect.service.SparkConnectServer 1
$SPARK_HOME/sbin/start-connect-server.sh \
--master spark://lihaozhe02:7077 \
--conf spark.connect.grpc.binding.port=15002
# 运行 Python 客户端
cd ~/opt/code/demo-python && source venv/bin/activate && python src/app.py
预期输出末尾:[OK] 全部测试通过! --- 证明故障切换后集群仍能正常处理新作业。
注意事项
- 运行中的作业:Active Master 故障切换时,正在运行的作业会失败(driver 端与 Master 的 session 丢失),但集群可继续接收新作业。如需运行中作业不中断,应使用 cluster 模式 + driver 资源预留。
- Connect Server 重连:Connect Server 内部会缓存与 Active Master 的 session,切换后需要重启 Connect Server 绑定新 Master。
- 旧 Master 重新加入:被 kill 的 Master 重启后默认加入为 STANDBY,不会自动抢占 Active。
停止 HA 集群
bash
# 1. 停止 Connect Server(如有)
$SPARK_HOME/sbin/spark-daemon.sh stop org.apache.spark.sql.connect.service.SparkConnectServer 1
# 2. 在所有节点停止 Worker
for h in lihaozhe01 lihaozhe02 lihaozhe03; do
ssh lhz@$h "$HOME/opt/spark-4/sbin/stop-worker.sh"
done
# 3. 在所有节点停止 Master
for h in lihaozhe01 lihaozhe02 lihaozhe03; do
ssh lhz@$h "$HOME/opt/spark-4/sbin/stop-master.sh"
done
# 4. 停止 History Server
$SPARK_HOME/sbin/stop-history-server.sh
HA 集群状态实测
完成上述所有测试后,HA 集群状态:
Master 状态:
- lihaozhe01: STANDBY (曾为 Active,被 kill 后重启回 STANDBY)
- lihaozhe02: ALIVE (自动接管的 Active Master)
- lihaozhe03: STANDBY
Workers:
- 192.168.10.101 (lihaozhe01) cores=2 mem=2048MB
- 192.168.10.102 (lihaozhe02) cores=2 mem=2048MB
- 192.168.10.103 (lihaozhe03) cores=2 mem=2048MB
集群总资源: 6 cores / 6144MB