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角色
node01192.168.1.101NameNode(Active) + ResourceManager + ZK
node02192.168.1.102NameNode(Standby) + ResourceManager(Standby) + ZK
node03192.168.1.103DataNode + NodeManager + ZK + History Server
node04192.168.1.104DataNode + NodeManager
node05192.168.1.105DataNode + NodeManager
组件最低推荐(生产)
CPU4 核16-32 核
内存8 GB64-128 GB
磁盘100 GB SSD4×1TB HDD(DataNode) + 256GB SSD(系统)
网络1 GbE10 GbE
软件版本说明
OSCentOS 7.9 / Ubuntu 20.04生产环境建议 CentOS
JDK1.8.0_311Spark 3.x 最佳兼容版本
Hadoop3.3.4与 Spark 3.3.0 配套(预编译 Hadoop 3.x)
Spark3.3.0稳定版本
ZooKeeper3.7.1NameNode HA + Spark Master HA
Scala2.12.15Spark 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` 检查端口占用
8History 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

posted @ 2026-07-31 10:17  starzy  阅读(1)  评论(0)    收藏  举报