Spark Core 企业级分布式集群搭建:从零到高可用生产环境部署全流程
SparkCore 之 Spark 企业级分布式集群搭建
摘要:从零搭建企业级 Spark 分布式集群——涵盖集群规划、节点角色分配、SSH 免密、JDK 安装、Hadoop/YARN 基础集群部署、Spark 集群安装配置、ZooKeeper 高可用、History Server、性能调优参数、集群验证测试和生产环境检查清单。配有 4 张原创架构图,面向 Java、大数据及 AI 开发工程师,读完即可动手部署生产级 Spark 集群。
一、集群规划与架构总览
1.1 集群拓扑
本次搭建以 5 节点 企业级集群为例,采用 Spark on YARN 模式:
1.2 硬件规格建议
1.3 软件版本
| 节点 | IP | 角色 |
|---|---|---|
| node01 | 192.168.1.101 | NameNode(Active) + ResourceManager + ZK |
| node02 | 192.168.1.102 | NameNode(Standby) + ResourceManager(Standby) + ZK |
| node03 | 192.168.1.103 | DataNode + NodeManager + ZK + History Server |
| node04 | 192.168.1.104 | DataNode + NodeManager |
| node05 | 192.168.1.105 | DataNode + NodeManager |
| 组件 | 最低 | 推荐(生产) |
| CPU | 4 核 | 16-32 核 |
| 内存 | 8 GB | 64-128 GB |
| 磁盘 | 100 GB SSD | 4×1TB HDD(DataNode) + 256GB SSD(系统) |
| 网络 | 1 GbE | 10 GbE |
| 软件 | 版本 | 说明 |
| OS | CentOS 7.9 / Ubuntu 20.04 | 生产环境建议 CentOS |
| JDK | 1.8.0_311 | Spark 3.x 最佳兼容版本 |
| Hadoop | 3.3.4 | 与 Spark 3.3.0 配套(预编译 Hadoop 3.x) |
| Spark | 3.3.0 | 稳定版本 |
| ZooKeeper | 3.7.1 | NameNode HA + Spark Master HA |
| Scala | 2.12.15 | Spark 3.3.0 编译版本 |
二、环境准备(所有节点执行)
2.1 配置主机名和 hosts
# 每个节点设置主机名
hostnamectl set-hostname node01 # node02/node03/node04/node05 分别执行
# 所有节点统一 /etc/hosts
cat >> /etc/hosts << 'EOF'
192.168.1.101 node01
192.168.1.102 node02
192.168.1.103 node03
192.168.1.104 node04
192.168.1.105 node05
EOF
2.2 关闭防火墙和 SELinux
# 所有节点执行
systemctl stop firewalld
systemctl disable firewalld
# 关闭 SELinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
⚠️ 生产环境安全建议:不要直接关闭防火墙,应配置具体端口规则。此处为简化部署流程。
2.3 配置 SSH 免密登录(node01 → 所有节点)
# 在 node01 上执行
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
# 分发公钥到所有节点(包括自己)
for host in node01 node02 node03 node04 node05; do
ssh-copy-id $host
done
# 验证免密
for host in node01 node02 node03 node04 node05; do
ssh $host "hostname"
done
2.4 安装 JDK 8(所有节点)
# CentOS
yum install -y java-1.8.0-openjdk-devel
# Ubuntu
apt install -y openjdk-8-jdk
# 验证并配置 JAVA_HOME
echo 'export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk' >> /etc/profile
source /etc/profile
java -version
2.5 时钟同步(所有节点)
# 安装 NTP
yum install -y ntp
# 启动并设为开机自启
systemctl start ntpd
systemctl enable ntpd
# 手动同步(可选)
ntpdate -u ntp.aliyun.com
集群时间不同步会导致 ZooKeeper 会话超时、Kerberos 认证失败等诡异问题。生产环境务必配置 NTP。
三、Hadoop/YARN 基础集群部署
Spark on YARN 依赖 Hadoop 的 HDFS 和 YARN 组件。以下是精简的生产级配置。
3.1 下载并解压 Hadoop(node01 操作,后分发)
# 下载 Hadoop 3.3.4
cd /opt
wget https://archive.apache.org/dist/hadoop/common/hadoop-3.3.4/hadoop-3.3.4.tar.gz
tar -xzf hadoop-3.3.4.tar.gz
ln -s hadoop-3.3.4 hadoop
# 配置环境变量(/etc/profile)
cat >> /etc/profile << 'EOF'
export HADOOP_HOME=/opt/hadoop
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
EOF
source /etc/profile
3.2 核心配置文件
#### core-site.xml
<configuration>
<!-- NameNode 地址 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<!-- 临时目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/data/hadoop/tmp</value>
</property>
<!-- 权限检查(测试环境可关闭) -->
<property>
<name>hadoop.http.staticuser.user</name>
<value>hadoop</value>
</property>
</configuration>
#### hdfs-site.xml
<configuration>
<!-- NameNode 数据目录 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>/data/hadoop/namenode</value>
</property>
<!-- DataNode 数据目录 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>/data/hadoop/datanode</value>
</property>
<!-- 副本数(生产建议 3) -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- HA 配置 -->
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>node01:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>node02:8020</value>
</property>
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node01:8485;node02:8485;node03:8485/mycluster</value>
</property>
<!-- 故障自动切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
</configuration>
#### yarn-site.xml
<configuration>
<!-- ResourceManager HA -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>node01</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>node02</value>
</property>
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>node01:2181,node02:2181,node03:2181</value>
</property>
<!-- Shuffle Service(Spark 动态资源分配必须) -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>spark_shuffle,mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
<value>org.apache.spark.network.yarn.YarnShuffleService</value>
</property>
<!-- NodeManager 资源 -->
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>49152</value> <!-- 48GB -->
</property>
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>16</value>
</property>
</configuration>
#### workers 文件
node03
node04
node05
3.3 分发到所有节点并初始化
# 在 node01 上分发 Hadoop
for host in node02 node03 node04 node05; do
scp -r /opt/hadoop-3.3.4 $host:/opt/
ssh $host "ln -s /opt/hadoop-3.3.4 /opt/hadoop"
scp /etc/profile $host:/etc/profile
done
# 格式化 ZK(node01)
hdfs zkfc -formatZK
# 启动 JournalNode(node01/node02/node03)
hdfs --daemon start journalnode
# 格式化 NameNode(node01)
hdfs namenode -format
# 启动 NameNode(node01)
hdfs --daemon start namenode
# 同步 NameNode(node02)
ssh node02 "hdfs namenode -bootstrapStandby"
# 启动集群
start-dfs.sh # 启动 HDFS
start-yarn.sh # 启动 YARN
# 在 node02 启动 Standby ResourceManager
ssh node02 "yarn --daemon start resourcemanager"
# 验证
hdfs dfsadmin -report
yarn node -list
访问 Web UI:NameNode http://node01:9870 · YARN RM http://node01:8088
四、ZooKeeper 集群部署
4.1 安装(node01/node02/node03)
# 下载
cd /opt
wget https://archive.apache.org/dist/zookeeper/zookeeper-3.7.1/apache-zookeeper-3.7.1-bin.tar.gz
tar -xzf apache-zookeeper-3.7.1-bin.tar.gz
ln -s apache-zookeeper-3.7.1-bin zookeeper
# 配置
cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
cat > /opt/zookeeper/conf/zoo.cfg << 'EOF'
tickTime=2000
dataDir=/data/zookeeper/data
dataLogDir=/data/zookeeper/logs
clientPort=2181
initLimit=10
syncLimit=5
server.1=node01:2888:3888
server.2=node02:2888:3888
server.3=node03:2888:3888
EOF
# 创建 myid
mkdir -p /data/zookeeper/data
echo 1 > /data/zookeeper/data/myid # node01=1, node02=2, node03=3
4.2 启动 ZooKeeper
# node01/node02/node03 分别执行
/opt/zookeeper/bin/zkServer.sh start
# 验证
/opt/zookeeper/bin/zkServer.sh status
# 输出: Mode: follower / Mode: leader
五、Spark 集群安装配置
5.1 下载并分发
# node01 上下载
cd /opt
wget https://archive.apache.org/dist/spark/spark-3.3.0/spark-3.3.0-bin-hadoop3.tgz
tar -xzf spark-3.3.0-bin-hadoop3.tgz
ln -s spark-3.3.0-bin-hadoop3 spark
# 分发到所有节点
for host in node02 node03 node04 node05; do
scp -r /opt/spark-3.3.0-bin-hadoop3 $host:/opt/
ssh $host "ln -s /opt/spark-3.3.0-bin-hadoop3 /opt/spark"
done
# 环境变量(所有节点)
cat >> /etc/profile << 'EOF'
export SPARK_HOME=/opt/spark
export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin
EOF
source /etc/profile
5.2 Spark 核心配置文件
#### spark-defaults.conf
# ===== 运行模式 =====
spark.master yarn
spark.submit.deployMode cluster
# ===== 序列化 =====
spark.serializer org.apache.spark.serializer.KryoSerializer
spark.kryoserializer.buffer.max 512m
# ===== 动态资源分配 =====
spark.dynamicAllocation.enabled true
spark.dynamicAllocation.minExecutors 2
spark.dynamicAllocation.maxExecutors 50
spark.dynamicAllocation.initialExecutors 5
spark.dynamicAllocation.executorIdleTimeout 120s
spark.shuffle.service.enabled true
# ===== Shuffle =====
spark.sql.shuffle.partitions 200
spark.shuffle.compress true
spark.shuffle.spill.compress true
spark.io.compression.codec snappy
# ===== 事件日志(History Server 需要) =====
spark.eventLog.enabled true
spark.eventLog.dir hdfs://mycluster/spark-history
spark.history.fs.logDirectory hdfs://mycluster/spark-history
spark.history.ui.port 18080
spark.history.retainedApplications 100
# ===== SQL 自适应执行 =====
spark.sql.adaptive.enabled true
spark.sql.adaptive.coalescePartitions.enabled true
spark.sql.adaptive.skewJoin.enabled true
# ===== Driver/Executor =====
spark.driver.memory 2g
spark.executor.memory 4g
spark.executor.cores 2
spark.driver.extraJavaOptions -XX:+UseG1GC
spark.executor.extraJavaOptions -XX:+UseG1GC
# ===== 日志 =====
spark.executor.logs.rolling.maxRetainedFiles 10
spark.executor.logs.rolling.strategy time
spark.executor.logs.rolling.time.interval daily
#### spark-env.sh
# 从模板复制
cp $SPARK_HOME/conf/spark-env.sh.template $SPARK_HOME/conf/spark-env.sh
cat >> $SPARK_HOME/conf/spark-env.sh << 'EOF'
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
export HADOOP_HOME=/opt/hadoop
export HADOOP_CONF_DIR=/opt/hadoop/etc/hadoop
export YARN_CONF_DIR=/opt/hadoop/etc/hadoop
# History Server
export SPARK_HISTORY_OPTS="-Dspark.history.fs.logDirectory=hdfs://mycluster/spark-history"
EOF
5.3 创建 HDFS 目录
# 创建 Spark 事件日志目录
hdfs dfs -mkdir -p /spark-history
hdfs dfs -chmod 777 /spark-history
# 创建 Spark 上传临时目录(K8s 模式用)
hdfs dfs -mkdir -p /spark-upload
hdfs dfs -chmod 777 /spark-upload
5.4 启动 History Server
# 在 node03 上启动
$SPARK_HOME/sbin/start-history-server.sh
# 验证
curl http://node03:18080
5.5 启动 YARN Shuffle Service
# 在所有 NodeManager 节点(node03/node04/node05)执行
# 前提:已将 spark-<version>-yarn-shuffle.jar 复制到 yarn lib 目录
cp $SPARK_HOME/yarn/spark-3.3.0-yarn-shuffle.jar $HADOOP_HOME/share/hadoop/yarn/lib/
六、集群验证测试
6.1 基础功能验证
# 1. spark-shell 测试
spark-shell --master yarn --deploy-mode client
# 在 spark-shell 中执行
val rdd = sc.parallelize(1 to 1000000)
rdd.count()
// 输出: res0: Long = 1000000
# 2. SparkPi 示例(YARN cluster 模式)
spark-submit \
--master yarn \
--deploy-mode cluster \
--class org.apache.spark.examples.SparkPi \
--executor-memory 2G \
--num-executors 3 \
$SPARK_HOME/examples/jars/spark-examples_2.12-3.3.0.jar \
1000
# 查看 YARN 日志
yarn logs -applicationId application_xxx
6.2 性能基准测试
# 大规模数据 Shuffle 测试(验证网络和磁盘 IO)
spark-submit \
--master yarn \
--deploy-mode cluster \
--name "Benchmark-ShuffleTest" \
--executor-memory 4G \
--executor-cores 2 \
--num-executors 5 \
--conf spark.sql.shuffle.partitions=100 \
--class org.apache.spark.examples.GroupByTest \
$SPARK_HOME/examples/jars/spark-examples_2.12-3.3.0.jar \
50 10000000 100
6.3 检查清单
#!/bin/bash
# 集群健康检查脚本
echo "=== 1. ZooKeeper 状态 ==="
for host in node01 node02 node03; do
echo -n "$host: "; ssh $host "/opt/zookeeper/bin/zkServer.sh status 2>/dev/null | grep Mode"
done
echo "=== 2. HDFS 状态 ==="
hdfs dfsadmin -report | grep -E "Live datanodes|DFS Used%"
echo "=== 3. YARN 状态 ==="
yarn node -list | grep -c "RUNNING"
echo " 个 NodeManager 运行中"
echo "=== 4. Spark History Server ==="
curl -s -o /dev/null -w "%{http_code}" http://node03:18080
echo " (200=正常)"
echo "=== 5. Web UI 可访问性 ==="
for url in "http://node01:9870" "http://node01:8088" "http://node03:18080"; do
echo -n "$url: "; curl -s -o /dev/null -w "%{http_code}" $url; echo ""
done
七、高可用架构全景
HA 故障演练
# 1. 模拟 Active NameNode 宕机
ssh node01 "jps | grep NameNode"
ssh node01 "kill -9 \$(jps | grep NameNode | awk '{print \$1}')"
# 2. 观察自动故障切换(10-30 秒内完成)
hdfs haadmin -getServiceState nn1 # 应为 standby
hdfs haadmin -getServiceState nn2 # 应为 active
# 3. 验证 HDFS 读写不受影响
hdfs dfs -ls /
hdfs dfs -put /tmp/test.txt /test.txt
# 4. 恢复 node01 NameNode
ssh node01 "hdfs --daemon start namenode"
hdfs haadmin -getServiceState nn1 # 应为 standby
八、部署流程全景
九、常见问题排查
| # | 问题 | 原因 | 解决 |
|---|---|---|---|
| 1 | `NameNode not formatted` | 首次启动未 format | `hdfs namenode -format` |
| 2 | `Connection refused: node01/192.168.1.101:8020` | NameNode 未启动 | 检查 `hdfs --daemon start namenode` |
| 3 | `ZKFC not elected` | ZK 集群未启动或不完整 | 3 个 ZK 节点必须 2 个以上正常运行 |
| 4 | `Container killed by YARN: exit code 137` | Executor OOM | 加大 `spark.executor.memory` 或 `memoryOverhead` |
| 5 | `ClassNotFoundException: YarnShuffleService` | spark-yarn-shuffle.jar 未部署 | 复制 jar 到 `$HADOOP_HOME/share/hadoop/yarn/lib/` |
| 6 | `spark-shell 卡在 "Submitting application"` | YARN 资源不足 | 检查 `yarn node -list` 和队列容量 |
| 7 | `java.net.BindException: Address already in use` | 端口冲突 | `netstat -tlnp` 检查端口占用 |
| 8 | History Server 无数据 | eventLog 目录未创建或权限 | `hdfs dfs -chmod 777 /spark-history` |
写在最后
搭建企业级 Spark 集群不是"下载解压启动"三步走,而是一整套系统工程——SSH 免密、时钟同步、HDFS HA、YARN HA、ZooKeeper、Shuffle Service、History Server,每个环节都有坑。
本文从集群规划到最终验证,覆盖了完整的企业级部署流程。遵循本文的配置和检查清单,你可以搭建出一个具备高可用、动态资源分配、事件日志回溯能力的生产级 Spark 集群。
集群稳了,数据才能跑起来。
starzy · AI Data Engineer · blog.starzy.cn · GitHub: starzy1990.github.io

浙公网安备 33010602011771号