简介:spark-3.2.0-bin-hadoop3.2.tgz 是面向大数据开发与数据分析人员的 Spark 3.2.0 二进制发行包,针对 Hadoop 3.2 环境完成兼容构建,解压后即可在已有 Hadoop 集群上直接部署运行,适合从事离线批处理、实时流计算与机器学习建模的中高级开发者使用。压缩包共 1476 个文件,整体约 287.02MB,以 462 个 py、238 个 jar、203 个 scala、135 个 java 等源码与依赖文件为主,并包含 sh、cmd 启动脚本、parquet/orc/csv 示例数据、json 与 md 配置说明等,覆盖 Spark Core、Spark SQL、Spark Streaming、MLlib 与 GraphX 各模块。该版本在 DataFrame/Dataset 性能、Catalyst 优化器、PySpark 一致性、Kubernetes 原生支持及内存管理等方面均有改进,并新增时间旅行等能力。目前已有 1123 人学习下载,读者可借此快速搭建本地或集群实验环境,对照源码与示例数据理解各组件调用方式,为后续调优与排错提供完整参考。
1. spark-3.2.0-bin-hadoop3.2.tgz 到底是什么:一个压缩包背后的整套离线大数据栈
很多人第一次看到spark-3.2.0-bin-hadoop3.2.tgz这个文件名,会以为它只是一个普通的 Spark 安装包,解压完敲个spark-shell就完事了。真到内网机器上跑起来才发现,这个 tgz 里其实压着一整套「Spark 运行时 + Hadoop 客户端依赖 + 一堆脚本」的组合,它决定了你的 Spark 能不能连上 HDFS、能不能提交到 YARN、能不能在完全离线的环境里跑起来。这篇文章就围绕这个压缩包,把从零开始安装 Hadoop、Spark 集群搭建、伪分布式验证、任务提交到 YARN 的完整链路讲清楚,适合正在做 hadoop 课程设计、网约车大数据综合项目、农产品价格数据分析这类实战任务的同学,也适合需要在离线环境里搭一套 Spark 数据分析环境的工程师。文件名里的bin-hadoop3.2不是随便起的,它直接决定了后面所有配置的走向。
2. 解压之前先想清楚:bin-hadoop3.2 这个后缀意味着什么
2.1 三种 Spark 包的区别与选型理由
Apache Spark 官网下载页上,同一个版本会摆出好几个 tgz,新手最容易在这里翻车。常见的有spark-3.2.0-bin-hadoop3.2.tgz、spark-3.2.0-bin-hadoop2.7.tgz、spark-3.2.0-bin-without-hadoop.tgz。它们的差别不在 Spark 本身,而在打包时内置的 Hadoop 客户端库版本。
| 包名后缀 | 内置 Hadoop 客户端 | 适用场景 |
|---|---|---|
| bin-hadoop3.2 | Hadoop 3.2.x 客户端 jar | 集群是 Hadoop 3.x,最省事 |
| bin-hadoop2.7 | Hadoop 2.7.x 客户端 jar | 老集群,Hadoop 2.x |
| bin-without-hadoop | 不含 Hadoop jar | 集群 Hadoop 版本特殊,需手动指定 classpath |
选bin-hadoop3.2的核心理由是:它内置的客户端 jar 和你集群上 NameNode、ResourceManager 的版本对得上,Spark 读写 HDFS、提交 YARN 任务时不会因为 RPC 协议或序列化类不匹配而报NoSuchMethodError。我一般会先hadoop version看一眼集群版本,再决定下哪个包。如果集群是 Hadoop 3.2.x 或 3.3.x,直接选这个包,能省掉大量手动补 jar 的麻烦。
bin-without-hadoop看起来最灵活,实际上最容易踩坑。它要求你在spark-env.sh里显式设置SPARK_DIST_CLASSPATH,把 Hadoop 的share/hadoop下所有 jar 拼进去。拼错一个路径,Spark 启动时就报ClassNotFoundException: org.apache.hadoop.conf.Configuration。除非你的 Hadoop 是源码编译的特殊版本,否则没必要给自己找这个麻烦。
2.2 离线环境下的依赖自检清单
内网机器不能联网,下载 tgz 之前先在能上网的机器上把依赖理清楚。这个包本身大约 200 多 MB,解压后 400 MB 左右,但它运行起来还依赖两样东西:JDK 和 Hadoop 集群。
JDK 必须是 8 或 11。Spark 3.2.0 官方编译目标是 Java 8,用 Java 17 会在启动时遇到IllegalAccessError之类的模块访问问题。我一般统一用 JDK 8,省心。检查命令:
java -version # 期望输出包含 "1.8.0" 或 "11.0" echo $JAVA_HOME # 必须指向 JDK 根目录,不是 jre 子目录Hadoop 集群这边,至少要确认三件事:NameNode 的 RPC 地址和端口、ResourceManager 的地址和端口、core-site.xml 和 hdfs-site.xml 能否拿到。离线环境下没法从官网下配置模板,通常是从集群运维那里拷贝一份core-site.xml、hdfs-site.xml、yarn-site.xml放到 Spark 的conf目录,或者直接放进$HADOOP_CONF_DIR指向的目录。
提示:
$HADOOP_CONF_DIR这个环境变量比把配置文件拷进 Spark conf 更推荐,因为集群配置变更时只需要改一处。
2.3 解压与目录结构速览
拿到 tgz 之后,解压路径不要带空格和中文,这是血泪经验。我一般放在/opt或用户家目录下:
tar -zxvf spark-3.2.0-bin-hadoop3.2.tgz -C /opt/ cd /opt/spark-3.2.0-bin-hadoop3.2 ls # bin conf examples jars kubernetes licenses python R sbin yarn几个关键目录的作用:bin放spark-submit、spark-shell、pyspark这些入口脚本;conf放配置模板,spark-env.sh.template和spark-defaults.conf.template需要复制成去掉.template的文件才生效;jars里就是内置的 Hadoop 客户端 jar 和 Spark 自身的所有依赖;sbin放集群启停脚本start-all.sh、stop-all.sh。examples目录里有官方自带的 jar 和样例数据,验证环境时非常有用,后面会用到。
3. 从零把 Spark 跑起来:伪分布式与 Standalone 集群搭建
3.1 Hadoop 伪分布式搭建的最小步骤
Spark 要读 HDFS,得先有个能用的 HDFS。单机学习场景下,Hadoop 伪分布式是最常见的起点。假设你已经下好了 Hadoop 3.2.x 的 tar 包,解压到/opt/hadoop,接下来改三个文件。
etc/hadoop/core-site.xml:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/data/tmp</value> </property> </configuration>etc/hadoop/hdfs-site.xml:
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/opt/hadoop/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/hadoop/data/datanode</value> </property> </configuration>etc/hadoop/hadoop-env.sh里把JAVA_HOME写死:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64参数说明:fs.defaultFS的端口 9000 是 NameNode RPC 端口,Spark 后面配置fs.defaultFS时要和它一致;dfs.replication伪分布式只有一台 DataNode,必须设成 1,否则文件永远处于副本不足状态;hadoop.tmp.dir一定要显式指定,默认在/tmp下,机器重启就丢,NameNode 格式化数据全没。
格式化并启动:
hdfs namenode -format start-dfs.sh jps # 应该看到 NameNode、DataNode、SecondaryNameNodejps是排查 Hadoop 和 Spark 进程最常用的命令,养成启动后先jps看一眼的习惯。如果 DataNode 没起来,去logs目录看hadoop-*-datanode-*.log,八成是name.dir和data.dir路径权限问题或者 clusterID 不一致。
3.2 Spark Standalone 集群的配置与启动
HDFS 通了之后,配 Spark 自己的 Standalone 集群。进入 Spark 的conf目录:
cd /opt/spark-3.2.0-bin-hadoop3.2/conf cp spark-env.sh.template spark-env.sh cp slaves.template slaves编辑spark-env.sh:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export HADOOP_CONF_DIR=/opt/hadoop/etc/hadoop export SPARK_MASTER_HOST=localhost export SPARK_MASTER_PORT=7077 export SPARK_WORKER_CORES=2 export SPARK_WORKER_MEMORY=2g编辑slaves,单机就写一行:
localhost参数说明:HADOOP_CONF_DIR指向 Hadoop 配置目录,Spark 靠它找到fs.defaultFS和 YARN 的 ResourceManager 地址;SPARK_MASTER_PORT默认 7077,是 Worker 向 Master 注册的端口;SPARK_WORKER_CORES和SPARK_WORKER_MEMORY决定这个 Worker 能提供给应用的总资源,设太大超过物理机实际资源会导致 Executor 起不来。
启动集群:
cd /opt/spark-3.2.0-bin-hadoop3.2 ./sbin/start-all.sh jps # 应该看到 Master 和 Worker访问http://localhost:8080能看到 Spark Master 的 Web UI,上面显示 Alive Workers 数量为 1,就说明 Standalone 集群起来了。这一步是后面所有任务提交的基础。
3.3 用 spark-shell 和 spark-submit 验证环境
环境搭好不验证等于没搭。先用spark-shell做交互式验证:
./bin/spark-shell --master spark://localhost:7077进去之后跑一段读 HDFS 的代码:
// 在 HDFS 上建个测试目录并写入数据 val data = Seq(("apple", 3), ("banana", 5), ("apple", 2)) val rdd = sc.parallelize(data) rdd.saveAsTextFile("hdfs://localhost:9000/test/spark_check") // 读回来统计 val loaded = sc.textFile("hdfs://localhost:9000/test/spark_check") loaded.collect().foreach(println)逻辑说明:sc.parallelize把本地集合转成 RDD,saveAsTextFile写到 HDFS,这一步同时验证了 Spark 到 HDFS 的写入链路;textFile读回来验证读取链路。如果写入时报Connection refused,说明fs.defaultFS配错或 NameNode 没起;如果报权限错误,检查当前用户对 HDFS 目录是否有写权限。
再用spark-submit提交官方样例,验证集群模式:
./bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master spark://localhost:7077 \ --executor-memory 1g \ --total-executor-cores 2 \ ./examples/jars/spark-examples_2.12-3.2.0.jar 10参数说明:--master指定 Standalone Master 地址;--executor-memory是每个 Executor 的内存,注意这里设的是 Executor 堆内存,不含 overhead;--total-executor-cores是所有 Executor 加起来用的核数,不能超过 Worker 提供的总核数;最后的10是传给 SparkPi 的参数,表示分区数。输出里能看到Pi is roughly 3.14...就说明集群模式跑通了。
4. 提交到 YARN 与读写 HDFS:生产里最常用的那条路
4.1 任务提交到 YARN 的完整流程
Standalone 适合学习和独立部署,但真实集群里绝大多数是 Hadoop YARN 模式。Spark 提交到 YARN 有两种模式:client和cluster。client模式 Driver 跑在提交机上,适合调试,日志直接打在终端;cluster模式 Driver 跑在 YARN 的 ApplicationMaster 里,适合生产,提交完终端就可以断开。
提交命令:
./bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode cluster \ --driver-memory 1g \ --executor-memory 1g \ --executor-cores 1 \ --num-executors 2 \ --queue default \ ./examples/jars/spark-examples_2.12-3.2.0.jar 10参数说明:--master yarn表示走 YARN 资源调度;--deploy-mode cluster让 Driver 在集群里跑;--num-executors是 Executor 数量,这个参数在 YARN 模式下才生效,Standalone 模式下无效;--queue指定 YARN 队列,生产环境通常按业务分队列,不写会用 default 队列。提交后终端会打印 application ID,用yarn application -status <appId>查状态,yarn logs -applicationId <appId>看日志。
YARN 模式的底层流程是:客户端把 Spark jar 和用户 jar 上传到 HDFS 的 staging 目录,然后向 ResourceManager 申请一个 Container 启动 ApplicationMaster,AM 再向 RM 申请 Executor Container,RM 通知 NodeManager 拉起 Executor 进程,Executor 反向注册到 Driver。理解这条链路,出问题时就知道该看哪一环的日志。
4.2 读写 HDFS 的常见配置与踩坑点
Spark 读写 HDFS 时,有几个配置项直接决定成败。第一个是fs.defaultFS,Spark 从HADOOP_CONF_DIR下的core-site.xml读取,如果没设HADOOP_CONF_DIR,就得在spark-defaults.conf里手动加:
spark.hadoop.fs.defaultFS hdfs://localhost:9000第二个是spark.hadoop.dfs.replication,写文件时的副本数,伪分布式下必须设 1。第三个是权限相关,HDFS 默认开启权限检查,如果提交任务的用户和 HDFS 目录属主不一致,会报Permission denied。临时绕过可以在core-site.xml里设hadoop.http.staticuser.user,但生产环境应该老老实实配权限。
读 JSON 是热词里高频出现的场景,Spark 3.2 读 JSON 用spark.read.json:
from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("ReadJsonFromHDFS") \ .config("spark.hadoop.fs.defaultFS", "hdfs://localhost:9000") \ .getOrCreate() df = spark.read.json("hdfs://localhost:9000/data/orders.json") df.printSchema() df.createOrReplaceTempView("orders") spark.sql("SELECT city, count(*) AS cnt FROM orders GROUP BY city ORDER BY cnt DESC").show() spark.stop()逻辑说明:SparkSession.builder里显式指定fs.defaultFS,避免依赖环境变量;spark.read.json会自动推断 schema,但生产环境建议用schema参数显式指定,避免推断开销和类型误判;createOrReplaceTempView注册临时视图后用 SQL 做聚合,这是数据分析案例里最常用的写法。参数上,spark.sql.shuffle.partitions默认 200,小数据量下会产生大量小任务,建议按数据量调到 10 到 50 之间。
4.3 内存与线程监测:别等 OOM 了才想起来看
Spark 内存问题是最常见的翻车点。Executor 内存分三块:Execution Memory(shuffle、join、sort 用)、Storage Memory(cache、broadcast 用)、User Memory(用户数据结构用)。统一内存管理下,Execution 和 Storage 可以互相借用,但 User Memory 是硬隔离的。
监测手段有几个。Web UI 的 Executors 页面能看到每个 Executor 的 Storage Memory 使用量和 GC 时间;spark.executor.memoryOverhead是堆外内存,默认是max(executorMemory * 0.1, 384MB),如果报Container killed by YARN for exceeding memory limits,八成是 overhead 不够,调大这个值。线程方面,spark.executor.cores决定每个 Executor 能并行跑几个 task,但一个 Executor 上跑太多 task 会导致 shuffle 时磁盘 IO 争抢,一般建议每个 Executor 2 到 5 个核。
# 提交时加上 GC 日志,方便排查内存问题 --conf "spark.executor.extraJavaOptions=-XX:+PrintGCDetails -XX:+PrintGCTimeStamps"注意:
spark.executor.extraJavaOptions里不要设-Xmx,Executor 堆大小由spark.executor.memory控制,手动设-Xmx会和 Spark 的内存管理冲突。
5. 避坑与排查:那些让集群起不来的细节
5.1 现象:spark-shell 启动报 ClassNotFoundException: org.apache.hadoop.conf.Configuration
原因:用了bin-without-hadoop包,或者SPARK_DIST_CLASSPATH没设对,Spark 找不到 Hadoop 客户端类。
解决:换成bin-hadoop3.2包最省事。如果必须用 without-hadoop,在spark-env.sh里加:
export SPARK_DIST_CLASSPATH=$(hadoop classpath)hadoop classpath命令会输出 Hadoop 所有 jar 的路径,前提是hadoop命令在 PATH 里。
5.2 现象:提交 YARN 任务一直处于 ACCEPTED 状态,不进入 RUNNING
原因:YARN 队列资源不足,或者队列有容量限制,AM 申请不到 Container。
解决:先yarn application -status <appId>看诊断信息,再用yarn queue -status <queueName>看队列资源。如果是资源不足,减小--executor-memory和--num-executors;如果是队列配置问题,换队列或找运维调整。还有一种情况是spark.yarn.am.memory设得太小,AM 起不来,默认 512MB,复杂任务建议调到 1g。
5.3 现象:HDFS 写入报 Permission denied: user=xxx, access=WRITE
原因:HDFS 目录属主和提交用户不一致,且目录没有开放写权限。
解决:用hdfs dfs -ls /path看目录属主,用hdfs dfs -chown改属主,或者hdfs dfs -chmod 777临时放开(仅测试环境)。生产环境应该用 Kerberos 或按用户建目录,不要图省事用 777。
5.4 现象:Executor 频繁 GC,任务跑得比单机还慢
原因:spark.executor.memory设得太大,导致单次 GC 停顿时间长;或者spark.executor.cores设得太多,多个 task 争抢内存。
解决:Executor 内存一般不超过 16g,超过后 GC 停顿会明显变长。每个 Executor 的核数控制在 5 以内。如果数据量大,宁可多加 Executor,不要给单个 Executor 堆太大内存。另外检查是否有数据倾斜,某个 key 的数据量远超其他 key,会导致单个 task 内存爆掉,用df.groupBy("key").count().orderBy(desc("count")).show()排查。
5.5 现象:伪分布式下 DataNode 启动后马上退出
原因:多次hdfs namenode -format导致 NameNode 和 DataNode 的 clusterID 不一致,或者dfs.datanode.data.dir指向的目录里有旧数据。
解决:停掉所有进程,删掉dfs.namenode.name.dir和dfs.datanode.data.dir下的所有数据,重新hdfs namenode -format,再启动。注意格式化只能做一次,重复格式化是新手最常见的翻车原因。
6. 把 spark-3.2.0-bin-hadoop3.2 用出生产味:几个我常用的进阶技巧
环境跑通只是起点,真正做数据分析项目时,有几个技巧能让你少走很多弯路。第一个是spark-defaults.conf的集中管理。不要每次提交都敲一长串--conf,把常用配置写进conf/spark-defaults.conf:
spark.master spark://localhost:7077 spark.executor.memory 2g spark.executor.cores 2 spark.sql.shuffle.partitions 20 spark.serializer org.apache.spark.serializer.KryoSerializer spark.hadoop.fs.defaultFS hdfs://localhost:9000KryoSerializer比默认的 Java 序列化快很多,尤其是 shuffle 数据量大时。spark.sql.shuffle.partitions按数据量调,小数据设 20 比默认 200 快一个数量级。
第二个技巧是用spark-submit的--files和--archives分发依赖。做网约车数据分析这类项目时,经常需要额外的 Python 包或配置文件,用--archives传一个 venv 压缩包,在 Executor 上解压使用,比在每个节点手动装包可靠得多。
第三个是验证数据倾斜的实用方法。除了看 Web UI 的 task 时间分布,我习惯在代码里加一段:
# 检查分区数据量分布,定位倾斜 df.rdd.mapPartitionsWithIndex( lambda idx, it: [(idx, sum(1 for _ in it))] ).collect()如果某个分区的数据量是其他的几十倍,基本可以确定倾斜。处理倾斜的常见手段是加盐打散:给 key 拼接随机前缀,聚合后再去掉前缀二次聚合。
最后一个习惯:每次改完配置,先jps看进程,再跑一个最小任务验证,不要直接上大任务。我见过太多次改了一个参数导致整个集群起不来,然后花两小时排查,其实只是spark-env.sh里多了一个空格。搭环境这件事,慢就是快,每一步验证到位,后面才不用吃后悔药。希望帮到你。
本文还有配套的精品资源,点击获取