资讯动态

Spark Standalone集群搭建实战:从配置到踩坑全指南

发布时间:2026/10/13 13:23:28 来源:尧图企业网站定制
折腾完几套大数据环境之后,我必须说,Spark 的 standalone 集群模式是搭建成本最低、最不容易劝退新人的一种方案。它不需要额外装 YARN、不需要单独部署资源调度服务,Spark 自己就带着 Master 和 Worker 两套进程,解压、改配置、启动,三步走完就能跑起来。这篇文章我会把完整的搭建过程、每个配置项背后的含义、启动验证步骤和日常最容易踩的坑全部串一遍,给正在搭环境或者准备搭环境的你一个能直接照着操作的参考。如果你是第一次接触 Spark,对 Master、Worker、Executor 这些名词还比较模糊,这篇文章也会从最基础的部分讲起。放心往下读,我会用工程实践里真正用得到的细节说话,而不是只列一堆官网文档里已有的参数。1. 看懂 standalone 集群的核心组成1.1 Master 与 Worker:集群的大脑和手脚理解 standalone 集群,第一步是搞清楚进程角色。Spark 自带两套核心进程,一套叫 Master,一套叫 Worker。Master 是集群的调度大脑,负责接收你提交的任务请求,然后在集群里找空闲的 Worker,把计算任务分配下去。Worker 是真正干活的手脚,它启动在每台参与计算的机器上,负责向 Master 上报自己的资源情况,并且在本机拉起 Executor 进程来执行具体的计算逻辑。这里有个容易混淆的点:实际跑计算的不是 Worker 进程本身,而是 Worker 在自己机器上创建的 Executor。每个 Worker 可以启动多个 Executor,具体数量由你的 CPU 核数和内存配置决定。你可以把 Worker 理解成一间工厂,Executor 是工厂里的工人,Master 是接单和派单的调度室。用户提交一个应用,Master 就会通知各个 Worker 说这个活需要多少个工人、多大工地,Worker 就地召集相应数量的 Executor 开工。另一个容易忽略的角色是 Driver。它运行在你的提交端,相当于整个应用的总指挥。Driver 做好任务切分以后,把一个个 Task 分发给 Executor 执行,Executor 把结果汇报给 Driver 再汇总。所以 standalone 集群里的核心就是两个层面:集群资源层面是 Master-Worker,应用执行层面是 Driver-Executor。1.2 为什么优先选用 standalone 而不是 YARN很多刚接触 Spark 的人会纠结资源管理器选哪个。公司生产环境里 YARN 用得确实多,因为它能和 Hadoop 生态很好地共存,一套资源池同时调度 MapReduce 和 Spark。但如果你只是自己学习、做原型验证、跑中小规模的数据任务,standalone 的优势非常明显。第一,部署轻。YARN 需要依赖 HDFS 生态里的 NameNode、ResourceManager、NodeManager 一大堆组件,standalone 只需要 Spark 安装包解压就能用。第二,问题排查链路短。整个集群就 Master 和 Worker 两类进程,任务跑不动直接看两个日志就能定位,不会被多套组件的日志绕晕。第三,资源隔离足够用。我的经验是,几十个节点的场景下 standalone 配合好内存参数,稳定性完全够扛,不是说非得上 YARN 才叫专业。当然,如果你的场景是 Hadoop 全家桶已经在跑,或者需要严格的多租户资源隔离,那 YARN 模式确实是更合理的选择。选型这事没有绝对的对错,只有适不适合当前阶段。1.3 standalone 模式的工作流程说明我在环境搭建完成后,总会用一句话向团队新人解释整套流程:你把一个 Jar 包交给 spark-submit,submit 会启动 Driver,Driver 向 Master 申请 Executor 资源,Master 找到空闲的 Worker 并协调出资源,Worker 启动 Executor,Executor 和 Driver 建立连接开始通信,整个应用就这样跑起来了。注意一个细节:Task 分发是 Driver 直接和 Executor 交互的,Master 只在资源申请阶段起着中介作用。所以提交任务以后,即使 Master 进程出了问题,已经启动的计算任务通常还能继续跑完(除非有新的 Executor 需要申请)。理解这个机制,对你后面排查Master 挂了但任务还在跑这种奇怪现象很有帮助。2. 环境准备与前置依赖排查2.1 JDK 与 Scala 版本怎么选Spark 是跑在 JVM 上的,所以 JDK 是第一依赖。版本选择上我建议你用 JDK 8,虽然新版本 Spark 也支持更高版本,但生产实践里 JDK 8 的兼容性最稳。这里说的稳是指你以后要接 Hive、接各种第三方库的时候,JDK 8 几乎不会碰到版本太新库不认的情况。Scala 这块要注意一个常见的坑:很多人会忽略 Scala 版本匹配问题。Spark 安装包本身是预编译好的,你用 Scala 写代码或者用 Spark 自带的交互式 shell,其实不需要单独装 Scala。但如果你要用 Scala 编译自定义 Jar 包,那就要保证你本地的 Scala 版本和 Spark 预编译的 Scala 版本一致。怎么看?解压 Spark 之后看一眼安装目录下的 jars 目录,里面有 scala-library 等 Scala 相关的包,版本一目了然。我建议统一用 Spark 包内的 Scala 版本配套开发,避免踩二进制兼容的坑。2.2 机器规划与网络要求搭建集群之前,先把机器和网络理清楚。我见过不少环境起不来、Worker 连不上 Master 的情况,八成都是 hostname 或者 IP 配置的问题。准备工作分三步。第一步,确认每台机器的 hostname,分别修改成你计划里的节点名,比如 node01、node02、node03,避免用 localhost 这种无法区分的名称。第二步,修改每台机器的 /etc/hosts,把所有节点的 IP 和 hostname 对应关系都写进去,这样各节点之间可以通过主机名互相访问。第四步,测试节点之间是否连通,用 ping 命令逐对检查。这里有一个容易被忽略的点:每台机器的时间最好保持同步,尤其是任务提交端和计算节点之间。因为 Spark 的部分调度和时间戳逻辑对时间偏差比较敏感,时间差过大会出现一些莫名其妙的失败。我一般会加一个简单的定时同步任务,确保节点间时间误差在秒级以内。2.3 下载 Spark 发行包与目录规划下载 Spark 发行包时,注意选对版本和打包方式。官网下载页会给多种预编译版本,注意带 Hadoop 版本的标签,例如带 Hadoop 3.x 的版本。如果你的环境里没有 Hadoop 依赖,也请选择这种预编译包,这样 Spark 运行时可以自带 Hadoop 相关类,避免提交任务时报 ClassNotFoundException。下载解压之后,我会做一个统一的目录规划。生产环境里我习惯把 Spark 部署在 /usr/local/spark,用软链接指到具体版本目录,例如 /usr/local/spark-3.5.0-bin-hadoop3。这样以后升级版本,只需要换软链接指向新目录,不用改一堆脚本里的路径。目录里最关键的子目录有三个。conf 目录放所有配置文件,sbin 目录放启动和停止脚本,bin 目录放 spark-submit、spark-shell 这些用户命令。搭建集群的过程,核心就是改 conf 下的几个文件。3. 配置文件逐项拆解3.1 spark-env.sh 里的关键参数进入 Spark 安装目录的 conf 目录,你会看到 spark-env.sh.template 这个模板文件。第一步就是把它复制成 spark-env.sh,然后在里面填写环境变量。这个文件是集群模式最重要的配置文件,没有之一。cp spark-env.sh.template spark-env.sh打开 spark-env.sh,重点配置以下几项。JAVA_HOME 必须指定,因为 Spark 的启动脚本会显式读取它来定位 JDK。镜像管理节点主机的机器名通过 SPARK_MASTER_HOST 指定,默认端口是 7077,一般不需要改,但如果端口被占用可以换成别的。每台 Worker 能使用的内存和 CPU 核数由 SPARK_WORKER_MEMORY 和 SPARK_WORKER_CORES 控制。这里要特别提醒:SPARK_WORKER_MEMORY 是有 JVM 上限的,并不是想设多大就多大。在 64 位机器上,如果 Worker 内存超过 32G,我建议用 H 后缀标识(如 32g)并确认操作系统对大内存页的支持,普通场景下用 g 后缀就够了。数值设置要从机器实际物理内存出发,留出系统、其他进程的余量。比如机器总内存 16G,系统本身和辅助进程占掉约 2G,那 Worker 内存设 8G 左右比较安全,不要贪多。下面是一份实际生产环境里验证过的参考配置:export JAVA_HOME/usr/local/jdk1.8.0_202 export SPARK_MASTER_HOSTnode01 export SPARK_MASTER_PORT7077 export SPARK_WORKER_MEMORY8g export SPARK_WORKER_CORES4 export SPARK_WORKER_WEBUI_PORT80813.2 worker 节点列表怎么维护第二步是维护 Worker 节点列表。在 Spark 老版本里这个文件叫 slaves,新版本叫 workers,你只需要在 Spark 安装目录的 conf 目录下找到对应模板,复制出来编辑即可。cp workers.template workers文件内容非常简单,每行写一个主机名或 IP 地址。如果你只有一个节点,写一行就行;如果你有三个节点,就把三个节点名都写进去。node01 node02 node03这个文件的作用是给启动脚本用的。当你执行 start-all.sh 的时候,脚本会读取这个文件,然后通过 SSH 到每一台机器的对应地址,远程启动 Worker 进程。所以这里有两个隐性要求。第一,所有节点都需要把 Spark 安装包放到相同路径,否则脚本远程执行时会找不到文件。我是用 rsync 一键同步的,省事且不会漏文件。第二,提交端到各节点需要配置好免密登录,否则脚本会卡在密码交互那里,启动过程直接中断。你可能会问,为什么 workers 文件要放在每台机器的同一个路径?因为 start-all.sh 在 node01 上执行时,它会尝试通过 SSH 登录到 node02,然后执行那个路径下的 sbin/start-worker.sh。如果 node02 上路径不一致,脚本就会报找不到命令。这一点是新人最容易踩的坑,一定要提前检查。3.3 内存、CPU 参数如何结合实际设定配完 Worker 的资源参数以后,还有一个地方要注意:Spark 应用本身的内存分配。在 standalone 模式下,应用 Executor 的内存大小由提交参数 spark.executor.memory 控制,Worker 只是划定了一个资源池,具体每个任务能用多少,由提交任务时申请的参数决定。如果你在提交任务时不为 executor 内存和核心数做限制,Spark 会默认尽量占满 Worker 的全部可用资源。比如你 Worker 内存 8G,提交一个应用不设参数,它可能一下子就把 8G 全部申请完。另一个任务再提交就得排队。所以实际提交任务时,要清楚自己的应用需要多少资源,在 spark-submit 时用 --executor-memory 和 --executor-cores 显式指定。我在这里踩过一次很典型的坑:集群一共三个节点,每台 Worker 8G 内存,总共 24G。当时我提交一个任务没设内存参数,一个 Executor 就把一个 Worker 的 8G 全占满了,后面的任务全部卡在等待资源。后来学乖了,每次提交任务前都按数据量估算需要多大内存,并且留出至少 20% 的余量给系统缓冲,任务排队的情况基本绝迹。4. 启动集群与验证全过程4.1 首次启动与 Web UI 观察配置全部完成以后,启动集群就简单了。在 Master 节点上执行:sbin/start-all.sh这个脚本会做两件事。第一,在本机启动 Master 进程。第二,通过 SSH 连接 workers 文件里列出的每台机器,启动对应的 Worker 进程。执行完以后,用 jps 命令检查各节点上的进程。Master 节点上应该能看到 Master 进程,每个 Worker 节点上应该能看到 Worker 进程。接下来,打开 Web UI 验证集群状态。Master 的 Web UI 默认端口是 8080,浏览器访问 http://node01:8080 就能看到集群总览页面。这个页面会列出来当前注册的 Worker 节点、每个节点的内存和 CPU 资源、当前运行的应用列表。如果你在这个页面里看到所有 Worker 节点都显示 Alive,状态为正常,那就说明集群环境已经基本跑通了。每个 Worker 节点自己也带一个 Web UI,端口默认是 8081,地址是节点名加 8081 端口。可以直接访问单个 Worker 页面,确认该节点上的 Executor 状态和资源使用情况。另外,这些 Web UI 端口如果和你机器上其他服务有冲突,可以通过 spark-env.sh 里的参数做修改。4.2 用 spark-submit 提交测试任务集群启动后,建议拿一个最简单的任务验证端到端流程是否通畅。我通常用 Spark 自带的计算示例跑一次圆周率估算:bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master spark://node01:7077 \ --executor-memory 2g \ --executor-cores 2 \ examples/jars/spark-examples_2.12-3.5.0.jar \ 100这个命令里的每个参数都值得说明。--master 指定连接 Master 的地址,格式是 spark://主机名:端口,那段 grad 是任务的计算量大小,数字越大计算时间越长。--executor-memory 和 --executor-cores 分别限制每个 Executor 的内存和 CPU,避免把一个 Worker 的资源全部占满。类路径里的 spark-examples 包是 Spark 自带的示例程序集合,不需要额外下载。任务提交以后,你会看到一串日志输出,最终会出现类似 Pi is roughly 3.142... 的结果。看到这个输出,说明你的 standalone 集群从提交、调度、分配到执行,整个链路已经通了。此时再去 Master 的 Web UI 看一眼,你会看到这个任务的状态从 RUNNING 变成 FINISHED,并且页面上会记录它占用的 Executor 数量和运行耗时。4.3 集群停止与重启的正确姿势停止集群和启动一样简单,一条命令:sbin/stop-all.sh它会把 Master 和所有 Worker 全部停掉。但要注意一点,这个命令只会停掉集群层面的进程,不会管你正在跑的 Spark 应用。如果你有一个长任务正在跑,直接执行 stop-all.sh,Driver 会尝试申请新的 Executor 补不上资源,最终任务失败。所以稳妥的做法是,先观察任务列表,等任务跑完或者主动停止应用,再停集群。如果你的集群环境需要经常重启,我还建议做一个简单的启动检查清单:检查 /etc/hosts 里的主机映射是否完整,检查免密登录是否正常,检查 Spark 路径在各节点是否一致。这三项没问题,start-all.sh 基本不会失败。5. 常见问题与排查技巧实录5.1 worker 一直连不上 master这是搭集群阶段出现频率最高的问题。你执行 start-all.sh 后,Master 正常启动,但 Web UI 页面里看不到任何 Worker 注册。排查思路按顺序来,不要乱。先看 Worker 的日志,日志在 Spark 安装目录下的 logs 目录,文件名通常是 spark-用户名-org.apache.spark.deploy.worker.Worker-主机名.out。打开日志,确认最后几行的错误信息。最常见的现象是连接超时,比如 Connection refused 或 timed out。这种日志的背后通常是三个原因。第一,Master 的 7077 端口没有在监听,你可以用 netstat -tlnp | grep 7077 确认。第二,防火墙把端口拦了,需要检查节点之间的防火墙策略。第三,hostname 或 hosts 解析不对,Worker 拿到错误的 Master 地址。这种问题排查起来,用 telnet 直接测试一下 Master 主机的 7077 端口能不能通,能马上缩小范围。我自己的习惯是,每次搭新环境都先把防火墙、hosts、免密这三件事安排明白,再动 Spark。很多玄学问题,最后查出来都是这一层的锅。5.2 任务提交后卡在 ACCEPTED集群起来了,任务能看到,但状态一直停在 ACCEPTED,不进 RUNNING。这个我遇到过不止一次。表面现象是任务被 Master 接受了,但一直没分配到 Executor,页面上的 Workers 资源一直显示 Used 为 0。优先怀疑资源申请问题。检查你提交任务时设置的 executor 内存和核心数,是否大于 Worker 实际剩余的可用资源。如果 Worker 内存是 8G,你申请了 10G,那 Master 一直找不到能容纳你的 Worker,任务就只能无限等资源。这里有个调试技巧:提交任务时看一下 Web UI 页面右侧的任务列表,它会显示 Waiting 状态和具体原因,通常能直接看到类似 Current usage: 8.0 GB Memory / 10.0 GB Memory requested 这种信息。另一种可能出在资源被之前没退出的应用占着。有些应用因为网络问题或 Executor 没有正常释放,过了一段时间还是占用着资源。遇到这种情况,先把卡死的应用从 Web UI 页面上杀掉,再重新提交任务,基本就能进入 RUNNING 状态。这里我再补充一个小习惯:给任务取个有意义的名字,通过 --name 参数,这样在页面上一眼能看出哪个任务该清理,哪个还有用。5.3 端口、hostname 与内存相关的坑除了上面两个高频问题,还有一些平时不起眼、关键时刻很要命的细节。第一个坑是重复的 Worker ID。如果你复制了虚拟机的完整磁盘,两台机器上的 Worker 身份标识一样,启动后会互相踢下线。排查方法是在 spark-env.sh 里为每台机器指定不同的 SPARK_WORKER_OPTS,里面加上唯一的 Worker 编号,或者干脆卸载副本重新装。第二个坑是机器负载过高导致 Executor 崩溃。standalone 模式没有自动重启机制,Executor 因为内存溢出被杀掉,任务就失败了。建议在 spark-env.sh 里给 Worker 加上一个预防性参数,使得 Executor 在内存不足时能更从容处理。如果问题依旧,优先考虑缩小单 Executor 的内存申请,同时提升 Task 的并行度,让任务分得更细,而不是一味加大内存。第三个坑是 jps 能看见进程但 Web UI 打不开。这类问题基本都指向防火墙或端口绑定异常。用 ss -tlnp 确认 Spark 的 Web 端口是否绑定在机器所有网卡上,有些默认配置只绑定本机访问,外部访问不到。解决方式是在 spark-env.sh 里显式加 SPARK_MASTER_HOST 和 SPARK_LOCAL_IP,明确绑定地址,问题就解决了。写在最后的几点体会我搭过不少 Spark 环境,也帮别人排过不少集群问题。让我印象最深的一点是,大多数看似复杂的问题,最终都会回归到最基础的网络、路径和资源参数上。所以我在文章开头才强调,先把机器规划、hosts、免密、同步这些底子打好,后面搭建才会顺利。关于资源配置,我个人的建议是宁小勿大。很多初学者一上来就把 Worker 内存设成机器全部物理内存,结果系统卡死,Spark 应用也跟着出问题。给系统留点余量,给别的进程留条活路,Spark 本身并不需要在资源上这么抠。如果你打算把 standalone 集群用于小规模的内部分析平台,我推荐后续关注两件事。一是日志收集,把 Master 和 Worker 的日志统一采集起来,排查问题会轻松很多。二是提交脚本标准化,把 spark-submit 命令参数固定成一个模板,这样每个人提交任务时不会因为参数漏写导致资源浪费或排队。环境搭建只是开始,稳定的使用习惯才是集群长期健康运行的关键。希望这篇经验对你有所帮助,遇到问题多看看日志,多拆解底层逻辑,自然就能摸透它。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑