☰
spark-3.2.0-bin-hadoop3.2 部署调参与排错实战
2026/11/3 2:56:55 网站建设 项目流程

简介:spark-3.2.0-bin-hadoop3.2.tgz 是面向大数据开发与数据分析学习者的 Spark 3.2.0 预编译发行包,已针对 Hadoop 3.2 做兼容构建,适合搭建本地实验环境、验证 Spark SQL 与 DataFrame 作业,也可用于课程教学和项目原型开发。压缩包共 1476 个文件,约 287.02MB,内含 238 个 jar、462 个 py、203 个 scala 与 135 个 java 源文件,并附带 sh、cmd 启动脚本、conf 配置模板及 parquet、orc、avro 等示例数据,基本覆盖 Spark Core、SQL、Streaming、MLlib 与 GraphX 的运行组件和示例资源。已有 1126 人学习,说明其在入门部署与版本对照场景中具有一定参考价值。解压后可直接使用 spark-submit、spark-shell、pyspark 等入口,配合内置示例与测试数据快速验证环境,并对照源码与配置文件理解作业提交、内存管理和 SQL 优化等机制,为后续集群部署和性能调优提供可复用的基础包。

1. 拿到 spark-3.2.0-bin-hadoop3.2.tgz 先别急着解压:这个包到底装了什么

文件名里其实已经交代了三件事:Spark 3.2.0、bin、hadoop3.2。bin 说明它是预编译的二进制分发包,解压就能用,不需要自己装 Scala、也不需要连 Maven 仓库拉依赖;hadoop3.2 说明包内自带的 Hadoop 客户端库是按 Hadoop 3.2.x 这条版本线编译的,提交到 YARN 时用的就是这批 jar。解开之后会看到 bin、sbin、jars、conf、examples 几个目录,spark-submit、spark-shell、spark-sql 在 bin 里,起停集群的脚本在 sbin 里。

它解决的是“在一台或几台机器上把 Spark 跑起来、把第一个作业提交成功”这件事,而不是“我要改造 Spark 内核”。适合两类人:想在本地把 DataFrame 和 SQL 作业跑通的数据开发,以及准备往 YARN 集群提交第一个作业的工程师。后面按“解压前确认环境 → 三种运行模式怎么配 → 参数怎么定 → 坑在哪”走一遍。

2. 解压前后必须确认的三件事:JDK 版本、Hadoop 客户端线、目录结构

这个包最大的优点是“不用编译”,最大的风险也在这里——它把一堆版本约定提前固定下来了,你只能顺着它走。装之前先花十分钟确认 JDK、确认集群侧的 Hadoop 版本、确认解压后的 jar 是不是你以为的那一批,比装完再回头查日志省事得多。下面三节分别对应这三件事,每一件都有一个可以直接粘贴的验证命令。

2.1 JDK 版本不对,后面全是玄学

Spark 3.2.0 支持 Java 8 和 Java 11,Java 17 不在支持范围内,强行跑会在启动阶段就抛出类文件版本不兼容的错误。这个错误信息看起来很像“依赖冲突”,实际跟依赖一点关系没有,属于那种查半天最后发现是环境问题的经典翻车场景。所以第一条命令永远是看 JDK:

# 只看版本行,避免一堆无关输出干扰判断 java -version 2>&1 | head -n 1 # 期望:openjdk version "1.8.0_xxx" 或 openjdk version "11.0.x" # 如果出现 17、21,先换 JDK,不要在 Spark 参数上找原因 # 多版本共存时确认 JAVA_HOME 指向哪个 echo $JAVA_HOME readlink -f $(which java)

参数说明:2>&1是因为 java 的版本信息走标准错误,不重定向会看不到;readlink -f用来确认which java命中的是软链还是真实路径,很多机器上/usr/bin/java指向的是一个 alternatives 软链,JAVA_HOME和它指向的未必是同一个 JDK,这是环境变量类问题里最常见的一种错位。

选 JDK 8 还是 11,我的建议是跟着集群走:集群上其他组件跑在 8 上就统一用 8,别为了“新一点”单独给 Spark 换 11,混搭之后出问题时排查成本远高于收益。所有节点的 JDK 大版本必须一致,standalone 模式下 master 和 worker 用不同 JDK,表现是 worker 注册成功但一跑任务就退出。

2.2 解压、环境变量与第一次 spark-submit 验证

确认完 JDK 就可以解压了。建议解压到一个统一的部署目录,并且把冗长的目录名改短——后面写配置、写脚本、敲命令都会感谢这个决定。

# 1) 解压到统一部署目录 tar -zxvf spark-3.2.0-bin-hadoop3.2.tgz -C /opt/module # 2) 目录名太长,改成 spark,方便后续所有脚本引用 mv /opt/module/spark-3.2.0-bin-hadoop3.2 /opt/module/spark # 3) 核对内置 Hadoop 客户端版本,确认是 3.2.x 这条线 ls /opt/module/spark/jars | grep -E '^hadoop-(client|hdfs|yarn)' | head

参数说明:-C指定解压目标目录,不加就会解到当前路径,容易在多个目录里留下副本,后面排查“改了配置为什么不生效”时会非常痛苦;mv改名不是洁癖,是因为spark-3.2.0-bin-hadoop3.2这个串会出现在几乎所有脚本和配置文件里,短名字能显著降低拼写错误率;最后那条ls | grep是核对内置客户端,正常应该看到hadoop-client-api-3.2.x.jar、hadoop-client-runtime-3.2.x.jar这类文件。

接着配环境变量。多节点部署时三个变量要在所有机器上保持一致,最省事的做法是写一个独立的 profile 片段:

# /etc/profile.d/spark.sh,所有节点内容一致 export SPARK_HOME=/opt/module/spark export PATH=$SPARK_HOME/bin:$SPARK_HOME/sbin:$PATH # 集群已有 Hadoop 时把配置目录指过去,on YARN 提交靠它找集群 export HADOOP_CONF_DIR=/opt/module/hadoop/etc/hadoop # 生效并验证 source /etc/profile spark-submit --version

spark-submit --version的输出里会同时打印 Spark 版本和编译用的 Scala 版本,正常情况下应该是 3.2.0 配 Scala 2.12.x。这一步能过,说明 JDK、解压路径、环境变量三件事都对了。如果这一步就报JAVA_HOME is not set,说明你在spark-env.sh里没写JAVA_HOME,而脚本启动时不读 shell 的登录环境,这是 standalone 模式下的高频问题,后面还会提到。

2.3 目录和 jars 怎么核对:一张对照表

解压完别急着提交作业,先花两分钟认清目录分工,能省掉后面很多“脚本在哪”“配置放哪”的来回找。

目录里面是什么什么时候会用到
bin/spark-submit、spark-shell、spark-sql、spark-sql客户端提交作业、交互式调试,日常用得最多
sbin/start-master.sh、start-workers.sh、stop-all.sh只在 standalone 模式下起停集群
conf/spark-defaults.conf、spark-env.sh、workers、日志配置模板所有配置入口,模板文件要复制后改名才生效
jars/Spark 自身 jar 加 Hadoop 客户端 jar排查依赖冲突、确认客户端版本
examples/jars/打包好的示例程序装完做冒烟测试,验证环境是否通
python/、R/PySpark 与 R 的绑定库用 Python 或 R 接口时才关心

有两件事值得提前说明。第一,conf下的文件基本都带.template后缀,直接改模板不会生效,必须先复制成同名正式文件,这是新手最容易忽略的一步。第二,这个包里的 Hadoop 只是客户端库,不含 HDFS 和 YARN 的守护进程,想跑 on YARN 必须有一个独立部署好的 Hadoop 集群,别指望解压完就自带集群。

3. local、standalone、on YARN 三种跑法的配置差别

三种模式的差别没有想象中大,核心是两件事:谁来做资源调度,以及 executor 跑在哪个进程里。local 模式把 driver 和 executor 塞在同一个 JVM,适合开发和验证;standalone 用 Spark 自带的 master/worker 管理资源,适合没有 YARN 的小集群;on YARN 把资源交给集群,适合已经有 Hadoop 的生产环境。下面按这三种分别给出可复制的起法和对应的必改项。

3.1 local 模式:五分钟跑通第一个作业

local 模式不需要任何配置,解压之后直接能跑。它的价值在于把“环境问题”和“作业问题”隔离开:如果 local 都跑不通,先别怀疑集群,八成还是 JDK 或环境变量的事。

# 用自带示例做冒烟测试,local[4] 表示用 4 个线程模拟 4 个核 $SPARK_HOME/bin/spark-submit \ --master local[4] \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar \ 100

参数说明:--master local[4]里的数字是并发线程数,写local[*]表示用满所有核,但本地开发不建议用满,机器还要留给 IDE 和浏览器;--class指定主类,示例包里有很多类,写错会报找不到主类;最后那个100是传给示例程序的参数,表示切分的任务数。跑通后日志里会打印一个接近 3.14 的近似值,看到它就说明 Spark 本身没问题了。

local 模式下有个容易误判的点:spark.default.parallelism的取值是本地核数,而不是集群总核数,所以你在本地调出来的并行度参数,搬到集群上未必合适。我的习惯是 local 只验证逻辑正确性,涉及性能的参数一律在目标集群上重新试。

3.2 standalone:master/worker 的启停与两个必改项

standalone 模式需要先配spark-env.sh,其中最常被漏掉的是JAVA_HOME——启动脚本不走登录 shell,环境变量里配了也不算数。另一个必改项是 worker 的资源声明,不写会按机器全部内存和全部核数上报,容易把机器占满。

# 1) 从模板复制出正式配置文件 cd $SPARK_HOME/conf cp spark-env.sh.template spark-env.sh # 2) 写入 master 地址与 worker 资源上限 cat >> spark-env.sh <<'EOF' export JAVA_HOME=/opt/module/jdk1.8.0 export SPARK_MASTER_HOST=node01 export SPARK_MASTER_PORT=7077 export SPARK_WORKER_CORES=8 export SPARK_WORKER_MEMORY=16g EOF # 3) 列出 worker 节点,每行一个 echo "node01" > workers echo "node02" >> workers # 4) 启动与停止 $SPARK_HOME/sbin/start-master.sh $SPARK_HOME/sbin/start-workers.sh $SPARK_HOME/sbin/stop-all.sh

参数说明:SPARK_MASTER_HOST写主机名而不是 IP,机器迁移时不容易出错;SPARK_MASTER_PORT默认 7077,端口冲突时才改,改了之后提交作业的 master URL 要同步改;SPARK_WORKER_CORES和SPARK_WORKER_MEMORY是这台机器愿意交给 Spark 的上限,建议给系统留出至少 2 核和 4G,否则 worker 一亮相就把机器吃满;workers文件只有start-workers.sh会读,start-worker.sh是单机起法。

启动后 master 会打印一个spark://node01:7077的地址,提交作业时用这个:

# standalone 集群模式提交,driver 在主节点,executor 在 worker 上 $SPARK_HOME/bin/spark-submit \ --master spark://node01:7077 \ --deploy-mode client \ --executor-memory 4g \ --executor-cores 2 \ --total-executor-cores 8 \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar \ 100

这里的--total-executor-cores是 standalone 模式独有的,它等同于“最多用多少个核”,YARN 上没有这个参数。--executor-cores与 worker 上报的核数不整除时,会有部分核数闲置,看起来像“资源明明够却排不上队”,这是资源碎片,不是 bug。

3.3 on YARN:两个环境变量和三个提交参数

on YARN 的配置反而比 standalone 少,因为资源调度交给集群了。关键是让 Spark 找得到集群:HADOOP_CONF_DIR或YARN_CONF_DIR指向 Hadoop 配置目录,里面要有core-site.xml和yarn-site.xml。这两个变量任选其一,但必须有一个,且指向的是解压出来的真实目录,不是压缩包里的。

# 生产上我一般用 cluster 模式,driver 跑在集群里,客户端断开不影响作业 $SPARK_HOME/bin/spark-submit \ --master yarn \ --deploy-mode cluster \ --queue root.default \ --num-executors 10 \ --executor-cores 4 \ --executor-memory 8g \ --conf spark.yarn.archive=hdfs:///spark-libs/spark-3.2.0-jars.zip \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar \ 100

参数说明:--deploy-mode cluster让 driver 跑在集群容器里,日志要去 YARN 的应用日志里翻,不会再打在本地终端,这是很多人第一次用 cluster 模式时以为“作业没输出”的原因;--queue指向队列名,队列名写错往往不报错而是挂住;spark.yarn.archive是最值得配的一项,把jars目录打包上传到 HDFS 后复用,避免每次提交都上传几百兆依赖,提交速度能差一个量级。

还有一个边界要清楚:包内是 Hadoop 客户端 3.2.x,如果你集群侧是 3.3.x,多数情况下可以正常通信,但如果用到了新版本的特定特性或安全认证方式,可能会在提交阶段报 RPC 相关异常。这种时候不要试图改 jar 包,正确做法是换对应版本的分发包,或者干脆自己按集群版本编译。

4. 内存与并行度:spark-3.2.0 上真正需要手调的五个参数

Spark 的参数面板有几百项,但真正决定作业跑不跑得动、快不快的就那么几个。这一章把内存、并行度、序列化三组参数拆开讲,每个都给换算关系和一个可复制的提交示例,参数值不是标准答案,但换算逻辑是通用的。

4.1 executor 内存与 memoryOverhead 的换算关系

最容易出错的地方是把--executor-memory当成容器总内存。实际上 YARN 申请的内存是 executor 内存加上堆外开销,再按调度器的最小分配单元向上取整。堆外开销的默认值是 executor 内存的 10%,且不低于 384MB,堆内堆外加上额外开销共同构成容器。

spark.executor.memorymemoryOverhead(默认策略)YARN 申请大致值说明
2g384MB(下限兜底)约 2.4G小作业够用,注意下限兜底
4g约 410MB约 4.4G常规 ETL 的常见档位
8g约 820MB约 8.8G有广播变量或宽表时用
16g约 1.6G约 17.6G单 executor 不建议再往上堆
# 显式指定 overhead,避免默认值在容器被 kill 时说不清 $SPARK_HOME/bin/spark-submit \ --master yarn --deploy-mode cluster \ --executor-memory 8g \ --conf spark.executor.memoryOverhead=2g \ --conf spark.memory.fraction=0.6 \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar 100

spark.executor.memoryOverhead建议在出现“容器超内存被杀”时显式调大,而不是盲目加 executor 内存——堆内加得过多,GC 时间会吃掉收益;spark.memory.fraction控制执行与存储共享区占堆的比例,默认 0.6,缓存需求大的作业可以降到 0.5 给对象留空间。另一个经验是:executor 内存不要超过节点可用内存的一半,一台 32G 的节点跑两个 16G 的 executor,一旦有一个任务出现数据倾斜,节点直接进入 swap,整个节点上的任务全部变慢,这种连锁反应很难在监控上第一时间看出来。

4.2 并行度:default.parallelism 与 shuffle.partitions 怎么定

并行度定小了,资源闲着;定大了,任务调度开销比计算还大。有个粗略的起点:把 executor 总核数算出来,shuffle 分区数取总核数的 2 到 3 倍。Spark 3.2.0 里 AQE 默认是打开的,它会自动合并过小的 shuffle 分区,所以宁可往大了设,也别设得太小。

# 先算清总核数:10 个 executor × 4 核 = 40 核 $SPARK_HOME/bin/spark-submit \ --master yarn --deploy-mode cluster \ --num-executors 10 --executor-cores 4 --executor-memory 8g \ --conf spark.sql.shuffle.partitions=120 \ --conf spark.default.parallelism=120 \ --conf spark.sql.adaptive.enabled=true \ --conf spark.sql.adaptive.coalescePartitions.enabled=true \ --conf spark.sql.adaptive.advisoryPartitionSizeInBytes=128m \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar 100

参数说明:spark.sql.shuffle.partitions只对 SQL 和 DataFrame 的 shuffle 生效,默认 200,很多人看到 200 就跑,其实要看数据量;spark.default.parallelism作用于 RDD 算子,两者不要混为一谈;advisoryPartitionSizeInBytes是 AQE 合并分区时的目标大小,128m 是个稳妥起点,写太大会合并出超大分区,反而拖长单任务时间。判断并行度是否合适有个简单办法:看 stage 里任务耗时的分布,如果大部分任务在几秒内完成而个别任务跑几分钟,那是数据倾斜,不是并行度问题,调分区数解决不了。

4.3 序列化与 Kryo:什么时候值得开

默认的 Java 序列化兼容性好但体积大、速度慢。Kryo 通常在 shuffle 数据量大、或者有大量自定义对象在网络上传输时收益明显。开关很简单,坑在于自定义类没注册时会退化成带类名的全量写入,体积反而更大。

--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \ --conf spark.kryo.registrationRequired=false \ --conf spark.kryo.classesToRegister=com.example.Order,com.example.OrderItem

spark.kryo.registrationRequired=false表示未注册的类不报错、走兼容路径,开发阶段建议保持 false,稳定后再按日志里提示的类名逐步补进classesToRegister。我一般的顺序是:先用默认序列化把作业跑对,再用 Kryo 压一次耗时对比,收益不到 10% 就不折腾——序列化改动带来的问题往往延迟暴露,属于那种“上线三天后才在某个特定数据分片上炸”的类型。

5. 排查清单:spark-3.2.0-bin-hadoop3.2 上线前最容易翻车的五个地方

下面五条都是现象、原因、解决三段式,全是在真机上来回折腾出来的。看到类似报错时,先按顺序对一遍,多数情况能少走半天弯路。

5.1 启动就报类文件版本不支持

现象:spark-submit --version或spark-shell启动瞬间抛出Unsupported class file major version 61之类的错误,堆栈指向类加载阶段。

原因:JDK 版本高于 Spark 3.2.0 支持的范围,编译产物的字节码版本对不上。这个报错文本里不会写“你的 JDK 太新”,所以很容易被误判成 jar 冲突。

解决:把JAVA_HOME和PATH里的 java 统一降到 8 或 11,同时确认spark-env.sh里写的JAVA_HOME和 shell 里的是同一个,两个地方不一致时,交互式命令能跑、脚本启动失败,是最迷惑人的组合。

5.2 提交到 YARN 报找不到 Hadoop 类

现象:local 模式一切正常,一加--master yarn就报NoClassDefFoundError,类名是 Hadoop 相关的包。

原因:HADOOP_CONF_DIR没配或指向了不存在的目录,Spark 拿不到集群配置,退化成找不到对应客户端类的状态。

解决:echo $HADOOP_CONF_DIR确认路径存在且里面有core-site.xml、yarn-site.xml;如果集群启用了认证相关配置,对应文件也必须在同一目录下,缺一个都可能表现成类找不到。

5.3 容器被 YARN 杀掉,日志写超出内存限制

现象:作业跑一段时间后 executor 丢失,YARN 日志里写类似“超出物理内存限制被 kill”的信息,且超出的量往往不大,比如申请 10G 用了 10.5G。

原因:--executor-memory只算了堆内,堆外开销按默认 10% 走,实际使用中 Python 进程、Netty 缓冲、广播变量都会额外占内存,10% 不够。

解决:显式把spark.executor.memoryOverhead提到 1.5G 到 2G,再观察是否还超;同时检查有没有在 executor 里起 Python UDF 或加载大字典,这类操作的内存不在堆内统计里。

5.4 worker 注册成功但作业一直排队

现象:standalone 模式下 master 页面能看到 worker,提交作业后一直WAITING,没有任何 executor 分配。

原因:worker 上报的可用核数被SPARK_WORKER_CORES限死,而请求的--executor-cores大于剩余可分配核数,资源碎片导致永远凑不齐一个 executor。

解决:把--executor-cores调成能整除 worker 核数的值,或者直接减少 executor 数、增大单 executor 核数;--total-executor-cores也别设得比集群实际核数还大。

5.5 作业卡在最后几个 task 不动

现象:99% 的 task 几秒完成,剩下几个跑十几分钟,stage 一直不结束。

原因:典型数据倾斜,某个 key 的数据量远大于其他 key;也有可能是 AQE 的倾斜优化没覆盖到当前场景,比如倾斜发生在 join 的另一侧。

解决:先看 stage 里任务耗时分布确认倾斜,再考虑给热点 key 加随机前缀打散、或者调整 join 策略;不要一上来就加大spark.sql.shuffle.partitions,那只会把倾斜摊到更多任务上,总时长不会变。

6. 把 spark-3.2.0-bin-hadoop3.2 跑成可复现环境的一个技巧

环境类问题的麻烦之处在于“配好了就忘了怎么配的”。我的做法是把配置固化成两样东西:一份spark-defaults.conf加一个自检脚本。前者让所有提交共享同一套参数,后者让任何一台新机器在五分钟内能判断“能不能跑”。

# $SPARK_HOME/conf/spark-defaults.conf,只写稳定不变的项 spark.master yarn spark.submit.deployMode cluster spark.serializer org.apache.spark.serializer.KryoSerializer spark.sql.shuffle.partitions 120 spark.sql.adaptive.enabled true spark.executor.memoryOverhead 2g spark.yarn.archive hdfs:///spark-libs/spark-3.2.0-jars.zip

参数说明:写进这个文件的项会被所有spark-submit继承,临时要改的用命令行覆盖即可,命令行优先级更高;spark.master写死后,本地调试要显式加--master local[*]覆盖,别图省事把这行删了又加、加了又删,配置漂移就是这么来的。

自检脚本更简单,就是按顺序跑四件事:

#!/bin/bash set -e echo "[1/4] JDK"; java -version 2>&1 | head -n 1 echo "[2/4] 版本"; $SPARK_HOME/bin/spark-submit --version 2>&1 | grep -i version | head -n 2 echo "[3/4] 客户端"; ls $SPARK_HOME/jars | grep -c '^hadoop-client' echo "[4/4] 冒烟"; $SPARK_HOME/bin/spark-submit --master local[2] \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar 20 2>&1 | tail -n 3

set -e让脚本遇到第一个失败就停,比一路跑完再看日志清楚;第四步的tail -n 3是为了只留结果行,避免几百行日志把关键信息顶掉。这个脚本我一般放在部署目录下,新机器解压完先跑它,四步全绿再谈业务作业。

再给一组验证项,判断“环境是否真的可用”比“进程是否起来了”靠谱得多:

验证项期望结果不通过时先看哪
spark-submit --version3.2.0 与 Scala 2.12.xJAVA_HOME、解压路径
local 冒烟输出近似圆周率值示例 jar 路径、主类名
YARN 提交应用状态从 ACCEPTED 到 RUNNING队列名、HADOOP_CONF_DIR
executor 数量与--num-executors一致队列资源上限、overhead 是否超限

最后说个我自己的习惯:每次换机器或换集群,我都会把spark-defaults.conf里除 master 之外的项先注释掉,只留冒烟能跑的最小集合,等基础链路通了再逐条放开。这样出问题时能立刻定位到“是哪一条配置引入的”,比一次全配上再二分排查省太多时间。这套流程不复杂,但能把“装环境”从玄学变成一件有确定答案的事,值得花一个下午做扎实。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询