1. 集群搭建前的整体规划设计
1.1 硬件选型与节点规划
先说结论:别一上来就盯着官网推荐配置抄,那是跑POC用的,不是跑生产用的。我经历过一次血的教训——最开始用4台8核32G的机器搭了套小集群,跑几千行SQL没感觉,等数据量一上来,NameNode直接卡到假死,YARN上跑的作业全部超时,整个平台一夜之间变成了“不可用状态”。后来老老实实把硬件和节点角色重新梳理了一遍,才算稳下来。
分布式计算集群的核心并不在于单台机器有多强,而在于“角色分工”和“资源池化”这两件事。一个典型的Hadoop+Spark集群,角色大致分成两类:
- 管控节点(Master):负责元数据、调度、协调。这类节点要稳,CPU不用太夸张,但内存要大,磁盘要可靠。
- 计算节点(Worker):负责真正干活的存储和计算。这类节点要“多核大内存”,磁盘容量要足,网络带宽要够。
以一套10节点起步的生产环境为例,我常用的规划是这样的:
| 角色 | 节点数 | 硬件建议 | 部署组件 |
|---|---|---|---|
| 管控节点 | 3 | 16核 / 64G内存 / 2块300G SSD RAID1 | NameNode、ResourceManager、Zookeeper(3个节点平分) |
| 计算节点 | 5-7 | 32核 / 128G内存 / 4块4T SATA Raid0 | DataNode、NodeManager |
| 提交节点 | 1 | 8核 / 16G内存 | 网关、Spark客户端、Hive客户端 |
为什么管控节点要3个而不是1个?因为高可用。NameNode和ResourceManager都支持Active/Standby模式,加上Zookeeper做自动故障切换,最少就是3个节点(奇数个节点做选主,避免脑裂)。而计算节点初始只要5-7台,原因是太少了会出现“资源碎片”,毕竟我们跑Spark作业时,每个Executor都要固定占用几个CPU和内存,节点太少,任务一多就直接排队排到天荒地老。
还有两个常被忽略的点,一个是网卡,一个是交换机。节点间数据拷贝速度极大影响作业效率,我建议每台机器至少配双口万兆网卡,交换机要支持万兆转万兆或万兆转千兆的兼容模式,否则你会发现某些Stage特别慢,而上层Spark日志里根本查不出原因。
注意:生产环境别买“异构”机器,也就是不要每个节点配置不一样。异构集群的调度会让你头疼到怀疑人生——大节点吃不满,小节点拖后腿,整体利用率极难提上去。宁可一开始稍微少买几台同配的,也别贪多买一堆杂牌配置。
1.2 软件栈选型与版本匹配
集群搭建中最烦的不是安装,而是版本组合。我记得有一次在社区里看到有人问“为什么我的Spark能启动但提交作业就报ClassNotFound”,下面一堆人回复,最后发现是编译Spark时用的JDK版本和运行时JDK版本不一致,折腾三天才查清楚。所以我会强调:软件版本必须锁定,且要注意组合兼容性。
一套我目前在生产环境用得最顺手的版本组合(也是社区里比较推荐的)是这样的:
- 操作系统:CentOS 7.9 或 Rocky Linux 8.x(Linux内核3.10+,注意关闭透明大页)
- JDK:JDK 8u202 或 JDK 11(如果你用的Spark 3.x,建议JDK 8或11;Hadoop 3.x也完全兼容)
- Hadoop:3.3.x(建议用3.3.4+,带了很多稳定性修复)
- Spark:3.3.x 或 3.5.x(建议3.5.x,内存、SQL优化更强)
- Zookeeper:3.7.x
- 调度与存储:YARN + HDFS(Hadoop自带)
为什么不能随便换版本?因为Hadoop和Spark之间是“基于RPC通信”的,不同版本协议可能变化,你用Spark 3.5调Hadoop 2.7的接口,大概率会遇到各种“不兼容”报错。虽然Spark官方说“支持Hadoop 2.x/3.x”,但这个支持是有边界的。
另外,如果你准备用Hive、HBase、Flink这些组件,建议先看它们的Release Notes里是否明确列了“依赖的Hadoop版本区间”。比如Hive 3.1.3搭配Hadoop 3.3.x就没有太大问题,但Hive 2.x配Hadoop 3.x,需要额外打补丁。这些坑,网上都有,但最好在选型阶段就避开。
版本锁定后,所有节点的JDK配置必须一致,不要一台装OpenJDK7,另一台装Oracle JDK8。我习惯的做法是在每台机器上部署相同的 /usr/local/jdk8 路径,然后统一配置 /etc/profile 里的环境变量,保证集群里“哪里都用同一个Java”。
1.3 网络规划与安全基线
网络是集群的生命线,但偏偏是最容易被新手忽略的部分。HDFS写入一个文件时,数据流要跨节点传输三份副本,如果网卡不支持万兆,写大文件时IO就会成为瓶颈。在规划阶段要提前把以下内容定下来:
- 主机名规划:像
node01、node02、node03这种短横线命名,别用带中文或下划线的。同时把/etc/hosts里每个节点的主机名和IP对应关系写死,配置Hadoop时统一使用主机名而不是IP。 - 端口规划:HDFS默认8000/8020,YARN默认8030/8031/8032,Spark HistoryServer默认18080,Zookeeper默认2181。这些端口要提前确认没有被云安全组、防火墙拦截。在云环境(阿里云、腾讯云)上,安全组往往比本机防火墙更严格,别漏了。
- SSH免密登录:从提交节点到所有集群节点要配置免密,否则你启动/停止集群时会被反复要求输密码,操作起来非常痛苦。
- 时间同步:分布式集群最怕时间不一致,因为HDFS的租约、YARN的调度都依赖时间戳。建议部署NTP或Chrony服务,让所有节点和公司内网的时间源同步。
安全基线方面,生产环境一定要控制好权限。比如HDFS默认的权限校验比较宽松,supergroup 只有一个 hdfs 用户,但实际使用中你肯定有多个开发和运维人员要访问集群。建议基于操作系统用户做Kerberos或有简单方案用Apache Ranger,这里不逐一展开。至少要做到:没人能随便删根目录,没人能通过RPC直接操作HDFS上层文件。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心组件部署与配置
2.1 Hadoop HDFS 与 YARN 的关键配置
整个安装过程其实就是四个步骤:下载解压、修改配置文件、分发到所有节点、格式化元数据并启动进程。但如果只是跟着默认配置走,性能会非常平庸,所以我们要花更多时间在参数调优上。
先说 hdfs-site.xml。最核心的几个参数:
code复制dfs.replication=3
dfs.namenode.name.dir=/data/dfs/name
dfs.datanode.data.dir=/data/dfs/data1,/data/dfs/data2
dfs.namenode.handler.count=100
dfs.datanode.handler.count=30
副本数是3,这个不用多解释。dfs.namenode.name.dir 要放在RAID1或者云盘的可靠存储上,这是整个集群的“命根子”——一旦元数据文件损坏,所有文件都找不回来。dfs.datanode.data.dir 可以考虑配多个目录,每个目录对应一块独立磁盘,既能提高IO并发,也方便磁盘扩容。
dfs.namenode.handler.count 默认只有10,对于并发高的集群远远不够。我的经验是“100起步”,如果机器核数多、客户端数量大,可以调到300甚至更高。这个参数影响的是NameNode能同时处理的RPC请求数,类似于服务器的最大连接数,太小就会看到 java.net.SocketTimeoutException: 7500 millis timeout。
然后是 yarn-site.xml。这里重点配置的是资源分配:
code复制yarn.nodemanager.resource.memory-mb=98304
yarn.nodemanager.resource.cpu-vcores=24
yarn.scheduler.maximum-allocation-vcores=8
yarn.scheduler.maximum-allocation-mb=32768
yarn.nodemanager.vmem-check-enabled=false
第一行是每台计算节点可以被YARN分配的总内存。通常我建议留出节点物理内存的10%~15%给操作系统和系统进程,比如128G物理内存就设约110G,但如果你容器里还跑着其他服务,要适当调低。第二行是CPU核数,类似道理,也建议留出部分给OS和高性能IO。vmem-check-enabled 这个参数,生产环境建议关掉,因为Java堆外内存和操作系统虚拟内存统计方式在容器里经常出现误判,开着容易导致作业被误杀。
注意:这几个参数如果设得太大,会导致多个作业同时抢资源时互相拖垮;如果设得太小,又会浪费大机器的算力。所以先算物理内存总量,再减掉系统占用,最后设置一个“资源池最大值”。
2.2 Zookeeper 部署与高可用逻辑
Zookeeper在集群里承担的是“协调者”角色:保存NameNode Active/Standby状态、保存HBase元数据、做分布式锁。由于它本身是CP模型(强一致性优先),所以节点数必须配置为奇数个,推荐3个或5个。
Zookeeper的配置文件 zoo.cfg 关键项:
code复制tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/data
clientPort=2181
server.1=zk01:2888:3888
server.2=zk02:2888:3888
server.3=zk03:2888:3888
2888 和 3888 分别是集群内部通信端口和选举端口,记得在防火墙上放行。启动后,在各个Zookeeper节点的 dataDir 下需要创建一个 myid 文件,内容分别是1、2、3,用于标识自己的Server ID。
部署完Zookeeper,我们可以通过 zkServer.sh status 查看当前节点的角色(leader/follower),如果3个节点都显示follower,通常说明数据目录或myid配置有问题。
我在实际运维中发现,Zookeeper的日志量不容小觑,特别是元数据变更频繁时,zoo.out 会迅速膨胀。建议日常运维把Zookeeper的日志级别调高到WARN,同时做日志按大小滚动。
2.3 Spark On YARN 环境配置
Spark本身并不是一个必须要单独安装“集群软件”的框架,它更像是一个计算引擎,提交作业到YARN上面去跑。但我们依然需要把Spark客户端部署到网关机或提交节点上,并且配置好与HDFS、YARN的交互参数。
spark-defaults.conf 里我一般会配置这些:
code复制spark.master=yarn
spark.submit.deployMode=cluster
spark.eventLog.enabled=true
spark.eventLog.dir=hdfs://mycluster/spark-logs
spark.yarn.historyServer.address=node01:18080
spark.sql.adaptive.enabled=true
spark.sql.shuffle.partitions=200
spark.yarn.executor.memoryOverhead=4096
重点解释最后一行:spark.yarn.executor.memoryOverhead 是Executor运行时用于存放JVM堆外内存、内部数据结构、线程栈等部分的内存。如果你要跑pyspark,这个值尤其重要,因为Python进程的资源全在这块。默认值只有 max(384MB, 0.1 * executor.memory),对很多任务来说不够,遇到OOM别只盯着“还能调大executor.memory”这个方向,也要看看Overhead。
另外,建议开启 spark.sql.adaptive.enabled=true(Spark 3.2之后叫AQE)。它会根据运行时的数据量自动调整shuffle分区数,避免“分区开大了每个任务只有几MB数据”这样的浪费。这个开关是我从Spark 2.x迁移到Spark 3.x之后觉得收益最明显的一项。
2.4 启动流程与校验方法
配置完成后,启动顺序大概是这样的:先Zookeeper,再JournalNode和NameNode,再启动YARN的ResourceManager和NodeManager,最后在提交节点启动Spark HistoryServer。
第一次启动前,不要忘了在主NameNode节点执行一次 hdfs namenode -format,它会初始化元数据目录。这个操作只能执行一次,之后如果再执行,集群所有数据会“丢失”(从NameNode视角看)。
启动完可以先跑一个最基础的检查命令:
bash复制hdfs dfsadmin -report
看到所有DataNode都显示 In Service,并且容量统计正确,就说明HDFS层基本正常。然后到YARN的Web UI(默认端口8088)里查看NodeManager是否都在线。最后跑一个经典的wordcount,验证整个计算链路是否通畅:
bash复制hadoop jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.4.jar wordcount /tmp/input /tmp/output
如果wordcount能秒过,再submit一个SparkPi任务验证Spark作业链路:
bash复制spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster /opt/spark/examples/jars/spark-examples_2.12-3.3.2.jar 100
看到输出“Pi is roughly 3.1415”,说明这套集群从HDFS到YARN到Spark全通了。这一步,我建议所有新人务必走一遍,别直接就开始跑业务代码——如果连SparkPi都失败,业务SQL失败时你根本分不清是环境问题还是代码问题。
3. 资源调度、高可用与稳定性设计
3.1 YARN 调度器选型
YARN支持两种主流的调度器:Capacity Scheduler和Fair Scheduler。我们生产环境用得最多的是Capacity Scheduler(也是Hadoop默认的)。它的核心思想是“按队列占用百分比分配资源”,比如你建一个“数据开发队列”占60%,一个“数据平台队列”占40%,不同业务线之间互不抢资源,特别适合多团队共用一个集群的场景。
Capacity Scheduler的配置在 yarn-site.xml 里:
code复制yarn.resourcemanager.scheduler.class=org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler
yarn.scheduler.capacity.root.queues=default,dev,prod
yarn.scheduler.capacity.root.dev.capacity=30
yarn.scheduler.capacity.root.prod.capacity=60
yarn.scheduler.capacity.root.default.capacity=10
还要打开一个关键开关:
code复制yarn.scheduler.capacity.root.dev.accessible-node-labels=*
意思是这个队列的作业可以调度到所有节点上。如果你要搞GPU队列、SSD队列,就可以用 node-labels 给节点打标签,再绑定特定队列。不过这个功能有些复杂,初期不建议折腾。
排队的背后原理是:ResourceManager会根据每个节点的可用资源与任务申请的资源做匹配,选择最合适的节点启动容器。所以你的Executor内存如果设置得很大(比如20G一个),而每个NodeManager总资源只有100G,那么一个节点最多同时跑5个Executor,再多就直接排队。这个“资源碎片”问题可以通过调节 yarn.scheduler.maximum-allocation-mb 和Executor内存共同解决。
3.2 NameNode 高可用(QJM)配置
生产环境必须做NameNode HA,不然一旦NameNode所在的物理机宕机,整个HDFS就瘫痪了,所有依赖HDFS的计算任务都会失败。Active NameNode和Standby NameNode之间通过JournalNode同步EDIT日志。
配置要点:
- 在
hdfs-site.xml里配置dfs.nameservices为mycluster,然后分别为Active和Standby节点命名。 dfs.ha.namenodes.mycluster=nn1,nn2dfs.namenode.shared.edits.dir=qjournal://node01:8485;node02:8485;node03:8485/mycluster- 在
core-site.xml里配置fs.defaultFS=hdfs://mycluster
关键点是JournalNode必须是奇数个(通常是3个),因为它要向多数派写入成功才返回成功。JournalNode本身不需要很高的配置,但磁盘IO要稳定,建议放在SSD上。
Standby NameNode不仅做热备,还承担了定期合并镜像(Checkpoint)的任务,所以它的元数据目录也要用可靠存储。日常巡检时,要留意Standby节点的“镜像合并时间”,如果太久,说明它的CPU或IO负载可能过高。
3.3 元数据保护与备份策略
HDFS的元数据是集群最宝贵的资产。我见过最严重的一次事故,是管理员误删了NameNode的 fsimage 目录,整个集群从“健康”变成“空仓库”只用了不到一秒。所以元数据备份怎么强调都不过分。
可行的备份策略有:
- 定期下载fsimage和edits文件:用
hdfs dfsadmin -fetchImage或直接从NameNode的元数据目录复制到备份服务器。 - 开启Trash回收站:在
core-site.xml里设置fs.trash.interval=1440,这样删除文件后不是立刻物理删除,而是进入/user/xxx/.Trash,可以保留1天再清空。这项配置能挡住绝大多数的“手滑”。 - 使用快照:对关键目录(比如数据仓库目录)开启HDFS快照功能,定期给目录打快照,误删时可以快速回滚。
很多团队把注意力都放在“怎么调优性能”上,很少花时间做备份演练。等真出过事,你会明白“恢复演练”才是最重要的运维动作。我建议每季度做一次“模拟元数据丢失,从备份恢复”的演练,虽然可能耽误半天时间,但比事故发生后再救急强得多。
3.4 集群多租户隔离思路
随着公司业务增长,一个集群往往会有多个部门使用。这时如果不做资源隔离,就会经常出现“A部门跑大任务,B部门作业全部排队”的投诉。除了用Capacity Scheduler做队列隔离,我们还可以用HDFS目录权限做数据隔离,用YARN队列做资源隔离,用Ranger(或Sentry)做权限隔离。
我习惯的目录规范是:
code复制/user/wh/<部门>/<业务线>/
给每个部门创建独立的HDFS目录和Linux用户,并把这些用户对应的YARN队列关系绑定好。这样既方便做数据权限控制,也方便日常排查“这个作业是谁提交的”。如果团队规模不大,也可以暂时只做YARN队列隔离,等规模上来再叠Ranger。
4. 性能调优与常见问题排查
4.1 HDFS、YARN、Spark 三层调优路线图
分布式集群的性能瓶颈往往不在一层,而是多层叠加。我这里给出一套“自底向上”的调优顺序:
第一层:HDFS调优
- 调整
dfs.replication,如果数据重要性不高或底层是RAID,可以降到2,能省不少存储空间,但要注意节点不足时数据安全性下降。 - 调整
dfs.blocksize,默认128MB,如果是大文件批处理场景,可以调到256MB,减少NameNode元数据条目数,提升读写大文件时的性能。 - 增加DataNode的并发IO线程数:
dfs.datanode.handler.count,从默认10调到30,能缓解大量小文件写入时的RPC阻塞。 - 启用短路读(short-circuit read):如果计算和存储混布,DataNode本地读文件时可以直接读取本地磁盘,不需要网络传输。在
hdfs-site.xml里设置dfs.client.read.shortcircuit=true并确保libhadoop.so可用。
第二层:YARN调优
- 调大Container的“最小分配粒度”(
yarn.scheduler.minimum-allocation-mb)为512或1024MB,默认是128MB,对大规模作业来说,调度粒度太小会增加调度器压力。 - 调整
yarn.nodemanager.pmem-check-enabled=false和yarn.nodemanager.vmem-check-enabled=false,关闭物理内存和虚拟内存的强校验,减少误杀。 - 设置合理的
yarn.app.mapreduce.am.resource.mb,确保ApplicationMaster不会被OOM杀掉。
第三层:Spark调优
- Executor内存:一般建议堆内存4G~16G之间,不要超过16G。超过16G后,JVM的GC停顿会变得明显,不一定更快。每个Executor最好使用2-4个核心,这样既能并发处理多个分区,又不会因为太多线程竞争CPU。
- Shuffle分区数:如果通过AQE已经自动调整,就不必手动设置太多。如果手动设置,参考“每个分区128MB-512MB数据”这一原则推算。
- 序列化方式:可以使用Kryo序列化器(
spark.serializer=org.apache.spark.serializer.KryoSerializer),在大多数场景下比Java序列化快很多、占用内存少。但要注意,使用Kryo后,自定义类需要注册。 - 动态资源分配:如果集群并发量大、负载波动明显,开启
spark.dynamicAllocation.enabled=true,可以让Spark根据作业负载动态申请和释放Executor,对集群整体利用率有很大提升。
4.2 常见故障排查清单
我在运维过程中整理了这份速查表,基本能覆盖90%的初级问题:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 作业反复失败,报Container killed | 内存超用(真实OOM或Overhead配置不足) | 查看YARN日志,调大Executor内存或Overhead |
| 写HDFS很慢,但机器负载不高 | 小文件过多或网卡带宽瓶颈 | 检查HDFS目录文件数,查看网卡速率(iftop等工具) |
| 连接NameNode超时 | 网络不通或NameNode负载过高 | ping测试,看NameNode的JVM GC日志和RPC队列 |
| Spark任务出现大量Shuffle FetchFailed | 数据倾斜或节点不稳定 | 开启AQE,调整并行度,检查节点网络和磁盘 |
| Zookeeper频繁出现Session expired | 网络抖动或ZK节点负载过高 | 增大 tickTime,检查多节点网络延迟 |
| 运行SQL结果不准或缺失数据 | 代码逻辑或分区裁剪问题 | 先用 explain 看执行计划,检查HDFS数据文件是否损坏 |
排查的思路是“先底层后上层,先资源后代码”。遇到任务失败,第一件事是去YARN的日志页面看具体报错,而不是改代码重跑。YARN日志会记录Container退出码和异常堆栈,90%的问题在那里都能找到。
4.3 数据倾斜的诊断与处理
数据倾斜是Spark和Hive任务中遇到最多、也最容易让人头疼的问题。典型症状是:大部分Task几秒跑完,就有一两个Task跑几十分钟甚至失败;或某个Task处理的数据量明显比其他大。
诊断方法很简单:
sql复制select key, count(*) from table group by key order by 2 desc limit 10;
如果发现某个key的count远大于其他key,这个key就是数据倾斜的源头。
处理思路主要有几种:
- 两阶段聚合(局部聚合+全局聚合):给key加随机前缀,先做一轮聚合,再去掉前缀做第二轮聚合。比如
concat(key, '_', rand()*10)打散后分组聚合,再把结果还原。适用于count、sum这类聚合函数。 - Filter+Union:把倾斜key单独过滤出来,走单独处理逻辑,不倾斜的部分走原逻辑,最后union。适用于join场景。
- 广播小表:如果大表Join小表,把小表用
broadcast join广播到每个Executor,避免shuffle,从根上避免倾斜。
在Spark 3.x中,AQE还会自动把“倾斜分区”拆分(spark.sql.adaptive.skewJoin.enabled=true),可以在你什么都没做的情况下帮你处理一部分倾斜,但业务侧的优化依然不可替代。
4.4 监控、日志与日常巡检
集群部署好,并不是结束,而是运维的开始。我建议至少部署一套监控系统,把以下指标全部纳入:
- HDFS容量趋势:剩余空间是否逼近阈值(建议低于20%就要告警)。
- DataNode节点存活与磁盘IO:关注节点是否频繁掉线,磁盘是否出现坏道。
- YARN资源使用率:队列占用是否长期处于高位,作业排队时长是否异常。
- NameNode RPC延迟和GC耗时:RPC延迟超过几秒说明NameNode可能成为瓶颈。
开源方案里,Grafana+Prometheus的组合是目前最主流的。Hadoop、Spark都有现成的Exporter可以采集指标,半小时就能把基础监控搭起来。
日志层面,NameNode和ResourceManager的日志要重点关注 ERROR 级别日志,DataNode的日志主要看是否频繁上报“Bad Block”。另外,建议开启HDFS的 dfs.audit.logger,记录所有文件操作审计日志,方便后期排查“哪些用户删过哪些文件”。
日常巡检,我建议安排一个“每周清单”:
hdfs dfsadmin -report检查节点状态和容量。- 查看ResourceManager UI中是否有长时间PENDING的应用。
- 在Zookeeper里执行
stat查看各节点状态和请求延迟。 - 检查系统日志里是否有硬件水位线超标(如CPU温度、磁盘预测故障)。
这些操作看似琐碎,但能把90%的隐患消灭在萌芽期。真等集群“病发”,往往是好几个问题同时爆发,到时候排查起来就是地狱难度了。
5. 集群部署策略、扩展与面试高频点
5.1 从“测试集群”到“生产集群”的演进路径
很多团队一上来就想搭“大而全”的生产集群,结果搭到一半发现网络规划错了、资源不足、组件版本冲突,只好推倒重来。我的建议是分三步走:
- 第一步:测试集群(3节点):用3台虚拟机或云主机,装上Hadoop、Spark、Zookeeper,目的是验证版本兼容性、跑通核心流程。此时不搞高可用,只验证功能和性能可行性。
- 第二步:准生产集群(8-10节点):加装NameNode HA、YARN队列、Spark HistoryServer等。重点是把日志、监控、备份等运维体系建立起来。
- 第三步:生产集群(10+节点):按业务需求扩容计算节点,绑定账号和权限系统,接入数据同步任务(如Canal、Sqoop),细化资源隔离和告警阈值。
这个路径能让你每一步都有明确的目标和验收标准,不会一上来就被复杂性击垮。
5.2 K8s 部署 vs 传统裸机部署
最近几年,很流行把大数据组件容器化之后部署到Kubernetes上。很多热搜词都指向“k8s集群搭建”。如果你问我的建议,我的态度是:看场景和团队能力。
Kubernetes的好处很明显:弹性伸缩、统一运维、快速部署。尤其适合那种“使用量波动大、需要频繁扩容缩容”的场景。但Hadoop和Spark这类“有状态、重型IO”的应用,在K8s上跑需要额外处理很多问题,比如数据本地性、持久化卷(PVC)、节点亲和性、共享内存等。一个没有专门K8s运维团队的团队,贸然上K8s会踩很多坑。
我建议的折中方案是:**计算层使用K8s编排Spark作业(Spark On K8s),存储层仍然使用独立的HDFS集群。**这样既能享受K8s的弹性,又不会因为存储层容器化而带来数据可靠性风险。如果你对K8s不熟,传统裸机部署Hadoop仍然是“最稳妥、最不容易被领导半夜叫醒”的选择。
5.3 大数据面试中集群搭建类问题的高频考点
如果你正在找大数据开发或运维方向的工作,集群搭建这块是面试官必问的内容。我整理一下高频考点:
- HDFS写入流程:客户端请求NameNode获取可用DataNode列表,将数据分成块,按流水线顺序写入三个DataNode,最后一个DataNode返回ACK。面试时要把“副本放置策略(第一副本同节点或同机架,第二副本同机架不同节点,第三副本不同机架)”讲清楚。
- YARN任务提交流程:客户端提交Application到ResourceManager,RM启动ApplicationMaster,AM向RM申请资源,然后在NodeManager上启动Container。面试官一般会让你说“AM崩溃了怎么办”,回答要点是YARN会重试AM。
- Spark作业执行模型:从RDD DAG到Stage划分,再到Task调度。重点是宽依赖和窄依赖的区别、pipeline的计算方式、shuffle的具体过程。
- NameNode宕机了怎么恢复:这是最常见的运维题。先看是否有HA(自动切换),没有HA就需要用Standby NameNode镜像或fsimage备份恢复。如果有JournalNode,可以把NameNode切换到Standby节点。
- 数据倾斜的解决方案:见上文4.3节,面试时不仅要说出方案,还要说出“为什么两阶段聚合能解决倾斜”背后的原理(局部聚合减少shuffle数据量,全局聚合得到最终结果)。
如果你能把上面这几点“真刀真枪”地在自己的集群上实操过,面试时可以讲出具体的报错和排查过程,面试官会觉得你是真做过,而不是背书。
5.4 集群扩容与平滑升级经验
最后聊聊扩容和升级。当集群资源不够用,需要加新节点时,步骤其实并不复杂:
- 在新机器上安装相同版本的JDK、Hadoop、Spark客户端。
- 把主节点的配置目录同步到新机器,确保
core-site.xml、hdfs-site.xml、yarn-site.xml一致。 - 启动新节点上的DataNode和NodeManager。
- 在NameNode Web UI确认新节点状态正常。
升级则是另一个故事。最稳妥的方式是“滚动升级”:一次只停一个节点,升级它,再启动它,确认健康后再动下一个节点。但HDFS和YARN有些配置变更不支持滚动完成,需要整个集群短暂停服。我建议在升级前做一次完整的数据和配置备份,并且在测试集群上先演练一遍升级流程,再在生产操作。
版本升级最好“跨小版本,守大版本”——比如从Hadoop 3.3.4升到3.3.6,问题不大;但从Hadoop 2.x升到3.x,建议当成一个新集群项目来做,数据迁移、客户端兼容性、配置重写一样都少不了。
我在实际使用中最深的一点体会是:集群搭建这件事,50%的精力都在设计和规划上,30%在参数调优,只有20%在敲命令本身。很多人装了一晚上配置,最后跑不了数,大概率是设计阶段出了问题。所以,如果你正准备搭建自己的分布式计算集群,建议先把规划和选型想清楚,再动手敲命令,你的夜晚会平静很多。
