1. 为什么今天还要认真学 Spark 安装部署?不是早有 Docker 一键脚本了吗?
Spark --最全的安装部署,这个标题看着像老派运维文档,但实际是每个想真正用好 Spark 的人绕不开的“成人礼”。我带过不少刚从 Python Pandas 转过来的数据分析同学,一上来就跑pyspark,发现.show()卡住、collect()报 OOM、集群任务永远 pending——最后查一圈,问题出在本地模式没配对 Java 版本,Standalone 的 worker 启动时连不上 master 的 7077 端口,YARN 上提交任务却因spark.yarn.jars指向了本地路径而直接失败。这些都不是代码问题,是环境骨架没搭稳。
你可能觉得:“现在都上云了,EMR、Databricks、阿里云 E-MapReduce 一点就起集群,谁还手装 Spark?”——这话对一半。云平台封装得越深,底层异常越难定位。去年某电商大促期间,一个实时推荐任务在 EMR 上延迟飙升,排查三天才发现是 YARN 的yarn.nodemanager.resource.memory-mb和 Spark executor memory 配置冲突,导致 container 频繁被 kill;而这个问题,只有亲手在 Standalone 模式下调过SPARK_WORKER_MEMORY、在 YARN 模式下改过yarn-site.xml的人,才能一眼看出资源调度链路哪一环断了。
更关键的是:所有 Spark 性能调优、故障诊断、版本兼容性判断,都建立在你对部署模型的肌肉记忆之上。比如你知道 local[*] 是用当前机器所有 CPU 核数模拟并行度,那看到local[4]就明白它和物理核数无关,只和逻辑线程数有关;你知道 Standalone 的 master 会把 application jar 自动分发到各 worker,那遇到ClassNotFoundException就不会傻等 driver 日志,而是立刻去 worker 节点查/spark/work/目录下有没有对应 jar 包;你知道 Spark on YARN 分 client 和 cluster 两种 deploy mode,那看到--master yarn --deploy-mode client就清楚 driver 进程跑在提交命令的机器上,网络策略、JVM 参数、日志落盘路径全要按这台机器来设计。
所以这不是一份“过时”的安装指南,而是一套 Spark 运行时的解剖图谱。它覆盖 local(单机调试)、Standalone(小规模自建)、HA(生产级容灾)、YARN(企业级资源统一调度)四大核心部署形态,每一种都对应不同的资源抽象层级、故障域边界和监控粒度。接下来我会带你一层层拆开:不只告诉你敲什么命令,更说清每个配置项背后的调度逻辑、每个端口暴露的通信协议、每个目录承载的生命周期状态——就像修车师傅不只换轮胎,还得知道减震器怎么影响转向精度。
2. 四种部署模式的本质差异与选型逻辑
2.1 不是“装法不同”,是运行时契约完全不同
很多人把 Spark 安装理解成“换几个配置文件”,这是最大误区。四种模式本质是 Spark ApplicationMaster(AM)与资源管理器之间签订的不同服务等级协议(SLA)。Local 模式下,Spark 自己当资源管家;Standalone 模式下,Spark 自建轻量级资源池;HA 模式是 Standalone 的高可用增强;YARN 模式则是 Spark 向外部资源池(YARN)申请服务。它们的差异不是安装步骤多几行,而是整个执行模型的根基。
| 部署模式 | 资源管理者 | Application 生命周期控制方 | 典型适用场景 | 故障恢复能力 | 网络依赖复杂度 |
|---|---|---|---|---|---|
| local | Spark Driver 进程自身 | Driver 进程 | 本地开发、单元测试、小数据集验证 | 无(进程挂即任务亡) | 零(纯本机 loopback) |
| standalone | Spark Master 进程 | Master + Worker 进程 | 5~20 节点内网集群、POC 验证、教学实验 | Master 单点故障(需 HA 改进) | 中(Master/Worker 间需互通 7077/8081 等端口) |
| standalone-HA | ZooKeeper 协调多个 Master | ZooKeeper + Master/Worker | 生产环境中小规模集群(<50 节点) | Master 故障秒级切换(ZK 选举) | 高(需 ZK 集群 + Master 间心跳) |
| yarn | YARN ResourceManager | YARN RM + Spark AM | 企业级混合负载集群(Hadoop 生态共存) | RM 故障由 YARN HA 保障,AM 可重试 | 极高(需 HDFS、YARN、Spark 三方 Kerberos 认证) |
提示:很多团队踩坑源于混淆了“部署模式”和“执行模式”。比如在 YARN 上用
--deploy-mode client,driver 进程仍在提交机上运行,此时若提交机网络中断,整个任务就失败——这和部署模式无关,是执行模式选择错误。部署模式决定资源从哪来,执行模式决定 driver 在哪跑。
2.2 Local 模式:不是“玩具”,是调试黄金标准
spark-shell --master local[*]看似简单,却是最易被低估的环节。local[*]中的*并非代表“所有核”,而是Runtime.getRuntime().availableProcessors()返回值——在 Docker 容器里,若未设置--cpus或--cpu-quota,它返回的是宿主机 CPU 数,而非容器限制值。我见过某金融客户在 K8s 上跑 Spark 任务,local[*] 启动后疯狂创建 64 个线程,瞬间打满节点,触发 K8s OOMKill。
更隐蔽的是内存陷阱。Local 模式下,spark.driver.memory默认 1g,但 JVM 堆外内存(off-heap)如 Netty 缓冲区、Unsafe 操作空间不在此限。当处理 Parquet 大文件时,spark.sql.adaptive.enabled=true会动态调整 shuffle 分区,若 driver 内存不足,直接OutOfMemoryError: Direct buffer memory。解决方案不是盲目加spark.driver.memory,而是配spark.unsafe.sorter.spill.reader.buffer.size(默认 1m)和spark.shuffle.file.buffer(默认 32k)——这些参数在 Standalone/YARN 模式下同样生效,但 Local 模式下最容易暴露。
2.3 Standalone 模式:自建集群的“最小可行系统”
Standalone 的核心是 Master-Worker 架构,但它不是 Hadoop YARN 的简化版。YARN 的 ResourceManager 是纯粹的资源仲裁者,NodeManager 只负责容器启停;而 Spark Worker 既要启动 executor 进程,又要监控其内存/CPU 使用、汇报心跳、缓存 block 数据。这意味着 Worker 的稳定性直接影响 shuffle 效率。
关键配置SPARK_WORKER_CORES和SPARK_WORKER_MEMORY必须与物理机真实资源匹配。曾有个案例:某实验室用 32 核 128G 服务器部署 Worker,但设SPARK_WORKER_CORES=32,结果所有 executor 竞争同一组 CPU,GC 延迟飙升。正确做法是留 2 核给 OS + 2 核给 Worker 进程自身,SPARK_WORKER_CORES=28,再通过spark.executor.cores=4控制每个 executor 的并发线程数。
注意:Standalone 不支持细粒度资源隔离。YARN 可以用 cgroups 限制单个 container 的 CPU quota,而 Standalone 的 Worker 进程内所有 executor 共享 JVM 堆和操作系统线程,因此
spark.executor.memory设置过高会导致 GC 停顿时间不可控。生产环境建议单 Worker 上 executor 数 ≤ 4,每个 executor memory ≤ 16g。
2.4 HA 模式:ZooKeeper 不是“加个服务”,是重构故障域
Standalone-HA 不是简单起多个 Master 进程。它要求:
- 所有 Master 进程配置相同
spark.deploy.recoveryMode=ZOOKEEPER spark.deploy.zookeeper.url指向 ZK 集群(非单点!)spark.deploy.zookeeper.dir指定 ZK 上存储 recovery 信息的路径(如/spark)
最关键的细节:Worker 只连接当前 active Master,不感知 ZK。当 active Master 故障,ZK 选举新 Master 后,Worker 会主动重连——这个重连动作由 Worker 进程内部的MasterWatcher线程完成,间隔由spark.deploy.zookeeper.poll.interval.ms(默认 10s)控制。这意味着故障切换窗口 = ZK 会话超时时间(sessionTimeoutMs)+ 重连间隔。若 ZKsessionTimeoutMs=30000,则最长可能 40 秒无调度能力。
另一个隐形坑:Application 状态恢复。Standalone 默认不持久化 application 状态,HA 模式下需开启spark.deploy.recoveryMode=ZOOKEEPER且所有 Master 共享同一 ZK 路径,才能在 Master 切换后继续调度未完成的任务。但注意:driver 进程本身不恢复,已运行的 task 会失败重试,这是 Spark 的 at-least-once 语义决定的。
2.5 YARN 模式:不是“换个 master 地址”,是融入企业资源治理体系
Spark on YARN 的本质是 Spark ApplicationMaster(AM)作为 YARN 上的一个普通 container 运行,由 YARN RM 分配资源,再由 AM 向 YARN 申请 executor containers。这带来三个硬性约束:
- HDFS 依赖:
spark.yarn.jar必须指向 HDFS 路径(如hdfs://nameservice1/spark/jars/spark-yarn_2.12-3.3.2.jar),因为 YARN NodeManager 只能从 HDFS 或本地路径拉取 jar,不能访问提交机的本地文件系统。 - Kerberos 认证:若集群开启安全认证,
spark.yarn.principal和spark.yarn.keytab必须指定,且 keytab 文件需上传到 HDFS 并设置可读权限。 - Container 内存计算公式:YARN 的
yarn.scheduler.maximum-allocation-mb限制了单 container 最大内存,而 Spark executor 的总内存 =spark.executor.memory+spark.executor.memoryOverhead(默认为前者的 10%,最小 384m)。若计算值超过 YARN 限制,container 申请直接被拒绝。
我亲眼见过某政务云项目因忽略第三点,在 YARN 上反复报Requested container exceeds maximum allowed capacity,排查两天才发现spark.executor.memory=12g导致总内存 13.2g,而 YARN 设置的maximum-allocation-mb=12288(12g)。
3. 四种模式实操详解:从零开始的完整部署链
3.1 Local 模式:单机调试的终极配置清单
Local 模式虽简单,但要发挥全部调试价值,必须定制spark-defaults.conf。以下是我压箱底的本地开发配置模板(路径:$SPARK_HOME/conf/spark-defaults.conf):
# 核心执行参数 spark.master local[8] spark.driver.memory 4g spark.driver.maxResultSize 2g spark.sql.adaptive.enabled true spark.sql.adaptive.coalescePartitions.enabled true # 内存与 GC 优化(避免频繁 Full GC) spark.driver.extraJavaOptions -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+UnlockExperimentalVMOptions -XX:G1MaxNewSizePercent=60 -XX:G1NewSizePercent=40 # Shuffle 与序列化(提升本地小数据集性能) spark.serializer org.apache.spark.serializer.KryoSerializer spark.kryo.registrationRequired false spark.shuffle.compress true spark.io.compression.codec lzf # SQL 优化(本地调试常用) spark.sql.adaptive.localShuffleReader.enabled true spark.sql.adaptive.skewJoin.enabled true为什么这样配?
local[8]:避开local[*]在容器中的不确定性,明确指定 8 线程,匹配主流笔记本 CPU 核数;spark.driver.maxResultSize=2g:防止collect()拉取过多数据导致 driver OOM,比默认 1g 更安全;- G1GC 参数:Spark driver 是典型的低延迟、高吞吐应用,G1 比 CMS 更适合,
MaxGCPauseMillis=50将 GC 停顿控制在 50ms 内; lzf压缩:比默认snappy压缩率更高、CPU 开销更低,本地调试时 shuffle 数据量不大,优先选压缩率。
实操验证步骤:
- 下载 Spark 二进制包(推荐 3.3.2,Scala 2.12,Hadoop 3.3+);
- 解压后进入
conf/目录,复制spark-defaults.conf.template为spark-defaults.conf,填入上述配置; - 启动
spark-shell,执行:
观察 Web UI(http://localhost:4040)的 Stages 页面,确认 Parallelize 任务被拆分为 8 个 tasks(对应sc.parallelize(1 to 1000000).map(_ * 2).count()local[8]); - 故意触发 OOM:执行
sc.parallelize(1 to 10000000).collect(),观察是否报SparkException: Job aborted due to stage failure: Total size of serialized results of 10000000 tasks (2.1 GB) exceeded spark.driver.maxResultSize (2.0 GB)—— 这证明配置生效。
实操心得:本地模式下 Web UI 的 4040 端口可能被占用。可在
spark-defaults.conf中加spark.ui.port 4041,或启动时加--conf spark.ui.port=4041。别用--conf覆盖所有配置,spark-defaults.conf是唯一权威来源。
3.2 Standalone 模式:三节点集群搭建全流程
假设三台机器:master-node(192.168.1.10)、worker1-node(192.168.1.11)、worker2-node(192.168.1.12),均安装 JDK 11+、SSH 免密互通。
Step 1:统一环境准备所有节点执行:
# 创建专用用户(避免 root 运行) sudo useradd -m sparkuser sudo passwd sparkuser # 授予免密 sudo 权限(仅限 spark 目录) echo "sparkuser ALL=(ALL) NOPASSWD: /opt/spark/sbin/*.sh" | sudo tee /etc/sudoers.d/sparkStep 2:配置spark-env.sh在master-node的$SPARK_HOME/conf/spark-env.sh中:
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 export SPARK_MASTER_HOST=192.168.1.10 export SPARK_MASTER_PORT=7077 export SPARK_MASTER_WEBUI_PORT=8080 export SPARK_WORKER_CORES=28 export SPARK_WORKER_MEMORY=64g export SPARK_WORKER_PORT=7078 export SPARK_WORKER_WEBUI_PORT=8081 export SPARK_DAEMON_MEMORY=2g # Master/Worker 进程自身 JVM 内存在worker1/2-node的同文件中,只需改两行:
export SPARK_MASTER_HOST=192.168.1.10 export SPARK_WORKER_CORES=24 # worker 节点留更多核给 OSStep 3:配置slaves文件master-node的$SPARK_HOME/conf/slaves写入:
192.168.1.11 192.168.1.12注意:这里写 IP,不是 hostname,避免 DNS 解析失败。
Step 4:分发与启动
# 从 master-node 分发到所有 worker scp -r $SPARK_HOME sparkuser@192.168.1.11:/opt/ scp -r $SPARK_HOME sparkuser@192.168.1.12:/opt/ # 启动 master(master-node) $SPARK_HOME/sbin/start-master.sh # 启动所有 workers(master-node) $SPARK_HOME/sbin/start-slaves.sh验证要点:
- 访问
http://192.168.1.10:8080,确认 Workers 列表显示两个节点,状态为 ALIVE; - 在
master-node执行spark-submit --master spark://192.168.1.10:7077 ...,观察 Web UI 的 Executors 标签页,确认 executor 进程出现在对应 worker 的 8081 端口页面; - 关闭一个 worker(
$SPARK_HOME/sbin/stop-worker.sh),观察 master UI 的 Workers 列表是否变为 DEAD,且 running applications 是否自动重调度到剩余 worker。
常见问题:Worker 启动后立即退出。检查
logs/spark-sparkuser-org.apache.spark.deploy.worker.Worker-1-hostname.out,90% 是java.net.BindException: Address already in use—— 确认SPARK_WORKER_PORT未被占用,或用netstat -tuln | grep 7078查看。
3.3 Standalone-HA 模式:ZooKeeper 集群协同部署
HA 模式需至少 3 台 ZooKeeper 节点(奇数台防脑裂)。假设 ZK 集群:zk1(192.168.1.20)、zk2(192.168.1.21)、zk3(192.168.1.22)。
Step 1:部署 ZooKeeper(简略)每台 ZK 节点:
# conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/var/lib/zookeeper clientPort=2181 server.1=192.168.1.20:2888:3888 server.2=192.168.1.21:2888:3888 server.3=192.168.1.22:2888:3888创建myid文件:echo "1" > /var/lib/zookeeper/myid(对应 server.x 的 x)。
Step 2:配置 Spark Master HA所有 Master 节点(master-node+ 新增backup-master)的spark-env.sh:
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=192.168.1.20:2181,192.168.1.21:2181,192.168.1.22:2181 -Dspark.deploy.zookeeper.dir=/spark-ha"注意:-D参数必须写在SPARK_DAEMON_JAVA_OPTS中,不能写在spark-defaults.conf。
Step 3:启动双 Master
# 在 master-node 启动第一个 Master $SPARK_HOME/sbin/start-master.sh # 在 backup-master 启动第二个 Master $SPARK_HOME/sbin/start-master.sh此时访问http://192.168.1.10:8080和http://192.168.1.13:8080(backup-master IP),只有一个显示 “ALIVE”,另一个显示 “STANDBY”。
故障注入测试:
kill -9主 Master 进程;- 等待 10~15 秒(ZK session timeout + poll interval),访问 standby Master 的 8080 端口,状态应变为 “ALIVE”;
- 提交新任务
spark-submit --master spark://192.168.1.10:7077,192.168.1.13:7077 ...(注意 master URL 用逗号分隔),验证任务正常运行。
实操心得:ZK 路径
/spark-ha必须所有 Master 一致,且 ZK 集群需稳定。曾有个项目因 ZK 单节点磁盘满导致 session 失效,两个 Master 同时认为自己是 leader,造成任务重复调度。务必监控 ZK 的Latency_avg和OutstandingRequests指标。
3.4 Spark on YARN 模式:企业级集成部署
前提:Hadoop 集群已运行(HDFS + YARN),版本兼容 Spark(Spark 3.3+ 需 Hadoop 3.3+)。
Step 1:准备 Spark 依赖包
# 在 Spark 解压目录执行 ./dev/make-distribution.sh --tgz -Phadoop-3.3 -Pyarn -Pvendor-repo # 生成 spark-3.3.2-bin-hadoop3.3.tgz将此包分发到所有 YARN NodeManager 节点的/opt/目录,并解压。
Step 2:配置spark-defaults.conf
spark.master yarn spark.submit.deployMode client spark.yarn.jars hdfs://nameservice1/spark/jars/* spark.yarn.archive hdfs://nameservice1/spark/archives/spark-3.3.2-yarn-archive.tar.gz spark.yarn.am.memory 2g spark.yarn.am.memoryOverhead 1g spark.executor.memory 8g spark.executor.memoryOverhead 4g spark.executor.cores 4 spark.executor.instances 10Step 3:上传依赖到 HDFS
# 创建 HDFS 目录 hdfs dfs -mkdir -p /spark/jars /spark/archives # 上传 Spark 自身 jar(关键!) hdfs dfs -put $SPARK_HOME/jars/* /spark/jars/ # 打包 YARN 依赖(避免单个 jar 过多) cd $SPARK_HOME tar -czf spark-yarn-archive.tar.gz yarn/ commons-configuration/ hadoop-* log4j* hdfs dfs -put spark-yarn-archive.tar.gz /spark/archives/Step 4:提交任务验证
spark-submit \ --master yarn \ --deploy-mode client \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.3.2.jar \ 1000观察 YARN ResourceManager UI(http://rm-host:8088),确认 Application 状态为 FINISHED,且spark-sql或spark-shell启动后能在 YARN 上看到对应的 ApplicationMaster container。
注意事项:
spark.yarn.jars的*通配符必须存在,YARN 会自动扫描该目录下所有 jar。若写成具体 jar 名,新增依赖需手动更新。spark.yarn.archive是性能优化项,将大量小 jar 打包成 tar.gz,减少 YARN 分发次数,实测可提升大作业启动速度 30%。
4. 部署后必做的五项健康检查与避坑指南
4.1 端口连通性矩阵:一张表扫清所有网络障碍
Spark 各组件间通信依赖固定端口,防火墙或安全组未开放会导致“看似启动成功,实则无法工作”。以下是四模式通用端口检查表:
| 组件 | 端口 | 协议 | 作用 | 检查命令(在客户端执行) | 常见失败原因 |
|---|---|---|---|---|---|
| Master (Standalone) | 7077 | TCP | Worker 注册、Application 提交 | telnet 192.168.1.10 7077 | Master 未启动、防火墙拦截、SPARK_MASTER_HOST配错 |
| Master WebUI | 8080 | HTTP | 集群状态监控 | curl -I http://192.168.1.10:8080 | Master 启动但 WebUI 端口被占、SPARK_MASTER_WEBUI_PORT配错 |
| Worker WebUI | 8081 | HTTP | Worker 资源详情 | curl -I http://192.168.1.11:8081 | Worker 未启动、SPARK_WORKER_WEBUI_PORT配错 |
| YARN ResourceManager | 8088 | HTTP | YARN 应用监控 | curl -I http://rm-host:8088 | YARN RM 未启动、HDFS 未就绪 |
| YARN NodeManager | 8042 | HTTP | NodeManager 状态 | curl -I http://nm-host:8042 | NodeManager 未启动、YARN 配置未同步 |
提示:在云环境(如 AWS/Aliyun),安全组规则必须双向开放。例如 Worker 要连 Master 的 7077,同时 Master 也要能回连 Worker 的 8081(用于 UI 展示),否则 WebUI 上 Worker 状态显示为 UNKNOWN。
4.2 日志溯源三板斧:快速定位启动失败根源
90% 的部署失败,日志里早有答案。记住这三个关键日志位置:
Master 启动日志:
$SPARK_HOME/logs/spark-<user>-org.apache.spark.deploy.master.Master-1-<hostname>.out- 关键线索:
Starting Spark master at spark://...表示成功;Failed to bind to address表示端口冲突;ZooKeeper not available表示 HA 配置错误。
- 关键线索:
Worker 启动日志:
$SPARK_HOME/logs/spark-<user>-org.apache.spark.deploy.worker.Worker-1-<hostname>.out- 关键线索:
Successfully registered with master表示注册成功;Connection refused表示连不上 Master;Failed to load class表示 Java 版本不匹配。
- 关键线索:
YARN ApplicationMaster 日志:通过 YARN UI 点击 Application → Logs,查看
stdout和syslog- 关键线索:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/hadoop/fs/FileSystem表示 Hadoop 依赖缺失;Failed to connect to master表示spark.master配错。
- 关键线索:
实操技巧:用
tail -f实时跟踪日志,同时在另一窗口执行启动命令。当看到Starting Spark master时,立刻Ctrl+C中断 tail,说明 Master 已启动;若一直卡在INFO Utils: Successfully started service 'sparkMaster'后无下文,则是 WebUI 端口问题。
4.3 Java 版本与 Scala 版本的“双重绑定”陷阱
Spark 是 Scala 编写的,其二进制包严格绑定 Scala 版本(如spark-3.3.2-bin-hadoop3.3.tgz中的2.12表示 Scala 2.12)。而 Java 版本影响 JVM 行为:
- Spark 3.0+ 要求 Java 8u191+ 或 Java 11+;
- Java 17+ 需添加
--add-opens参数(Spark 3.3.2 已内置,但旧版需手动加); - 致命组合:Java 17 + Scala 2.11(Spark 2.x)会导致
java.lang.NoSuchMethodError: scala.runtime.LazyRef.<init>(Ljava/lang/Object;)V—— 因为 Scala 2.11 的 LazyRef 构造函数签名在 Java 17 中被移除。
验证方法:
# 查看 Spark 包 Scala 版本 unzip -p $SPARK_HOME/jars/spark-core_2.12-3.3.2.jar META-INF/MANIFEST.MF | grep Implementation-Title # 输出:Implementation-Title: Spark project Core (Scala 2.12) # 查看 Java 版本 java -version # 必须匹配:Scala 2.12 对应 Java 8/11,Scala 2.13 对应 Java 11/174.4 内存配置的“三层漏斗模型”:从 OS 到 Executor 的逐级收敛
Spark 内存不是简单设置spark.executor.memory就完事。它遵循严格的三层漏斗:
- OS 层:物理内存 - OS 预留(建议 4g)- 其他进程内存;
- JVM 层:
spark.executor.memory= JVM heap size; - Off-heap 层:
spark.executor.memoryOverhead= heap 外内存(Netty buffer、JVM native memory);
计算公式:executor 总内存 = spark.executor.memory + spark.executor.memoryOverhead
YARN 限制:yarn.scheduler.maximum-allocation-mb必须 ≥ executor 总内存。
实例演算:
一台 64g 内存服务器,YARN 设置maximum-allocation-mb=57344(56g):
- 若设
spark.executor.memory=48g,则memoryOverhead至少max(0.1*48g, 384m)=4.8g,总内存 52.8g < 56g,OK; - 若设
spark.executor.memory=50g,则memoryOverhead=5g,总内存 55g < 56g,仍 OK; - 若设
spark.executor.memory=52g,则memoryOverhead=5.2g,总内存 57.2g > 56g,YARN 拒绝分配。
避坑口诀:“Executor memory 留 10% 给 overhead,总和别超 YARN 最大值,OS 再留 4g 给自己”。
4.5 WebUI 访问不了?五个维度交叉排查法
WebUI 打不开是最常见问题,按此顺序排查:
进程是否真在运行?
ps aux | grep spark确认 master/worker 进程存在,注意区分org.apache.spark.deploy.master.Master和org.apache.spark.deploy.worker.Worker。端口是否监听?
netstat -tuln | grep :8080确认 master 进程绑定了 8080,而非127.0.0.1:8080(只允许本地访问)。配置是否生效?
查看logs/spark-*.out启动日志,搜索Bound to address,确认绑定的是0.0.0.0:8080而非127.0.0.1:8080。网络是否可达?
从浏览器所在机器telnet 192.168.1.10 8080,若不通,检查防火墙(sudo ufw status)或云安全组。反向代理是否干扰?
若通过 Nginx 反代 WebUI,确认proxy_pass指向http://192.168.1.10:8080,且proxy_set_header Host $host;未被篡改。
实操心得:Spark 3.0+ 默认绑定
0.0.0.0,但某些旧版或定制包可能默认127.0.0.1。强制指定:在spark-env.sh加export SPARK_MASTER_WEBUI_ADDRESS=0.0.0.0。
5. 后续演进:从部署到生产就绪的必经之路
部署完成只是起点。真正的生产就绪,还需跨越三道坎:
第一坎:监控体系落地
不要只靠 WebUI。必须集成 Prometheus + Grafana:
- Spark 提供
/metrics/master/json和/metrics/applications/jsonREST API; - 用 Prometheus 的
spark_exporter抓取,重点关注master.aliveWorkers(存活 Worker 数)、application.status(应用状态)、executor.count(executor 数量); - 设置告警:
aliveWorkers < 2触发短信,application.status == "FINISHED"但duration > 300s触发邮件。
第二坎:日志集中管理
Worker 日志分散在各节点,故障时翻找效率极低。方案:
- Filebeat 采集
$SPARK_HOME/logs/下所有*.out和*.err; - 发送到 ELK,索引名
spark-logs-{date}; - Kibana 中用
process: "Worker"+message: "OutOfMemory"快速定位 GC 问题。
第三坎:配置即代码(GitOps)
所有spark-defaults.conf、spark-env.sh、yarn-site.xml必须纳入 Git 仓库,分支策略:
- `main