Spark四大部署模式原理与生产级配置实战
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 ApplicationMasterAM与资源管理器之间签订的不同服务等级协议SLA。Local 模式下Spark 自己当资源管家Standalone 模式下Spark 自建轻量级资源池HA 模式是 Standalone 的高可用增强YARN 模式则是 Spark 向外部资源池YARN申请服务。它们的差异不是安装步骤多几行而是整个执行模型的根基。部署模式资源管理者Application 生命周期控制方典型适用场景故障恢复能力网络依赖复杂度localSpark Driver 进程自身Driver 进程本地开发、单元测试、小数据集验证无进程挂即任务亡零纯本机 loopbackstandaloneSpark Master 进程Master Worker 进程5~20 节点内网集群、POC 验证、教学实验Master 单点故障需 HA 改进中Master/Worker 间需互通 7077/8081 等端口standalone-HAZooKeeper 协调多个 MasterZooKeeper Master/Worker生产环境中小规模集群50 节点Master 故障秒级切换ZK 选举高需 ZK 集群 Master 间心跳yarnYARN ResourceManagerYARN RM Spark AM企业级混合负载集群Hadoop 生态共存RM 故障由 YARN HA 保障AM 可重试极高需 HDFS、YARN、Spark 三方 Kerberos 认证提示很多团队踩坑源于混淆了“部署模式”和“执行模式”。比如在 YARN 上用--deploy-mode clientdriver 进程仍在提交机上运行此时若提交机网络中断整个任务就失败——这和部署模式无关是执行模式选择错误。部署模式决定资源从哪来执行模式决定 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.enabledtrue会动态调整 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_CORES32结果所有 executor 竞争同一组 CPUGC 延迟飙升。正确做法是留 2 核给 OS 2 核给 Worker 进程自身SPARK_WORKER_CORES28再通过spark.executor.cores4控制每个 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.recoveryModeZOOKEEPERspark.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 重连间隔。若 ZKsessionTimeoutMs30000则最长可能 40 秒无调度能力。另一个隐形坑Application 状态恢复。Standalone 默认不持久化 application 状态HA 模式下需开启spark.deploy.recoveryModeZOOKEEPER且所有 Master 共享同一 ZK 路径才能在 Master 切换后继续调度未完成的任务。但注意driver 进程本身不恢复已运行的 task 会失败重试这是 Spark 的 at-least-once 语义决定的。2.5 YARN 模式不是“换个 master 地址”是融入企业资源治理体系Spark on YARN 的本质是 Spark ApplicationMasterAM作为 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.memoryspark.executor.memoryOverhead默认为前者的 10%最小 384m。若计算值超过 YARN 限制container 申请直接被拒绝。我亲眼见过某政务云项目因忽略第三点在 YARN 上反复报Requested container exceeds maximum allowed capacity排查两天才发现spark.executor.memory12g导致总内存 13.2g而 YARN 设置的maximum-allocation-mb1228812g。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:MaxGCPauseMillis50 -XX:UnlockExperimentalVMOptions -XX:G1MaxNewSizePercent60 -XX:G1NewSizePercent40 # 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.maxResultSize2g防止collect()拉取过多数据导致 driver OOM比默认 1g 更安全G1GC 参数Spark driver 是典型的低延迟、高吞吐应用G1 比 CMS 更适合MaxGCPauseMillis50将 GC 停顿控制在 50ms 内lzf压缩比默认snappy压缩率更高、CPU 开销更低本地调试时 shuffle 数据量不大优先选压缩率。实操验证步骤下载 Spark 二进制包推荐 3.3.2Scala 2.12Hadoop 3.3解压后进入conf/目录复制spark-defaults.conf.template为spark-defaults.conf填入上述配置启动spark-shell执行sc.parallelize(1 to 1000000).map(_ * 2).count()观察 Web UIhttp://localhost:4040的 Stages 页面确认 Parallelize 任务被拆分为 8 个 tasks对应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.port4041。别用--conf覆盖所有配置spark-defaults.conf是唯一权威来源。3.2 Standalone 模式三节点集群搭建全流程假设三台机器master-node192.168.1.10、worker1-node192.168.1.11、worker2-node192.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_HOST192.168.1.10 export SPARK_MASTER_PORT7077 export SPARK_MASTER_WEBUI_PORT8080 export SPARK_WORKER_CORES28 export SPARK_WORKER_MEMORY64g export SPARK_WORKER_PORT7078 export SPARK_WORKER_WEBUI_PORT8081 export SPARK_DAEMON_MEMORY2g # Master/Worker 进程自身 JVM 内存在worker1/2-node的同文件中只需改两行export SPARK_MASTER_HOST192.168.1.10 export SPARK_WORKER_CORES24 # 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 sparkuser192.168.1.11:/opt/ scp -r $SPARK_HOME sparkuser192.168.1.12:/opt/ # 启动 mastermaster-node $SPARK_HOME/sbin/start-master.sh # 启动所有 workersmaster-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.out90% 是java.net.BindException: Address already in use—— 确认SPARK_WORKER_PORT未被占用或用netstat -tuln | grep 7078查看。3.3 Standalone-HA 模式ZooKeeper 集群协同部署HA 模式需至少 3 台 ZooKeeper 节点奇数台防脑裂。假设 ZK 集群zk1192.168.1.20、zk2192.168.1.21、zk3192.168.1.22。Step 1部署 ZooKeeper简略每台 ZK 节点# conf/zoo.cfg tickTime2000 initLimit10 syncLimit5 dataDir/var/lib/zookeeper clientPort2181 server.1192.168.1.20:2888:3888 server.2192.168.1.21:2888:3888 server.3192.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.shexport SPARK_DAEMON_JAVA_OPTS-Dspark.deploy.recoveryModeZOOKEEPER -Dspark.deploy.zookeeper.url192.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:8080backup-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版本兼容 SparkSpark 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.confspark.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 UIhttp://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)7077TCPWorker 注册、Application 提交telnet 192.168.1.10 7077Master 未启动、防火墙拦截、SPARK_MASTER_HOST配错Master WebUI8080HTTP集群状态监控curl -I http://192.168.1.10:8080Master 启动但 WebUI 端口被占、SPARK_MASTER_WEBUI_PORT配错Worker WebUI8081HTTPWorker 资源详情curl -I http://192.168.1.11:8081Worker 未启动、SPARK_WORKER_WEBUI_PORT配错YARN ResourceManager8088HTTPYARN 应用监控curl -I http://rm-host:8088YARN RM 未启动、HDFS 未就绪YARN NodeManager8042HTTPNodeManager 状态curl -I http://nm-host:8042NodeManager 未启动、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表示连不上 MasterFailed 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时立刻CtrlC中断 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 11Java 17 需添加--add-opens参数Spark 3.3.2 已内置但旧版需手动加致命组合Java 17 Scala 2.11Spark 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/11Scala 2.13 对应 Java 11/174.4 内存配置的“三层漏斗模型”从 OS 到 Executor 的逐级收敛Spark 内存不是简单设置spark.executor.memory就完事。它遵循严格的三层漏斗OS 层物理内存 - OS 预留建议 4g- 其他进程内存JVM 层spark.executor.memory JVM heap sizeOff-heap 层spark.executor.memoryOverhead heap 外内存Netty buffer、JVM native memory计算公式executor 总内存 spark.executor.memory spark.executor.memoryOverheadYARN 限制yarn.scheduler.maximum-allocation-mb必须 ≥ executor 总内存。实例演算一台 64g 内存服务器YARN 设置maximum-allocation-mb5734456g若设spark.executor.memory48g则memoryOverhead至少max(0.1*48g, 384m)4.8g总内存 52.8g 56gOK若设spark.executor.memory50g则memoryOverhead5g总内存 55g 56g仍 OK若设spark.executor.memory52g则memoryOverhead5.2g总内存 57.2g 56gYARN 拒绝分配。避坑口诀“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_ADDRESS0.0.0.0。5. 后续演进从部署到生产就绪的必经之路部署完成只是起点。真正的生产就绪还需跨越三道坎第一坎监控体系落地不要只靠 WebUI。必须集成 Prometheus GrafanaSpark 提供/metrics/master/json和/metrics/applications/jsonREST API用 Prometheus 的spark_exporter抓取重点关注master.aliveWorkers存活 Worker 数、application.status应用状态、executor.countexecutor 数量设置告警aliveWorkers 2触发短信application.status FINISHED但duration 300s触发邮件。第二坎日志集中管理Worker 日志分散在各节点故障时翻找效率极低。方案Filebeat 采集$SPARK_HOME/logs/下所有*.out和*.err发送到 ELK索引名spark-logs-{date}Kibana 中用process: Workermessage: OutOfMemory快速定位 GC 问题。第三坎配置即代码GitOps所有spark-defaults.conf、spark-env.sh、yarn-site.xml必须纳入 Git 仓库分支策略main