1. 项目概述:为什么一个“环境搭建”值得花两小时认真对待
在数据工程领域混了十多年,我见过太多人卡在第一步——不是写不出 WordCount,而是连 spark-shell 都启动不起来。有人花三天反复重装 JDK,有人在 HADOOP_HOME 路径里加了又删、删了又加,还有人对着 jps 输出里缺一个 NameNode 干瞪眼到凌晨两点。这不是能力问题,是环境配置这件事本身就有它固有的“反直觉性”:它不考验算法思维,却极度依赖对 Java 生态、Linux 环境变量、分布式系统抽象层级的 肌肉记忆式理解 。你按文档敲完所有命令, spark-shell 闪退;你改了 core-site.xml ,HDFS 根本起不来;你打包了 jar, spark-submit 却报 ClassNotFoundException ——这些都不是偶然错误,而是系统在用报错告诉你:“你漏掉了某个隐含前提”。
这篇文章讲的不是“如何复制粘贴命令”,而是带你把 Spark 开发环境拆解成可触摸、可验证、可回溯的六个逻辑模块:JDK 是地基,Scala 是语言载体,Spark 二进制包是执行引擎,PySpark 是 Python 接口层,Hadoop 是底层存储与资源调度底座,而 IDEA + sbt 是整个开发流的编译与调试中枢。每一个模块的安装、路径配置、版本对齐、环境变量注入,背后都有明确的技术动因。比如为什么必须用 JDK 11 而不是 JDK 17?因为 Spark 3.2.x 官方编译链锁定在 Java 11,JDK 17 的模块化机制会破坏 spark-sql 的类加载顺序;为什么 Hadoop 必须装,哪怕你只跑 local 模式?因为 Spark 的 HadoopConf 类在初始化时会硬依赖 hadoop-common 中的 Configuration 类,不装 Hadoop, spark-shell 启动时就会抛 NoClassDefFoundError ,而不是友好的提示信息。
我用 Ubuntu 20.04.3 实测过全部流程,所有路径、命令、配置项都来自真实终端记录。文中不会出现“理论上可以”“一般建议”这类模糊表述,而是直接告诉你: export SPARK_HOME=/opt/spark 这一行必须写在 ~/.bashrc 的末尾而非开头,否则 source 后 which spark-shell 仍会返回空; dfs.replication=1 是单机伪分布模式的铁律,设成 0 或 2 会导致 start-dfs.sh 启动后立即退出; build.sbt 里 spark-core 的版本号必须和你下载的 Spark 二进制包主版本严格一致,差一个小数点(如 3.2.0 vs 3.2.1 )就会触发 sbt 的 Ivy 缓存冲突,导致依赖解析失败。这些细节,是书本不会写、官方文档不会强调、但你在真实项目里每天都会撞上的墙。接下来,我们就从最底层的 JDK 开始,一层一层把这堵墙砌实。
2. 核心组件安装与版本对齐:每个依赖都是有脾气的
2.1 JDK:不是装上就行,关键是让 JVM “认得清自己”
Spark 是用 Scala 写的,而 Scala 运行在 JVM 上。这意味着 JDK 不仅是编译工具,更是 Spark 运行时的“呼吸系统”。很多人以为 sudo apt install default-jre 就万事大吉,但这个包在 Ubuntu 20.04.3 上默认安装的是 OpenJDK 11.0.13,表面看版本没错,实际却埋着两个坑:一是 default-jre 只装了 JRE(Java Runtime Environment),缺少 javac 编译器,而 IDEA 在构建 Scala 项目时会调用 javac 处理混合代码;二是它的 JAVA_HOME 环境变量指向 /usr/lib/jvm/default-java ,这个软链接在系统更新后可能被重置,导致后续所有依赖 JDK 的工具(包括 Spark、Hadoop、IDEA)集体失联。
正确的做法是手动安装并显式声明 JAVA_HOME :
# 先卸载可能冲突的 default-jre
sudo apt remove default-jre default-jdk
# 下载官方 OpenJDK 11 二进制包(推荐使用 https://adoptium.net/ 的 Temurin 构建)
# 或者用 apt 安装更可控的 openjdk-11-jdk 包
sudo apt update
sudo apt install openjdk-11-jdk
# 验证安装
java -version
# 正确输出应为:
# openjdk version "11.0.19" 2022-10-18
# OpenJDK Runtime Environment (build 11.0.19+7-post-Ubuntu-0ubuntu120.04)
# OpenJDK 64-Bit Server VM (build 11.0.19+7-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)
# 关键一步:找到真实的 JDK 安装路径
sudo update-alternatives --config java
# 输出类似:
# There are 2 choices for the alternative java (providing /usr/bin/java).
# Selection Path Priority Status
# ------------------------------------------------------------
# * 0 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1101 auto mode
# 1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1101 manual mode
# 2 /usr/lib/jvm/java-17-openjdk-amd64/bin/java 1091 manual mode
# 记下第一行 Path 列的完整路径(这里是 /usr/lib/jvm/java-11-openjdk-amd64)
# 将其写入 ~/.bashrc(注意:必须是绝对路径,不能用 ~ 符号)
echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# 最终验证
echo $JAVA_HOME
# 应输出:/usr/lib/jvm/java-11-openjdk-amd64
java -cp . HelloWorld # 如果有 HelloWorld.java,能成功编译运行即证明 JDK 完整可用
提示:
JAVA_HOME必须指向 JDK 根目录(即包含bin/、lib/、jre/的文件夹),而不是bin/子目录。很多初学者在这里栽跟头,echo $JAVA_HOME输出/usr/lib/jvm/java-11-openjdk-amd64/bin是典型错误。
2.2 Scala:不必精通语法,但必须懂它的“编译契约”
Scala 和 Spark 是共生关系。Spark 的核心 API( RDD 、 DataFrame )最初就是用 Scala 设计的,Python 和 SQL 接口都是后来封装的。因此,即使你只用 PySpark,本地开发环境里也必须装 Scala 编译器( scalac )和标准库。原因在于:IDEA 的 Scala 插件在解析 Spark 源码、提供代码补全、检查类型安全时,需要调用 scalac 进行语义分析;更重要的是, sbt (Scala 的构建工具)在解析 build.sbt 时,会根据 scalaVersion := "2.12.11" 这一行去 Maven 仓库下载对应版本的 Scala 编译器和库,如果本地没装, sbt 会自己下载,但下载路径和缓存策略不可控,极易与 IDEA 的 SDK 配置冲突。
安装 Scala 有两个等效路径,我推荐后者,因为它与 IDEA 深度集成,避免了手动管理 SCALA_HOME 的麻烦:
路径一:手动安装(适合


被折叠的 条评论
为什么被折叠?



