部署Spark Standalone 高可用集群

适用版本:Spark 4.0+(要求 JDK 17+,内置 Scala 2.13)、ZooKeeper 集群已部署完成(安装步骤省略,复用三节点 ZooKeeper 集群)。
集群规模:Spark Master × 2、Spark Worker × 3,通过 ZooKeeper 实现 Master 主备选举与状态恢复。


1. 集群规划

节点角色说明
node01Zookeeper + Master + WorkerMaster 主/备由 ZooKeeper 选举产生,不固定
node02Zookeeper + Master + Worker同上
node03Zookeeper + Worker纯计算节点

ZooKeeper 集群连接串(已部署):node01:2181,node02:2181,node03:2181

涉及端口:

端口用途
7077Master RPC 端口(应用、Worker 连接)
8080Master Web UI
8081Worker Web UI

2. 前置条件

2.1 安装 JDK 17(Spark 4.0 起不再支持 Java 8/11,三台节点均执行)

tar zxf jdk-17.0.11_linux-x64_bin.tar.gz -C /opt/

cat >> /etc/profile << 'EOF'
export JAVA_HOME=/opt/jdk-17.0.11
export PATH=$JAVA_HOME/bin:$PATH
EOF

source /etc/profile
java -version    # 应显示 17.x

2.2 主机名解析与免密登录

确认三台节点 /etc/hosts 已配置 node01/node02/node03 互相解析,且节点间 SSH 免密已配置(一键脚本依赖)。

2.3 确认 ZooKeeper 集群健康

zkServer.sh status    # 三台中应显示 1 个 leader、2 个 follower

3. 安装 Spark(三台节点均执行)

# 下载(以 4.0.0 为例,可替换为 4.x 更新版本)
wget https://archive.apache.org/dist/spark/spark-4.0.0/spark-4.0.0-bin-hadoop3.tgz

# 解压并创建软连接
tar zxf spark-4.0.0-bin-hadoop3.tgz -C /opt/
cd /opt
ln -s spark-4.0.0-bin-hadoop3 /opt/spark

4. 配置 spark-env.sh(HA 核心配置)

cd /opt/spark/conf
cp spark-env.sh.template spark-env.sh
vim spark-env.sh

添加如下内容:

export JAVA_HOME=/opt/jdk-17.0.11
export SPARK_HOME=/opt/spark

# 注意:HA 模式下务必注释掉或不要设置 SPARK_MASTER_HOST,
# 否则两个 Master 都会认为自己是 Leader,无法构成主备
# export SPARK_MASTER_HOST=node01
export SPARK_MASTER_PORT=7077
export SPARK_MASTER_WEBUI_PORT=8080
export SPARK_WORKER_DIR=/data/spark/work
# 日志目录(可选,默认在 $SPARK_HOME/logs)
export SPARK_LOG_DIR=/data/spark/logs

# ZooKeeper HA 配置(两个 Master 节点配置必须完全一致)
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER \
-Dspark.deploy.zookeeper.url=node01:2181,node02:2181,node03:2181 \
-Dspark.deploy.zookeeper.dir=/spark"

参数说明:

参数含义
spark.deploy.recoveryMode恢复模式,设为 ZOOKEEPER 启用主备选举
spark.deploy.zookeeper.urlZooKeeper 集群连接串(主机名:端口,逗号分隔)
spark.deploy.zookeeper.dirSpark 在 ZooKeeper 中存储集群状态(Worker、Driver、Application)的目录,默认 /spark

5. 配置 workers 文件

cp workers.template workers
vim /opt/spark/conf/workers

内容如下(即 3 个 Worker 节点):

node01
node02
node03

6. 配置 spark-defaults.conf(可选,推荐)

cp spark-defaults.conf.template spark-defaults.conf
vim /opt/spark/conf/spark-defaults.conf

添加如下内容:

# 应用默认连接的 Master 列表(两个 Master 都写上,逗号分隔)
spark.master spark://node01:7077,node02:7077

# 每个 Worker 节点可分配给 Executor 的最大内存
spark.worker.memory 16g
# 每个 Worker 节点可分配的 CPU 核数
spark.worker.cores 8

# 本地存储优化
spark.local.dir /opt/spark/local
# 应用完成后自动清理本地目录
spark.worker.cleanup.enabled true
# 清理周期(秒)
spark.worker.cleanup.interval 600
# 保留时间1天(秒)
spark.worker.cleanup.appDataTtl 3600 

若不在此配置,提交应用时需显式指定:
spark-submit --master spark://node01:7077,node02:7077 ...

7. 分发配置并配置环境变量

# 在 node01 上执行,将配置分发到另外两个节点(Spark 安装目录整体拷贝一次即可)
scp -r /opt/spark-4.0.0-bin-hadoop3 node02:/opt/
scp -r /opt/spark-4.0.0-bin-hadoop3 node03:/opt/
# node02、node03 上同样创建软连接
ln -s /opt/spark-4.0.0-bin-hadoop3 /opt/spark

# 三台节点均配置环境变量
cat >> /etc/profile << 'EOF'
export SPARK_HOME=/opt/spark
export PATH=$SPARK_HOME/bin:$SPARK_HOME/sbin:$PATH
EOF
source /etc/profile

# 创建日志、工作、临时目录
mkdir -p /data/spark/{logs,work,local}

8. 启动集群

启动顺序:先启动两个 Master(完成 ZooKeeper 选举),再启动 Worker。ZooKeeper 集群需已处于运行状态。

# node01 执行:启动 Master1 + 所有 Worker
start-all.sh

# node02 执行:启动 Master2(Standby)
start-master.sh

如果希望 Worker 启动时就同时感知两个 Master(推荐,故障切换更平滑),可改用以下方式启动 Worker:

# 三个 Worker 节点分别执行(或用一键脚本统一执行)
start-worker.sh spark://node01:7077,node02:7077

9. 验证高可用

9.1 查看进程

# node01、node02 上应有 Master 和 Worker 进程
jps | grep -E 'Master|Worker'

# node03 上应有 Worker 进程
jps | grep Worker

9.2 查看 Web UI 确认主备状态

访问 http://node01:8080 和 http://node02:8080,其中一个显示 Status: ALIVE(Active Master),另一个显示 Status: STANDBY。两个页面都能看到 3 个 Alive Worker。

如果两个 Master 都显示 ALIVE,说明 ZooKeeper HA 配置没有生效(通常是 SPARK_DAEMON_JAVA_OPTS 拼写错误、或配置了 SPARK_MASTER_HOST),此时两个 Master 各自独立调度,集群处于不健康状态,必须修复后重启。

9.3 故障切换测试

# 在 Active Master 所在节点杀掉 Master 进程
stop-master.sh        # 或 kill -9 <Master PID>

观察:

  1. 原 Standby Master 的 Web UI 状态变为 ALIVE,日志出现 I have been elected leader!;
  2. 等待约 1~2 分钟(新 Master 从 ZooKeeper 恢复 Worker/Application 状态),原 Standby 页面上 3 个 Worker 恢复 Alive;
  3. 期间正在运行的应用不受影响,仅新应用提交会被短暂阻塞。

恢复后,可将原 Master 重新启动作为新的 Standby:

start-master.sh

9.4 提交测试应用

spark-submit --master spark://node01:7077,node02:7077 \
  --class org.apache.spark.examples.SparkPi \
  /opt/spark/examples/jars/spark-examples_2.13-4.0.0.jar 100

10. 集群一键启停脚本

在 node01 创建 spark-cluster.sh:

vim /opt/spark/sbin/spark-cluster.sh

内容如下:

#!/bin/bash
SPARK_HOME=/opt/spark
MASTERS=(node01 node02)
WORKERS=(node01 node02 node03)
MASTER_URL="spark://node01:7077,node02:7077"

case "$1" in
  start)
    for m in "${MASTERS[@]}"; do
      echo "===== Starting Master on $m ====="
      ssh $m "$SPARK_HOME/sbin/start-master.sh"
    done
    echo "===== Waiting for master election (10s) ====="
    sleep 10
    for w in "${WORKERS[@]}"; do
      echo "===== Starting Worker on $w ====="
      ssh $w "$SPARK_HOME/sbin/start-worker.sh $MASTER_URL"
    done
    ;;
  stop)
    for w in "${WORKERS[@]}"; do
      echo "===== Stopping Worker on $w ====="
      ssh $w "$SPARK_HOME/sbin/stop-worker.sh"
    done
    for m in "${MASTERS[@]}"; do
      echo "===== Stopping Master on $m ====="
      ssh $m "$SPARK_HOME/sbin/stop-master.sh"
    done
    ;;
  status)
    for m in "${MASTERS[@]}" "${WORKERS[@]}"; do
      echo "===== $m ====="
      ssh $m "jps | grep -E 'Master|Worker' || echo 'no spark process'"
    done
    ;;
  *)
    echo "Usage: $0 {start|stop|status}"
    exit 1
esac

赋权并使用:

chmod +x /opt/spark/sbin/spark-cluster.sh
ln -s /opt/spark/sbin/spark-cluster.sh /usr/local/bin/spark-cluster

spark-cluster start     # 一键启动(先 Master 后 Worker)
spark-cluster status    # 查看各节点进程
spark-cluster stop      # 一键停止(先 Worker 后 Master)

11. 常见问题

  1. 两个 Master 都是 ALIVE:SPARK_DAEMON_JAVA_OPTS 未生效或配置了两个不同的 ZooKeeper 地址。检查三台节点 spark-env.sh 是否一致、参数拼写是否正确,修复后重启全部 Master。
  2. Worker 一直处于失联状态:检查防火墙是否放行 7077/8080/8081 端口,以及 Worker 启动命令中的 Master 地址是否正确。
  3. 切换后 Worker 长时间不恢复:正常,新 Leader 需要 1~2 分钟从 ZooKeeper 恢复状态并等待 Worker 重新注册;期间可查看新 Master 日志 $SPARK_LOG_DIR/spark-*-Master-*.out 中的 Recovery complete 字样确认恢复完成。
  4. 应用提交报错 Master 不可连接:确认应用使用的 master URL 同时包含两个 Master(spark://node01:7077,node02:7077),否则 Active Master 故障时应用无法自动切换。