Spark 3.2.0 与 Hadoop 3.2 环境搭建:从解压到集群验证的完整指南 简介spark-3.2.0-bin-hadoop3.2.tgz 是面向大数据开发与数据分析人员的 Spark 3.2.0 预编译发行包针对 Hadoop 3.2 环境构建解压后即可直接部署运行适合需要搭建分布式计算环境、开展批流处理与机器学习任务的工程师及数据科学从业者。压缩包共 1476 个文件约 287.02MB以 462 个 Python 文件、238 个 jar 包、203 个 Scala 与 135 个 Java 源文件为主另含 shell 脚本、配置文件、Parquet/ORC 示例数据及说明文档覆盖 Spark Core、Spark SQL、Streaming、MLlib、GraphX 等核心组件。该版本在性能、SQL 优化、PySpark 一致性、Kubernetes 原生支持及内存管理上均有改进并新增时间旅行等特性。目前已有 1124 人学习下载可帮助读者快速获得可运行的完整发行环境省去自行编译与依赖配置的环节。1. Spark 3.2.0 搭配 Hadoop 3.2 的安装包为什么解压完反而跑不起来很多人第一次拿到spark-3.2.0-bin-hadoop3.2.tgz这个包第一反应是“解压、配个 PATH、敲 spark-shell完事”。我当年也这么想结果spark-shell起来之后控制台刷了一屏ClassNotFoundException再敲个sc.textFile直接卡死。后来才发现这个包名里的bin-hadoop3.2不是装饰它决定了你本地要不要额外放 Hadoop 的依赖、HADOOP_CONF_DIR要不要指、SPARK_DIST_CLASSPATH要不要手动拼。Spark 3.2.0 是 2021 年 10 月发布的稳定版配 Hadoop 3.2 的预编译包在离线环境里用得特别多尤其是那些不能随便连外网、只能靠 tgz 包分发的场景。这篇笔记就是把我自己装这套环境的过程拆开从 JDK 选型、解压路径、环境变量、配置项到跑通第一个 job中间踩过的坑也一并写出来。如果你正卡在“包解压了但 spark-shell 报错”这一步下面这些步骤可以照着走一遍。2. 装之前先把环境列清楚JDK、Hadoop 依赖和目录规划2.1 JDK 版本别乱选Spark 3.2.0 只认 8 和 11Spark 3.2.0 官方编译时用的 Scala 是 2.12运行在 JVM 上对 JDK 版本有明确要求JDK 8 和 JDK 11 都能跑JDK 17 不行会报IllegalAccessError之类的模块访问错误。我一般会选 JDK 8因为 Hadoop 3.2 那一代的生态对 JDK 8 兼容性最好很多老组件还没完全适配 11。如果你机器上已经有 JDK 11 且不想换也能用但要注意spark-submit时加--conf spark.driver.extraJavaOptions-Dio.netty.tryReflectionSetAccessibletrue否则 Netty 那块会出问题。检查当前 JDK 版本java -version # 期望输出类似 # openjdk version 1.8.0_362 # OpenJDK Runtime Environment (build 1.8.0_362-b08)如果输出是 17 或更高先装一个 JDK 8 并切过去。常见做法是用update-alternatives管理多版本sudo update-alternatives --config java # 交互式选择 JDK 8 对应的编号参数说明update-alternatives是 Debian/Ubuntu 系的多版本管理工具RHEL 系用alternatives。切完之后再java -version确认一遍别嫌麻烦这一步错了后面全白搭。2.2 目录规划别把 Spark 解压到 /tmp我见过有人把 tgz 直接解压到/tmp下重启机器后目录被清空然后跑来问“Spark 怎么没了”。Spark 的安装目录要放在一个稳定、有足够空间、路径里不带空格和中文的地方。我一般用/opt/bigdata/作为统一的大数据组件根目录sudo mkdir -p /opt/bigdata sudo chown -R $(whoami):$(whoami) /opt/bigdata然后把spark-3.2.0-bin-hadoop3.2.tgz传上去解压cd /opt/bigdata tar -zxvf spark-3.2.0-bin-hadoop3.2.tgz # 解压后得到 spark-3.2.0-bin-hadoop3.2 目录 ls spark-3.2.0-bin-hadoop3.2 # 应该能看到 bin/ conf/ jars/ sbin/ 等目录参数说明-z解 gzip-x解压-v显示过程-f指定文件名。解压出来的目录名很长我习惯做一个软链接后面配环境变量和写脚本都短一些ln -s /opt/bigdata/spark-3.2.0-bin-hadoop3.2 /opt/bigdata/spark这样以后升级版本只需要改软链接指向环境变量不用动。2.3 Hadoop 依赖bin-hadoop3.2 包里到底带了什么spark-3.2.0-bin-hadoop3.2.tgz这个包已经内置了 Hadoop 3.2 的客户端 jar在jars/目录下你能看到hadoop-client-api-3.2.x.jar和hadoop-client-runtime-3.2.x.jar。这意味着你不需要单独装一个 Hadoop 就能跑本地模式local mode和 Standalone 模式。但如果你要连已有的 HDFS 集群就需要把core-site.xml和hdfs-site.xml放到 Spark 的conf/目录下或者设置HADOOP_CONF_DIR指向 Hadoop 的配置目录。我一般会检查一下 jars 目录里 Hadoop 相关包的版本ls /opt/bigdata/spark/jars/ | grep hadoop-client # 期望看到 # hadoop-client-api-3.2.0.jar # hadoop-client-runtime-3.2.0.jar如果这两个包不在说明你拿到的不是bin-hadoop3.2版本可能是without-hadoop版本那就得自己补 Hadoop 依赖麻烦得多。所以下载的时候一定看清包名。3. 配置环境变量和核心参数三个文件决定能不能跑起来3.1 环境变量PATH、SPARK_HOME 和 JAVA_HOME 一个都不能少解压完只是把文件放好了要让spark-shell、spark-submit这些命令在任何目录下都能敲得配环境变量。我一般写在~/.bashrc或者/etc/profile.d/spark.sh里后者对所有用户生效# /etc/profile.d/spark.sh export SPARK_HOME/opt/bigdata/spark export PATH$SPARK_HOME/bin:$SPARK_HOME/sbin:$PATH export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 export HADOOP_CONF_DIR/opt/bigdata/hadoop/etc/hadoop参数说明SPARK_HOME指向软链接目录PATH里把bin和sbin都加进去bin下有spark-shell、spark-submitsbin下有start-master.sh、start-worker.sh这些集群启停脚本。JAVA_HOME必须指向 JDK 8 的根目录不是bin目录。HADOOP_CONF_DIR如果你不连 HDFS 集群可以先不设但设了之后 Spark 会自动读取里面的core-site.xml。写完记得 source 一下source /etc/profile.d/spark.sh which spark-shell # 期望输出 /opt/bigdata/spark/bin/spark-shell如果which找不到检查 PATH 拼写和文件权限。常见坑是/etc/profile.d/下的脚本没有可读权限导致登录时没被加载。3.2 spark-env.shMaster 和 Worker 的资源参数在这里定Spark 的conf/目录下有一堆.template文件需要复制成正式文件再改。最关键的是spark-env.shcd /opt/bigdata/spark/conf cp spark-env.sh.template spark-env.sh然后在spark-env.sh里加export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64 export SPARK_MASTER_HOST192.168.1.100 export SPARK_MASTER_PORT7077 export SPARK_WORKER_CORES4 export SPARK_WORKER_MEMORY8g export SPARK_WORKER_INSTANCES1参数说明SPARK_MASTER_HOST填你 Master 节点的 IP 或主机名别写localhost否则 Worker 节点连不上。SPARK_WORKER_CORES是每个 Worker 能给应用用的 CPU 核数SPARK_WORKER_MEMORY是给应用用的内存注意这两个值不是越大越好要留够系统本身的开销。SPARK_WORKER_INSTANCES是一台机器上起几个 Worker 进程一般设 1除非你机器核特别多、想隔离资源。3.3 workers 文件Standalone 集群里 Worker 节点列表如果你要搭 Standalone 集群还需要配workers文件cp workers.template workers里面写上所有 Worker 节点的主机名或 IP一行一个192.168.1.101 192.168.1.102 192.168.1.103这个文件只在 Master 节点上配start-all.sh会 SSH 到这些节点去启动 Worker。前提是 Master 到这些节点已经配了免密登录否则启动脚本会卡在输密码。免密登录的配置不在本篇范围但如果你start-all.sh之后发现只有 Master 起来了、Worker 没起来八成就是 SSH 的问题。3.4 log4j.properties日志级别不改控制台全是噪音Spark 默认的日志级别是 INFO跑一个 job 控制台能刷几千行。我一般把conf/log4j.properties.template复制一份然后把根级别改成 WARNcp log4j.properties.template log4j.properties找到log4j.rootCategoryINFO, console这一行改成log4j.rootCategoryWARN, console这样只有警告和错误才会打到控制台调试的时候再临时改回 INFO。注意 Spark 3.2.0 用的是 log4j 1.x 的配置格式别拿 log4j2 的写法套会不生效。4. 启动与验证从 local 模式到 Standalone 集群4.1 先跑 local 模式确认包本身没问题在折腾集群之前先用 local 模式验证一下包解压和环境变量没问题spark-shell --master local[2]参数说明--master local[2]表示用本地模式跑开 2 个线程模拟并行。如果这一步能进到 Scala 的交互界面并且看到 Spark 的 logo 和版本号3.2.0说明 JDK、环境变量、包本身都没问题。如果报JAVA_HOME is not set回去检查spark-env.sh里的JAVA_HOME如果报ClassNotFoundException检查jars/目录是不是完整。进去之后敲几行验证val data Seq(1, 2, 3, 4, 5) val rdd sc.parallelize(data) rdd.map(_ * 2).collect() // 期望输出 Array(2, 4, 6, 8, 10)能出结果就说明 local 模式通了。退出用:quit。4.2 Standalone 集群启动start-all.sh 之后看什么local 模式没问题了再启 Standalone 集群。在 Master 节点上$SPARK_HOME/sbin/start-all.sh这个脚本会先启动 Master然后根据workers文件 SSH 到各节点启动 Worker。启动完之后用jps检查进程jps # Master 节点期望看到 # Master # Jps # Worker 节点期望看到 # Worker # Jps如果 Master 节点只有Jps没有Master去看$SPARK_HOME/logs/下的日志通常是端口被占用或者SPARK_MASTER_HOST配错了。如果 Worker 节点没有Worker进程检查 SSH 免密和workers文件里的主机名解析。4.3 Web UI 验证8080 端口能看到集群状态Spark Standalone 的 Master Web UI 默认在 8080 端口。浏览器打开http://master-ip:8080应该能看到集群概览Alive Workers 数量、Cores 总数、Memory 总量。如果页面打不开先确认防火墙有没有放行 8080再确认 Master 进程是不是真的在跑。在这个页面上你还能看到当前运行的应用列表、已完成的应用历史。提交一个 job 之后刷新页面能看到应用状态从 RUNNING 变成 FINISHED。4.4 提交第一个 jobspark-submit 到 Standalone 集群用spark-submit提交一个简单的 Pi 估算 jobspark-submit \ --master spark://192.168.1.100:7077 \ --class org.apache.spark.examples.SparkPi \ --executor-memory 2g \ --total-executor-cores 4 \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar \ 100参数说明--master指向 Master 的地址和端口--class指定主类--executor-memory是每个 Executor 的内存--total-executor-cores是所有 Executor 加起来能用的核数。最后的100是传给 SparkPi 的参数表示采样次数。跑完之后控制台会输出Pi is roughly 3.14...同时 Web UI 上能看到这个应用从 RUNNING 到 FINISHED 的完整过程。5. 避坑与排查装 Spark 3.2.0 时最容易翻车的五个地方5.1 现象spark-shell 启动报ClassNotFoundException: org.apache.hadoop.conf.Configuration原因你拿到的包不是bin-hadoop3.2版本而是without-hadoop版本jars/目录里没有 Hadoop 客户端 jar。或者你手动删过jars/下的某些包。解决重新下载spark-3.2.0-bin-hadoop3.2.tgz核对jars/下有没有hadoop-client-api-3.2.0.jar和hadoop-client-runtime-3.2.0.jar。如果确实需要用without-hadoop版本就得自己把 Hadoop 的 jar 包拷进jars/但版本要匹配否则会出现各种NoSuchMethodError。5.2 现象Worker 节点启动后 Master UI 上显示 Dead Workers原因Worker 连不上 Master常见的是SPARK_MASTER_HOST配成了localhost或者127.0.0.1导致 Worker 拿到的 Master 地址是回环地址连不到真正的 Master。解决把spark-env.sh里的SPARK_MASTER_HOST改成 Master 节点的真实 IP 或可解析的主机名然后重启集群。改完记得所有节点上的spark-env.sh都要一致别只改 Master。5.3 现象spark-submit 提交 job 后一直卡在ACCEPTED状态原因集群资源不够。SPARK_WORKER_CORES和SPARK_WORKER_MEMORY设得太小或者已经被其他应用占满新 job 排不上。解决看 Master Web UI 上的 Cores 和 Memory 使用情况把没用的应用 kill 掉或者调大 Worker 的资源参数。我一般会留 20% 的资源余量给系统本身别把 Worker 的 cores 和 memory 设成机器的全部。5.4 现象JDK 11 下跑 job 报io.netty.tryReflectionSetAccessible相关警告或错误原因JDK 11 的模块系统对反射访问做了限制Netty 在初始化时会尝试设置一些可访问性被 JVM 拒绝。解决在spark-submit时加--conf spark.driver.extraJavaOptions-Dio.netty.tryReflectionSetAccessibletrueExecutor 那边也加对应的spark.executor.extraJavaOptions。或者干脆换回 JDK 8省事。5.5 现象日志文件疯狂增长磁盘被占满原因Spark 默认的日志级别是 INFO而且log4j.properties里配置的 appender 可能没有做滚动切割长时间跑 job 日志会越写越大。解决把log4j.rootCategory改成WARN并且检查log4j.appender.console和文件 appender 的配置。如果用了文件 appender加上log4j.appender.file.MaxFileSize和MaxBackupIndex做滚动。我一般还会在spark-env.sh里加export SPARK_LOG_DIR/opt/bigdata/spark/logs把日志统一到一个有监控的目录下。6. 装完怎么验证跑通三个层次的检查和一个习惯装完 Spark 之后我一般会按三个层次来验证而不是跑一个 hello world 就完事。第一个层次是进程和端口。jps确认 Master 和 Worker 进程都在ss -tlnp | grep 7077确认 Master 的 RPC 端口在监听ss -tlnp | grep 8080确认 Web UI 端口在监听。这三个都对了说明集群骨架是活的。第二个层次是local 模式和集群模式各跑一个 job。local 模式用spark-shell --master local[2]跑一个rdd.map(_ * 2).collect()集群模式用spark-submit提交 SparkPi。两个都出结果说明计算链路是通的。第三个层次是看日志和 Web UI 的对应关系。提交 job 之后去 Master Web UI 上看应用状态变化同时去 Worker 节点的logs/目录下看该应用的 Executor 日志。如果 UI 上显示 FINISHED 但日志里有 ERROR说明 job 虽然结束了但中间有异常被吞掉了这种隐蔽问题最坑。我自己的习惯是每次装完一套 Spark都会把这三个层次的检查命令写成一个verify.sh脚本放在$SPARK_HOME/bin/下。下次换机器或者升级版本直接跑一遍脚本几分钟就能确认环境是否可用。这个习惯帮我省了很多“以为装好了结果上线才发现问题”的时间。#!/bin/bash # verify.sh - Spark 3.2.0 环境快速验证脚本 set -e echo 1. 检查进程 jps | grep -E Master|Worker || echo Master/Worker 进程未找到 echo 2. 检查端口 ss -tlnp | grep -E 7077|8080 || echo 端口未监听 echo 3. local 模式验证 $SPARK_HOME/bin/spark-shell --master local[2] EOF val rdd sc.parallelize(1 to 10) println(local mode sum: rdd.sum()) :quit EOF echo 4. 集群模式验证 $SPARK_HOME/bin/spark-submit \ --master spark://$(hostname -i):7077 \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar \ 10 21 | grep Pi is roughly echo 验证完成 这个脚本里set -e让脚本遇到错误就停避免错误被忽略。hostname -i取本机 IP如果你 Master 不在本机改成实际的 Master IP。最后那个grep Pi is roughly是为了从一堆日志里捞出关键结果脚本里做自动化验证时很有用。从那以后我每次装 Spark 都强制走一遍这个脚本不管多熟的机器都不跳过。希望这套流程能帮你把spark-3.2.0-bin-hadoop3.2.tgz顺利跑起来。本文还有配套的精品资源点击获取