Linux下Spark开发环境搭建:从版本选型到集群实战

发布时间:2026/9/17 5:12:03
Linux下Spark开发环境搭建:从版本选型到集群实战 1. 为什么Spark环境搭建这么容易卡壳我见过不少刚接触Spark的同事第一周几乎都在跟环境搏斗有人卡在版本不匹配装完Spark起不来有人被一大串ClassNotFoundException整到怀疑人生还有人跟着老教程配了半天结果连spark-shell都进不去。说实话Spark的开发环境搭建本身不算复杂但它踩坑的点非常集中——版本、依赖、配置、权限任何一个环节出问题都会在启动那一下集中爆发。这篇博文的定位很明确基于Linux环境带你从零开始完整搭建一套Spark开发环境从版本选型、JDK/Scala/Python准备、Spark安装、核心配置文件逐项拆解到本地模式验证、Standalone集群启动最后把最常见的三类故障排查链路完整走一遍。适合第一次接触Spark、或者之前只在Windows上跑过单机示例、现在想正经在Linux上搭一套可扩展开发环境的同学参考。我会尽量把每一步“为什么要这么做”讲清楚而不是扔给你一条命令了事。因为只有理解了配置背后的逻辑后面遇到诡异报错时你才知道往哪个方向查。2. 动手之前版本选型与全局规划清单一很多教程上来就让你下载安装但版本选型才是这整套环境能不能稳定跑起来的关键。Spark的版本号只是表面真正影响你安装决策的是它和Hadoop、JDK、Scala、Python之间的匹配关系。2.1 为什么我必须先强调Spark与Hadoop版本的匹配Spark本身不存储数据它需要对接存储层。本地开发时可以不依赖HDFS但生产环境的Spark必然要读写HDFS。Spark官方发行版分两种一种是源码包需要自己用Maven编译除非你要改源码否则不推荐另一种是预编译版本官网会明确标注Pre-built for Apache Hadoop 3.3.4之类的字样下载解压就能用。预编译版本里已经内置了对应Hadoop版本的客户端依赖省去了你自己处理依赖冲突的痛苦。我的建议是如果你没有特殊要求直接选Spark 3.5.x Hadoop 3.3.x的预编译包这是目前我用下来最稳的组合。组件推荐版本说明Linux发行版Ubuntu 20.04/22.04 LTS或CentOS 7.9内核版本不要太老影响网络和IO性能JDK8或11首选11Spark 3.x在JDK 8/11上最稳妥17部分版本有兼容问题Spark3.5.x当前稳定版API和生态最成熟Hadoop3.3.x与预编译版匹配即可本地不装HDFS也行Python3.8~3.10PySpark对3.11/3.12的支持目前仍然比较保守Scala2.12跟随Spark自带的Scala版本不要自己另装除非你需要开发Scala应用这里插一句很多新人会问“我已经装了Hadoop还需要再装一遍吗”——如果你只是搭建Spark开发环境跑示例和开发调试不强制装HDFS。Spark本地模式用file://协议读文件就行等真正要模拟分布式存储时再补Hadoop不迟。但如果你打算跑Standalone集群并且希望它有生产感还是建议配上HDFS。2.2 JDK、Scala、Python的选择逻辑JDK是硬依赖。Spark本身用Scala编写运行在JVM之上没有JDK连start-master.sh都跑不起来。我的习惯是装OpenJDK 11不要装Oracle JDK除非公司有明确要求。OpenJDK在Ubuntu上一条apt install openjdk-11-jdk就能装好省事。Scala这块最容易出问题。Spark 3.5默认使用Scala 2.12编译你在写Spark应用时也建议用Scala 2.12版本否则会遇到二进制不兼容的报错。但要注意如果你只是用spark-shell或spark-submit跑Spark自带的程序不需要单独安装Scala——Spark发行包里的jars/目录已经带了必需的Scala库。很多人以为要单独配Scala环境其实那是开发Scala应用时才需要做的事。Python的选择则关系到你用不用PySpark。如果只用Scala写Spark作业Python版本不匹配也无所谓但只要你会用到pyspark就必须把PYSPARK_PYTHON环境变量指到可用的Python解释器上。而且用PySpark时要注意driver和executor用的是同一个PYSPARK_PYTHON路径集群里每台机器都要有相同版本的Python否则执行阶段必然报ModuleNotFoundError。2.3 资源需求与目录规划别小看资源规划很多环境起不来就是因为磁盘被日志塞满或者内存分配超出机器物理上限。磁盘Spark安装包解压后约3GB加上日志和临时文件建议预留20GB以上。内存本地模式至少4GB可用内存如果跑Standalone集群每台Worker建议8GB以上。CPU开发环境2~4核够用集群环境按需求调整。目录规划我推荐一套通用方案/opt/spark # Spark主目录通过软链接指向解压目录 /data/spark/tmp # Spark临时文件目录 /data/spark/logs # Spark日志目录 /home/spark # 专门用户的主目录存放脚本和测试代码为什么要拆这么细因为Spark运行时会大量写临时文件日志也会持续增长。如果你把临时目录放在根分区或者系统目录跑几天就会发现磁盘满了。专门划一个数据盘挂载到/data后面清理和扩容都方便。3. Linux环境基础依赖的安装与验证Spark安装本身的步骤不多但基础依赖如果没准备好后面每一步都可能炸。这一节我按顺序讲清楚每项依赖怎么装、怎么验证。3.1 JDK安装tar包方式为什么更可控在Ubuntu上安装OpenJDK 11最快的方式是sudo apt update sudo apt install -y openjdk-11-jdk但我个人更推荐用tar包方式安装尤其当你需要在多台集群机器上保持完全一致的JDK版本时。apt源里的版本可能随源更新而变化而tar包可以锁死版本# 下载OpenJDK 11的tar包解压到/opt/jdk-11 sudo tar -zxvf openjdk-11.0.21_linux-x64_bin.tar.gz -C /opt/ # 创建软链接方便更换版本 sudo ln -s /opt/jdk-11.0.21 /opt/jdk然后配置JAVA_HOME环境变量echo export JAVA_HOME/opt/jdk ~/.bashrc echo export PATH$JAVA_HOME/bin:$PATH ~/.bashrc source ~/.bashrc验证java -version如果输出类似openjdk version 11.0.21说明JDK就绪。这一步错不了但如果你的Linux环境之前装过其他版本JDK务必确认java -version输出的确实是你指定的版本不要被PATH顺序干扰。3.2 Python环境的坑提前踩掉如果你打算用PySparkPython的安装要特别注意版本。根据我的实测Python 3.8~3.10和Spark 3.5配合最顺畅Python 3.11也能用但偶尔会碰到第三方库还没适配的情况Python 3.12则建议避开。# Ubuntu上安装Python 3.10 sudo apt install -y python3.10 python3.10-venv python3-pip安装完后一定要明确PYSPARK_PYTHON指向哪个解释器echo export PYSPARK_PYTHON/usr/bin/python3.10 ~/.bashrc这个变量很多人容易忽略但Spark在执行Python任务时会通过它找到Python解释器。不设置的话Spark会默认使用python命令而某些Linux系统里python默认指向Python 2分分钟报错。3.3 SSH免密登录与hosts解析这一节主要面向Standalone集群模式。如果你只跑本地模式可以跳过但建议还是配好毕竟后面一旦扩展到多节点就会用到。# 生成密钥对已有可跳过 ssh-keygen -t rsa -P -f ~/.ssh/id_rsa # 将公钥加入 authorized_keys cat ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys验证ssh localhost如果不用输密码就能登录说明免密配置成功。同时检查/etc/hosts确保本机的主机名能被解析# 把本机IP和主机名加入 hosts echo 192.168.1.100 spark-master | sudo tee -a /etc/hosts hostname # 确认主机名这个细节很容易被忽略但Spark的Master启动时会注册主机名Worker节点也需要通过主机名访问Master。如果主机名解析不了你会看到Worker一直在报Unable to connect to master。4. Spark本体安装下载、解压与目录结构基础依赖准备好后就可以装Spark本体了。这里每一步我都会写清楚验证方式避免“装完之后不知道成没成功”的尴尬。4.1 下载预编译包源码包不要碰进入 Spark官网下载页面 选择Spark版本3.5.x最新稳定版包类型Pre-built for Apache Hadoop 3.3.4或3.3.x下载方式直接下载tgz# 用wget下载到/opt目录 sudo wget https://archive.apache.org/dist/spark/spark-3.5.1/spark-3.5.1-bin-hadoop3.tgz -P /opt/下载完成后建议先做sha512校验官网提供了校验文件。这一步虽然多花半分钟但能防止下载到损坏的包。校验方法cd /opt echo 从官网复制的sha512值 spark-3.5.1-bin-hadoop3.tgz | sha512sum -c -输出OK就说明文件完整。4.2 解压与目录迁移sudo tar -zxvf /opt/spark-3.5.1-bin-hadoop3.tgz -C /opt/ # 创建软链接统一管理版本 sudo ln -s /opt/spark-3.5.1-bin-hadoop3 /opt/spark # 创建你规划好的目录 sudo mkdir -p /data/spark/tmp /data/spark/logs sudo chown -R $(whoami) /data/spark /opt/spark*这里我把/opt/spark用软链接指向具体版本目录好处是以后升级Spark时只需要重新解压、改软链接环境变量和配置都不用动。解压完成后建议花一分钟时间熟悉一下目录结构/opt/spark/ ├── bin/ # 可执行脚本spark-shell, spark-submit等 ├── sbin/ # 集群管理脚本start-master.sh, start-worker.sh等 ├── conf/ # 核心配置文件目录 ├── jars/ # 所有依赖jar包 ├── examples/ # 官方示例 ├── python/ # PySpark源码与包 └── data/ # 一些示例数据如果你需要经常和Spark打交道建议把bin/目录加入PATH但如果只是偶尔用直接用全路径也可以。4.3 环境变量配置与验证编辑~/.bashrc追加以下内容export SPARK_HOME/opt/spark export PATH$SPARK_HOME/bin:$SPARK_HOME/sbin:$PATH export HADOOP_CONF_DIR/opt/spark/conf # 如果没有Hadoop这一行可以省然后source ~/.bashrc spark-submit --version正常情况下会输出Welcome to Spark和版本信息。如果这里报错99%的原因是JAVA_HOME没设置好回到第3.1节检查。5. Spark配置文件的修改逻辑不仅要改还要懂为什么Spark安装完可以直接跑但要跑得舒服、跑得明白必须理解它的几个核心配置文件。很多人一上来就照着网上的配置模板复制粘贴结果参数填错或者含义理解偏差出了问题根本无从排查。5.1 spark-env.sh集群运行时的环境入口在$SPARK_HOME/conf目录下先把模板复制一份cp $SPARK_HOME/conf/spark-env.sh.template $SPARK_HOME/conf/spark-env.sh这个文件里可以配置全局环境变量。以下是开发环境很常用的一组配置# 指定JDK路径如果用tar包安装这里必须显式指定 export JAVA_HOME/opt/jdk # 指定Python解释器 export PYSPARK_PYTHON/usr/bin/python3.10 # Master节点主机名 export SPARK_MASTER_HOSTspark-master # Worker节点的CPU核心数与内存 export SPARK_WORKER_CORES4 export SPARK_WORKER_MEMORY8g # 日志目录 export SPARK_LOG_DIR/data/spark/logs # 临时目录 export SPARK_TMP_DIR/data/spark/tmp每项配置的含义JAVA_HOME如果你是通过apt装的OpenJDK可以省略如果是tar包方式必须指定否则Spark找不到JVM。SPARK_MASTER_HOSTMaster绑定的主机名或IP。如果不设置默认取本机主机名一旦/etc/hosts里没有对应映射Worker连不上就是这里出的问题。SPARK_WORKER_CORES和SPARK_WORKER_MEMORY决定每个Worker节点能分配多少资源给Executor。注意这个值要小于物理机的实际资源比如8GB内存的机器可以设6g留一部分给操作系统和JVM自身开销。我看到有人把SPARK_WORKER_MEMORY直接设成和物理内存一样大结果OS直接OOM这是初学者很容易踩的坑。5.2 spark-defaults.conf作业级参数的默认值同样先复制模板cp $SPARK_HOME/conf/spark-defaults.conf.template $SPARK_HOME/conf/spark-defaults.conf这个文件控制的不是集群本身而是每个Spark作业提交时的默认参数。推荐的开发配置spark.master spark://spark-master:7077 spark.driver.memory 2g spark.executor.memory 2g spark.executor.cores 2 spark.serializer org.apache.spark.serializer.KryoSerializer spark.sql.shuffle.partitions 200 spark.ui.port 4040重点解释两个spark.serializer: Spark默认使用Java序列化但性能和压缩率不如Kryo。开发阶段可以不换但生产环境强烈建议换成Kryo。不过Kryo需要注册类如果你用了自定义类要在代码里做配置否则反而可能报序列化错误。spark.sql.shuffle.partitions: 默认值是200这个参数和Shuffle时的分区数有关。如果你的数据量不大200个分区会产生大量空任务白白浪费资源。在开发调试时可以调小到20~50让任务跑快一点。5.3 workers文件与日志级别在Standalone集群中Master靠conf/workers文件来发现Worker节点。默认情况下这个文件叫workers.template打开后把需要作为Worker的机器主机名或IP填进去一行一个spark-master spark-worker-1 spark-worker-2如果你只有一台机器做伪分布式就填本机主机名。这里要注意Master是靠SSH去连接并启动各个Worker进程的所以第3.3节做的免密配置在这里生效。日志级别也值得调一下。默认Spark日志非常啰嗦启动时INFO刷屏看久了很混乱。编辑conf/log4j2.properties没有就复制模板把根日志级别从INFO改成WARNrootLogger.level WARN这样运行任务时只会输出WARN和ERROR级别的日志干净很多。排查问题时再临时改回INFO也不迟。6. 本地模式跑通再升级到Standalone集群模式配置文件的修改逻辑清楚后下面进入实战。我建议的顺序是先跑本地模式确认环境没问题再启动Standalone集群。这样每次引入新变量时都能准确定位问题出在哪一层。6.1 本地模式验证先解决“能不能跑”直接执行spark-shell --master local[*]local[*]表示使用本机所有可用的CPU核数来运行。如果配置正常你会看到Spark的logo和scala提示符。在Scala shell里跑一个简单验证val data 1 to 100 val result sc.parallelize(data).filter(_ % 2 0).count() println(s偶数个数: $result)如果输出偶数个数: 50说明Spark核心环境已经正常工作。如果你想验证PySpark先确认之前设置的PYSPARK_PYTHON变量生效了然后执行pyspark --master local[*]在Python里跑rdd sc.parallelize(range(1, 101)) result rdd.filter(lambda x: x % 2 0).count() print(f偶数个数: {result})输出同样是50。到这里本地模式就没问题了代码能跑说明依赖完整、配置正确。6.2 Standalone集群启动流程本地模式OK后我们把模式升级到Standalone。先启动Masterstart-master.sh启动后访问http://localhost:8080你应该能看到Master的Web UI里面会显示Master的地址默认为spark://spark-master:7077。然后启动Workerstart-worker.sh spark://spark-master:7077也可以一次性启动所有配置在workers文件里的节点start-workers.sh打开Master的Web UI如果Worker状态显示ALIVE说明集群节点注册成功。再跑一个官方示例验证集群模式spark-submit \ --class org.apache.spark.examples.SparkPi \ --master spark://spark-master:7077 \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.5.1.jar \ 10这个程序会计算圆周率最后一个参数10表示迭代次数。正常运行时你会看到Application出现在Web UI的应用列表里日志最后会输出一个非常接近3.14的数值。如果这一步能通过你的Spark开发环境就已经完全就绪了无论是写Scala作业还是PySpark作业都可以在这个基础上进行开发调试。7. 踩坑实录最典型的三类问题排查链路这一节我想认真写一下我实际踩过、也帮别人排查过的三类高频问题。直接给答案没有意义重要的是把排查思路走一遍下次遇到类似问题你知道往哪里看。7.1 JAVA_HOME或主机名解析导致的启动失败错误特征执行start-master.sh后Master进程秒退没有明显报错或者spark-shell启动时卡住不动一段时间后报连接拒绝。排查思路# 先看前台日志 start-master.sh 21 | head -50 # 再看Spark日志目录 tail -100 $SPARK_LOG_DIR/spark-$(whoami)-org.apache.spark.deploy.master.Master-1-*.out通常会发现日志里有JAVA_HOME is not set或Could not resolve hostname之类的信息。前者说明spark-env.sh里的JAVA_HOME没配好后者说明SPARK_MASTER_HOST指定的主机名在/etc/hosts里没有解析条目。修复步骤确认java -version能正常输出。在spark-env.sh里显式设置export JAVA_HOME/opt/jdk如果用的是软链接用readlink -f $(which java)追踪到真实路径。确保/etc/hosts里有本机IP和主机名的映射然后用hostname -f确认完整主机名是否被解析。7.2 内存分配不均导致的Worker启动失败错误特征start-workers.sh执行后Master UI里看不到Worker或者Worker显示DEAD。排查思路查看Worker日志tail -100 $SPARK_LOG_DIR/spark-$(whoami)-org.apache.spark.deploy.worker.Worker-1-*.out如果日志里有Not enough memory to launch worker说明SPARK_WORKER_MEMORY设得比物理可用内存还大或者系统本身可用内存不足。用free -h查看剩余内存。修复步骤把spark-env.sh里的SPARK_WORKER_MEMORY调小。经验值是留出物理内存的20%~30%给OS和JVM本身比如16GB物理内存的机器设10g比较稳。另外还有一个隐藏坑如果Master和Worker在同一个节点上跑Master本身会占用一定内存Worker内存设置过大会导致二者内存竞争。这种情况优先降低Worker内存。7.3 版本错配导致ClassNotFoundException或依赖冲突错误特征spark-submit运行作业时报一堆NoClassDefFoundError或java.lang.NoSuchMethodError且这些类看起来和Hadoop或Netty相关。排查思路这类问题几乎都是版本错配引起的。最常见的情况是你下载的Spark包是Hadoop 3.3的预编译版但环境变量里手动指定的Hadoop客户端是其他版本或者你本地有多个Spark版本SPARK_HOME指向了旧版。修复步骤确认SPARK_HOME指向的确实是你要用的Spark版本echo $SPARK_HOME。检查spark-submit --version输出的Hadoop版本标签看它和预编译包的后缀是否一致。如果你为了读写HDFS额外加了Hadoop相关依赖确保版本和Spark预编译版匹配。最简单的做法是先不加这些依赖跑通内置示例后再逐步引入外部依赖。提示遇到依赖问题时先查看$SPARK_HOME/jars下的jar包版本再排查自己引入的外部依赖不要盲目升级某个jar到最新版。Spark对Hadoop和Netty的版本是敏感的强行升级反而容易引发新的兼容问题。8. 环境跑通之后我建议你马上做的几件事搭建到这一步你已经有一个可用的Spark开发环境了。但在正式投入开发之前还有几个配置值得花点时间补齐它们会让你的日常开发顺畅很多。8.1 配置Spark History Server默认情况下Spark应用运行结束后再打开Web UI是看不到历史记录的。开发调试时想回头看某个作业的日志和统计信息就会发现界面已经404了。配置History Server可以解决这个问题。在spark-defaults.conf里加一行并建好日志目录spark.eventLog.enabled true spark.eventLog.dir file:///data/spark/logs/eventmkdir -p /data/spark/logs/event然后启动History Serverstart-history-server.sh以后跑的每个作业都会持久化Event Log在http://localhost:18080上就能查历史作业。这个配置对开发调试特别有用跑完的作业可以随时复盘不用重新执行。8.2 确认PySpark的Python版本与依赖隔离PySpark开发时一个很常见的问题是本地pip install pyspark装了一个版本SPARK_HOME/python里自带的又是另一个版本运行时不知道被谁覆盖。我的做法是不额外pip安装pyspark直接用Spark发行包内置的python目录这样能保证版本和JVM端的Spark完全一致。做法很简单在.bashrc里把$SPARK_HOME/python加到PYTHONPATHexport PYTHONPATH$SPARK_HOME/python:$SPARK_HOME/python/lib/py4j-*-src.zip:$PYTHONPATHsource ~/.bashrc然后运行pyspark它就会使用Spark自带的Python包避免双版本混乱。8.3 同一套配置用在多台机器上的注意点如果后续打算扩展到多节点集群有两点经验值得分享spark-env.sh可以在多台机器上保持相同内容因为JAVA_HOME、PYSPARK_PYTHON这些路径只要保持一致就行。workers文件只在Master节点上生效Worker节点不需要维护这个文件。所以实际部署时通常先把Master节点的整个/opt/spark目录同步到各Worker节点再在Master上配置workers文件指定Worker主机名免密做好就能一键拉起整个集群。我自己在实际部署时习惯用rsync把Spark安装目录和配置文件同步到所有节点rsync -avz /opt/spark/ spark-worker-1:/opt/spark/同步完再检查各节点的JAVA_HOME和Python版本是否一致。这一步如果搞不定后面任务量一大各种奇奇怪怪的报错就会冒出来。最后再说一个个人体会Spark环境搭建这件事本质上是版本管理、资源规划和配置理解三项能力的组合。版本不出错环境就成功了一大半资源规划不贪心Worker和Executor就不会反复OOM配置不盲抄遇到问题才不会被“网上说这样写”带偏。这套环境我目前在多个项目里用了两年多从单机开发到三节点集群都比较稳定。你按照这个流程搭完至少第一阶段的学习和开发是完全够用的。