
在大数据领域摸爬滚打这几年最常被问的一句话就是“我要搭一套分布式计算的集群该从哪下手”问这话的人一部分刚看完官方文档WordCount 在伪分布式里刚跑通另一部分干脆已经买好了三台服务器但系统都没装还有一小部分更惨——在虚拟机里把“集群”搭了三遍一问才发现三台机器上跑的是三个互相独立的伪分布式。我可以把这类人的下一步踩的坑提前预演出来不是把伪分布式重复装了三遍就是在 HA 配置上纠结两天或者被 YARN 的内存参数搞到任务连环 OOM。这篇就是给这些同学的一份完整实操参考覆盖 Hadoop 3.x 集群、Spark on YARN、Flink on YARN 以及 K8s 部署策略尽量把参数和架构选择背后的推理过程也写出来而不是丢给你一份“照着抄”的配置清单。1. 建集群前绕不开的四个灵魂拷问1.1 你的数据规模配得上几台机器很多人一开口就是“我要搭一个大数据集群”但问他“准备跑多大体量的数据”回答往往是“不知道先搭起来再说”。这是最大的隐患——集群规模是设计出来的不是拍脑袋拍出来的也不是“越多越好”堆出来的。我自己习惯用一套很简单的方法做粗算。假设你的业务每天新增 2TB 原始日志保留 30 天做分析那原始数据就是 60TB。接着乘两个系数压缩比和副本数。日志类数据用 Snappy 或 Zstandard 压缩压到原来的 1/3 很常见也就是 20TBHDFS 默认三副本实际占用的物理空间就是 60TB。算完之后再看单节点可用存储比如一台机器满配 12TB 硬盘格式化、操作系统、预留缓冲之后可用空间大约 80%也就是 9.6TB。60TB 除以 9.6TB至少需要 7 个数据节点。再补一个约束数据节点太少三副本的性能优势根本发挥不出来所以低于 3 个 DataNode 的“完全分布式”更多是学习用途。如果是测试环境规模按生产环境的 1/10 到 1/20 砍都行但角色划分必须和生产保持一致否则后面迁移到生产时还得重新踩一遍配置的坑。1.2 伪分布式、完全分布式和高可用到底差在哪这三者之间的区别是我面试里必问的基础题也是很多人最初级的概念混淆点。伪分布式一台机器上同时起 NameNode、DataNode、ResourceManager、NodeManager所有进程都在一个 JVM 里或几个 JVM 里跑。它只是为了让你体验“配置文件生效”和“提交任务走流程”不代表任何分布式能力。完全分布式至少 3 台机器不同角色分散到不同节点。此时 HDFS 的数据块才真正做到了跨节点存储但 NameNode 仍然只有一个它挂了整个文件系统就不可用。高可用HA在完全分布式基础上部署 Active/Standby 两个 NameNode配合 ZooKeeper 做自动故障转移同时引入 JournalNode 同步元数据。这是生产环境的最低底线。从完全分布式到 HA不是简单“多配一个 NameNode”而已。涉及dfs.nameservices、dfs.ha.namenodes、dfs.namenode.shared.edits.dir这些配置项还要规划 JournalNode 的部署位置。很多教程带你把 HA 搭起来了但从来没说清楚 JournalNode 为什么要奇数台——其实是为了满足基于多数派投票的一致性协议3 台里挂 1 台还能继续服务挂 2 台就脑裂风险极高了。1.3 硬件选型为什么堆 CPU 不如堆内存大多数初学者选机器时眼光全盯在 CPU 核数和 SSD 上反而忽视了内存带宽。做分布式计算的集群内存的重要性往往高于 CPU。以 Spark 为例一个 Executor 的 JVM 堆内内存加上堆外内存动辄就是 4GB 起步跑 Join 类的 Shuffle 操作时内存不足会直接导致磁盘溢写性能断崖式下降。这些开销不是靠 CPU 主频能补回来的。业界比较均衡的配比大约是每 2 个物理核搭配 8GB16GB 内存。如果是 YARN 节点单机 64GB 内存配 16 核是比较舒服的起点因为在 YARN 里还要给操作系统和外围服务留 10%20% 的余量。磁盘方面我更推荐“多块 HDD 一块 SSD 做系统盘”的方案而不是全上 SSD。HDFS 顺序读写为主机械硬盘在顺序场景下性价比很高SSD 留给 NameNode 的元数据目录、ZooKeeper 的事务日志这类高随机 IO 场景收益更明显。1.4 先画一张部署拓扑图再动手动手装系统之前我强烈建议先画一张角色分配表。下面是我给一个典型 10 节点学习/测试集群做的分配可以直接参考节点角色说明hadoop01NameNode(Active), ResourceManager, ZooKeeper, JournalNode, Flink JobManager主控节点hadoop02NameNode(Standby), ZooKeeper, JournalNode备主控节点hadoop03ZooKeeper, JournalNode协调节点hadoop04~hadoop10DataNode, NodeManager, Flink TaskManager数据与计算节点注意一个原则NameNode、ResourceManager、Flink JobManager 这类“领导角色”尽量不要和 DataNode 混部在同一个节点上。混部短期看省机器长期看问题很多——数据节点 IO 占满时NameNode 的 RPC 响应会被拖慢ResourceManager 在做资源调度时也会受到其他进程干扰。2. 底线工程操作系统、JDK 与网络的三件套配置2.1 系统版本和 JDK 版本的一个稳定组合我见过太多人在集群没跑起来之前先在装哪套系统、选哪个 JDK 版本上吵了半天。其实这里根本没有最优解只有“已验证过”的组合。到目前为止我遇到最稳的组合还是 CentOS 7.x / Rocky Linux 8.x 配 OpenJDK 8。Hadoop 3.x 官方文档明确支持 Java 8 和 Java 11Spark 3.x 也完全兼容 Java 8。虽然 Java 17 已经是很成熟的版本但在一些老的 Hive 版本、Hadoop 生态组件里依然会碰到反射权限或者模块化限制的问题。在生产环境里团队如果已经统一用容器镜像管理运行时那 JDK 版本可以更大胆一些但如果你是在裸机上手工搭建听我一句劝用 OpenJDK 8省下的时间够你多排查好几个问题。系统层面需要关掉的两个东西防火墙和 SELinux。在大数据集群的内部网络里NameNode 的 9870 端口、DataNode 的 9864 端口、ResourceManager 的 8088 端口彼此都要互通如果防火墙策略没有完全对齐你会看到“节点明明活着但集群里看不到它”这种玄学问题。测试环境直接关掉生产环境则需要把端口清单拉精准。2.2 SSH 免密和主机名映射的细节搭建 Hadoop 集群时SSH 免密登录是必须配置的否则你无法用start-dfs.sh一键启动所有节点上的进程。具体操作很简单三条命令# 在 hadoop01 上生成密钥对 ssh-keygen -t rsa -b 4096 -P -f ~/.ssh/id_rsa # 把公钥分发到所有节点 ssh-copy-id hadoop01 ssh-copy-id hadoop02 ssh-copy-id hadoop03 # ... 其余节点同理这里有一个几乎所有教程都不提的坑必须同时配置 hosts 文件并且集群里的每台机器都要保持一致。如果你在每台机器上用的是不同 IP 映射会出现很诡异的现象——用ssh hadoop02连过去没问题但 Hadoop 内部进程互相注册时用的是 IP最后你在 NameNode Web 界面看到的 DataNode 列表里全是裸 IP排查问题时极为痛苦。我的习惯是把主机名映射写在/etc/hosts里同时修改/etc/hostname然后在每台机器上hostnamectl set-hostname hadoop0X。别偷懒用 IP 直连后面的日志排查会告诉你为什么主机名这么重要。还有一个细节集群时间同步。业务日志的时间戳、HDFS 的文件时间、YARN 的容器启动时间跨节点不一致时会产生各种“看起来没毛病但就是对不上”的问题。生产环境用 NTP 或 chrony 指向统一时间源测试环境至少保证手动ntpdate校准一次。2.3 时钟同步与 swap 关闭为什么是隐性雷区系统默认开启的 swap 对 Hadoop/Spark 这类内存敏感型组件危害极大。以 Spark Executor 为例JVM 堆内存被换到磁盘上之后Full GC 的时间会从毫秒级飙升到秒级任务超时、心跳丢失接踵而至。更坑的是这类故障在监控里很难一眼定位因为 CPU 和内存曲线看起来都没有异常。关闭 swap 的方法# 立即关闭 swapoff -a # 永久关闭注释掉 /etc/fstab 中 swap 对应的行 # 然后重启验证关闭之后还要在/etc/sysctl.conf里调整两个内核参数# 尽量不触发 swap vm.swappiness 1 # 文件句柄和线程数上限调高 fs.file-max 6815744 net.core.somaxconn 32768这些参数调整完不重启不会立即生效可以执行sysctl -p重载然后ulimit -n确认一下。这些基础工作做完集群的“地基”才算是稳了。3. Hadoop 3.x 集群搭建一步步把 HDFS 和 YARN 跑起来3.1 三张核心配置文件的每一项解释下载 Hadoop 3.3.x 版本后解压到/opt/hadoop配置的核心就是etc/hadoop目录下的四类文件core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml。我先说前三个。core-site.xml里最关键的是fs.defaultFS它决定了整个集群对外暴露的 HDFS 入口地址configuration property namefs.defaultFS/name valuehdfs://hadoop01:9000/value /property property namehadoop.tmp.dir/name value/data/hadoop/tmp/value /property /configuration注意hadoop.tmp.dir这个参数很多人的 NameNode 起不来就是因为默认的/tmp/hadoop-${user}在系统重启后被动过元数据丢了。自定义一个独立的目录并且保证它有足够的磁盘空间这是血的教训换来的经验。hdfs-site.xml中最重要的三块NameNode 元数据目录、DataNode 数据目录、副本数configuration property namedfs.namenode.name.dir/name value/data/hdfs/name/value /property property namedfs.datanode.data.dir/name value/data/hdfs/data1,/data/hdfs/data2/value /property property namedfs.replication/name value3/value /property property namedfs.namenode.secondary.http-address/name valuehadoop02:9868/value /property /configuration如果 DataNode 上有多个磁盘目录就用逗号分隔写在dfs.datanode.data.dir里HDFS 会自动做负载均衡。这里有个细节不同目录的磁盘容量不一致时HDFS 不会做智能加权分配所以最好保证各目录所在磁盘容量接近否则写满小盘之后整块磁盘上的副本都会变成Under-Replicated。yarn-site.xml是后续跑 Spark/Flink 任务时的核心资源池配置我给一份相对保守的初始值configuration property nameyarn.resourcemanager.hostname/name valuehadoop01/value /property property nameyarn.nodemanager.resource.memory-mb/name value49152/value /property property nameyarn.nodemanager.resource.cpu-vcores/name value16/value /property property nameyarn.scheduler.minimum-allocation-mb/name value1024/value /property property nameyarn.scheduler.maximum-allocation-mb/name value8192/value /property /configuration如果单机是 64GB 内存yarn.nodemanager.resource.memory-mb不要写满 64GB要留给操作系统和 HDFS 数据节点进程。我按 75% 的比例折算设成 49152MB也就是说 YARN 最多能从这个节点上分配出去 48GB 内存。3.2 格式化 NameNode 的正确时机和禁忌Hadoop 集群第一次启动之前必须在 NameNode 上执行格式化操作hdfs namenode -format这一步会清空并重新生成dfs.namenode.name.dir目录下的元数据。问题在于很多新手在集群出故障后会条件反射般地执行namenode -format结果把整个文件系统的元数据全部清空而 DataNode 上还保留着旧的数据块。这样一来NameNode 里没有任何数据块的映射关系所有数据等于丢失。所以必须记住一条铁律格式化操作只能在集群从未初始化过、或者你明确要“抛弃所有旧数据”的时候执行。生产环境绝不允许在运行中的集群上随手-format。如果集群真的出了元数据问题优先考虑从dfs.namenode.name.dir中的current目录找回元数据或者从 SecondaryNameNode/JournalNode 里恢复。3.3 一键启动脚本与手工启动的取舍配置好workers文件Hadoop 3.x 里已经从slaves改名为workers把 hadoop04~hadoop10 的节点名写进去然后在 hadoop01 上执行start-dfs.sh start-yarn.sh如果你的 SSH 免密配置没问题这两个脚本会自动把 DataNode 和 NodeManager 进程拉到所有workers节点上。这时候可以用jps命令在每台机器上检查进程hadoop01 上应该看到 NameNode、ResourceManager、SecondaryNameNodehadoop02 上应该是 DataNode、NodeManagerhadoop03~hadoop10 上都是 DataNode、NodeManager我个人的习惯是优先用脚本启动进程挂掉排查时再手工拉起单个进程这样能更快定位是配置问题还是环境问题。手工启动 DataNode 的命令是hdfs datanode启动 NodeManager 是yarn nodemanager注意要在对应节点上执行。3.4 启动后第一件事用网页和命令行双重验证启动完成后先不要急着提交任务用两个方式确认集群健康。网页端HDFS 管理界面http://hadoop01:9870进去后看 Live Nodes 数量是否等于 DataNode 数量再看 HDFS 剩余空间。YARN 资源管理界面http://hadoop01:8088看 Active Nodes 数量以及每个节点的可用内存和核数。命令行端hdfs dfsadmin -report hdfs dfs -mkdir -p /tmp hdfs dfs -put /opt/hadoop/etc/hadoop/core-site.xml /tmp/ hdfs dfs -cat /tmp/core-site.xml | head -20如果dfsadmin -report能正常输出各 DataNode 的容量信息说明 HDFS 数据链路没问题。再用yarn node -list确认 NodeManager 都正常向 ResourceManager 注册了。到这里一套完全分布式的 Hadoop 集群就基本跑通了。4. Spark on YARN让计算资源真正被管起来4.1 为什么生产环境不推荐 Standalone 模式Spark 自带的 Standalone 模式非常容易上手sbin/start-master.sh和sbin/start-worker.sh一启动就完事。但在生产环境里我几乎不会推荐它作为主力部署方式原因有三点资源管理能力太弱。Standalone 没有队列概念也没有多租户隔离几个人同时提交任务时资源就是先到先得没法按业务重要程度动态分配。与 HDFS 的节点协调性差。YARN 是 Hadoop 生态统一的资源调度层NodeManager 与 DataNode 同节点部署可以实现数据本地性调度Standalone 里的 Worker 和 HDFS 节点之间没有这种内建感知。运维体系割裂。如果 YARN 已经在管理一批任务Spark 再单独起一套资源调度体系监控、告警、队列配额都要两套配置完全没有必要。所以更常见的做法是把 Spark 接入 YARN让 YARN 统一管理所有计算框架的资源。你只需要在spark-env.sh里把HADOOP_CONF_DIR指向 Hadoop 配置目录Spark 就能自动从 YARN 申请资源了。4.2 spark-submit 提交任务的参数推导过程Spark 提交任务时最让人头疼的是 Executor 数量和内存怎么定。我以一台 YARN 节点 48GB 可用内存、16 核为例演示一遍推导过程。假设每个 Executor 分配 4GB 内存、2 核。结合 YARN 的 overhead 机制实际每个 Executor 在 YARN 里占用的内存要加上spark.executor.memoryOverhead默认是 executor 内存的 10%所以实际占用大约是 4.4GB。一个节点可以容下的 Executor 数为48 / 4.4 ≈ 10但 YARN 还要给 ApplicationMaster 留资源实际一个节点放 34 个 Executor 比较合理留足余量。如果集群有 7 个数据节点每个节点放 3 个 Executor总共 21 个 Executor对应的提交命令大致是spark-submit \ --master yarn \ --deploy-mode cluster \ --driver-memory 2g \ --executor-memory 4g \ --executor-cores 2 \ --num-executors 18 \ --queue prod \ --conf spark.executor.memoryOverhead512m \ --class com.example.Main \ /opt/apps/my-job.jar这里--num-executors我没有写满 21因为要留出一个 Executor 的余量给任务重试和系统波动。如果你首次跑一个任务按这个参数能跑通再逐步调大executor-memory观察 GC 时间而不是一开始就贪多。4.3 动态资源分配与队列隔离Spark 支持动态资源分配开启之后 Executor 会随任务负载自动伸缩对多团队共享集群特别有用。在提交命令或spark-defaults.conf中加spark.dynamicAllocation.enabled true spark.dynamicAllocation.minExecutors 1 spark.dynamicAllocation.maxExecutors 30 spark.shuffle.service.enabled true注意spark.shuffle.service.enabled必须为true否则 Executor 动态缩容时已经写出的 Shuffle 数据可能无法被后续任务读取。这个 External Shuffle Service 要在每个 NodeManager 上单独启动并把spark_shuffle相关 jar 放到 YARN 的 classpath 里。资源队列隔离这块生产环境会配置多个 YARN 队列。比如给实时数仓团队一个realtime队列给离线任务一个batch队列两者互不挤占。配置在capacity-scheduler.xml里核心参数就是各队列的容量上限property nameyarn.scheduler.capacity.root.queues/name valuebatch,realtime/value /property property nameyarn.scheduler.capacity.root.batch.capacity/name value60/value /property property nameyarn.scheduler.capacity.root.realtime.capacity/name value40/value /property调完需要执行yarn rmadmin -refreshQueues刷新队列配置不用重启 YARN。5. Flink on YARN 与状态后端流计算集群的另一种搭法5.1 Session、Per-Job、Application 三种模式怎么选Flink 跑在 YARN 上有三种部署模式很多人一开始分不清。Session Mode会话模式先在 YARN 上启动一个常驻 Flink 集群后续任务都提交到这个集群上。优点是启动快资源和集群启动开销可以摊薄到多个任务缺点是任务之间共享资源一个任务出问题可能影响整个会话。Per-Job Mode单任务模式每个作业独占一个 Flink 集群资源隔离更干净作业结束集群也释放。但每次提交都要经历一次完整的集群启动过程对短任务很不友好。Application Mode应用模式是 Per-Job 的进化版它会把用户代码的main方法放到 JobManager 里执行避免客户端环境差异带来的问题。Flink 官方目前最推荐的就是这个模式。在实际生产里我用得最多的是 Application Mode。提交命令可以写成这样flink run-application \ -t yarn-application \ -Djobmanager.memory.process.size2048m \ -Dtaskmanager.memory.process.size4096m \ -Dtaskmanager.numberOfTaskSlots4 \ -Dparallelism.default8 \ -Dyarn.application.queuerealtime \ -c com.example.StreamJob \ /opt/apps/flink-job.jar如果只是临时跑一个测试作业用 Session Mode 会更快但要记得及时关掉空闲会话不然它一直占着 YARN 资源不释放。5.2 RocksDB 状态后端与 Checkpoint 目录规划Flink 和 Spark 的流处理最本质的区别之一是 Flink 有状态计算。状态后端的选择直接决定了状态存储的容量和性能。如果状态量很小几 GB 以内用 HashMapStateBackend 放在堆内存里性能最好。但生产环境的实时指标计算、窗口聚合、多流 join状态量动辄几十 GB 甚至上百 GB堆内存根本放不下。这时候用 RocksDBStateBackend 是更现实的选择它把热数据放在内存、冷数据落盘能支撑大规模状态。RocksDB 的配置要点是两个参数一个是状态后端类型一个是 Checkpoint 存储目录state.backend: rocksdb state.checkpoints.dir: hdfs://hadoop01:9000/flink/checkpoints execution.checkpointing.interval: 60000 state.backend.incremental: trueCheckpoint 目录放在 HDFS 上是必须的。很多人图省事用本地路径结果 TaskManager 宕机重启后JobManager 根本找不到那台机器上的 Checkpoint 数据作业只能从头恢复。另外state.backend.incremental: true这个参数一定要开它开启增量 Checkpoint否则每次全量快照对 HDFS 的压力会非常大。5.3 元数据库 MySQL MGR 在集群中的角色大数据集群里有个容易被忽略的角色——元数据库。Hive 的 Metastore、Ranger 的审计库、Atlas 的图数据库元数据甚至 Flink 的 Hive Catalog 都需要一个外部关系型数据库来存元信息。单机 MySQL 一旦宕机整个集群的分析链路全断。MySQL 8.0 的 MGR组复制是当前比较主流的高可用方案它不像主从复制那样需要手动 failover而是通过 Paxos 协议在组成员之间自动选主。搭建过程用 MySQL Shell 可以非常快# 在第一个实例上执行 dba.createCluster(mycluster) cluster dba.getCluster(mycluster) cluster.addInstance(rootmysql02:3306) cluster.addInstance(rootmysql03:3306)MGR 会自动选出一个主节点客户端通过 Router 连接到集群时主节点故障会自动切换到新的主节点。在大数据集群规划里我通常把 MGR 的三个节点和 ZooKeeper 的三节点放到同一组物理机上职责上互相隔离但硬件资源可以复用。6. 裸机、K8s 还是云主机部署策略的横向对比6.1 K8s 调度 Spark/Flink 的真实收益与代价最近两三年K8s 上跑 Spark 和 Flink 的呼声越来越高。它带来的收益是实实在在的资源利用率高、扩缩容快、环境标准化。但代价也实实在在网络和存储的复杂度被提高了一个量级。以 Spark on K8s 为例提交方式变成spark-submit \ --master k8s://https://k8s-api.example.com:6443 \ --deploy-mode cluster \ --conf spark.kubernetes.container.imageregistry.example.com/spark:v3.4 \ --conf spark.kubernetes.namespacebigdata \ --conf spark.kubernetes.authenticate.driver.serviceAccountNamespark \ --class com.example.Main \ local:///opt/apps/my-job.jar这里需要先构建一个包含 Spark 运行时和作业 jar 的 Docker 镜像。相比 YARNK8s 的一个优势是 Shuffle 数据可以借助外部存储比如对象存储来实现 Executor 的快速伸缩不再依赖 External Shuffle Service。但如果你对 K8s 的网络插件CNI、存储插件CSI不熟我建议先在 YARN 上跑半年再说。K8s 下的 Pod 网络、Ingress、PV/PVC 三层网路概念叠在一起排查“任务偶发超时”“Executor 之间连接失败”这类问题会非常痛苦。6.2 网络方案和存储方案是容器化最大的两道坎K8s 部署大数据组件时最常掉进去的两个坑一个是网络方案选型。Flannel 简单VXLAN 封装性能损耗在 10%20%Calico 用 BGP 直连性能损耗低但配置复杂需要底层网络支持。如果是机房内网裸机部署 K8s我优先推荐 Calico如果是云主机很多云厂商已经提供 VPC 原生网络方案性能更好。另一个是存储方案。Spark Executor 是临时 Pod不涉及持久化但 Flink 的 Checkpoint、HDFS DataNode 的数据目录都涉及持久化存储。用云厂商的云盘性能稳定但成本高用自建的 Ceph 或者直接用裸盘做 Local PV性能好但运维复杂。我的建议非常简单第一套 K8s 大数据集群存储不要走 CSI 动态供应直接把宿主机目录以 hostPath 的方式挂给 HDFS DataNode 和 Flink TaskManager跑通之后再逐步迁移到更成熟的存储方案。6.3 一套务实的中小规模集群部署策略如果让我给一个预算有限的中小团队一个务实的建议我会这么说数据量 100TB 以内、并发任务不超过 50 个直接裸机或云主机手工搭建 Hadoop Spark on YARN Flink on YARN运维成本最低能用最少的人数维持稳定。团队已经有 K8s 运维能力且对资源利用率要求高可以把 Spark、Flink 全部容器化但依然建议保留独立的 HDFS 做存储层不要急着把 HDFS 也容器化。如果是云上环境优先用对象存储替代 HDFS 的冷数据存储热数据用云盘计算层再弹性伸缩。这种混搭模式在成本和弹性之间平衡得最好。7. 集群跑起来之后真正的坑才开始7.1 NameNode 被格式化数据能找回吗这个场景我亲眼见过两次。一次是同事在排查节点故障时误执行了hdfs namenode -format另一次是脚本里不小心带了这个操作。格式化之后NameNode 的current目录被清空但 DataNode 上的数据块文件依然还在。这时候的救命稻草是如果曾经配置过 JournalNode 做 HA或者有 SecondaryNameNode 定期合并的fsimage马上停止所有写入从备份里恢复fsimage和edits文件再启动 NameNode。没有备份的话基本只能认栽。这也是为什么我反复强调NameNode 元数据目录所在的磁盘一定要单独挂载并做定期快照或异地备份。HDFS 数据本身有三副本但元数据一旦丢数据副本再多也找不到目录了。7.2 JVM 堆内内存和 YARN 容器内存打架提交 Spark 任务后发现 Executor 被 YARN 直接 kill日志里报Container killed on request. Exit code is 143这种问题十有八九是内存超限。YARN 用 cgroup 约束容器内存上限如果 JVM 堆内存加上堆外内存超过了容器限制操作系统会直接杀掉进程。比如给容器分配了 4GB但spark.executor.memory4g再加 10% overhead 和堆外内存总占用可能超过 4GB。解决方法是给 overhead 留足空间--conf spark.executor.memoryOverhead768m同时容器内存要大于executor-memory overhead之和。这个参数组合没有万能公式得结合任务的 Shuffle 量和序列化方式调整。一般来说堆外开销按堆内存的 15%20% 留比较稳妥。7.3 磁盘写入慢与数据倾斜的初步排查集群跑了一段时间后会开始出现“某个 Task 特别慢”的情况。如果不是节点硬件问题优先怀疑数据倾斜。在 Spark 中定位倾斜很简单看 Application 界面里某个 Stage 的 Task 耗时分布如果个别 Task 处理的数据量是平均值的几十倍基本就是 join 键或 group by 键分布不均。常见的处理思路有三个对热点 key 加随机前缀把数据打散到多个 Task改用 Broadcast Join把小表广播出去避免 Shuffle调大spark.sql.shuffle.partitions让分区数量变多单分区数据量降下来。Flink 里类似问题表现为某个 Subtask 的 backpressure 持续很高处理方式通常是调整 keyBy 的设计或者给状态加 TTL 防止状态无限膨胀。7.4 日常巡检的三个检查项集群交付之后日常巡检建议固定成三个动作HDFS 健康检查hdfs dfsadmin -report看是否有Under-Replicated块如果长期不恢复说明有节点掉线或磁盘空间满了。YARN 资源水位在 8088 界面看队列负载如果某个队列长期 90% 以上要考虑扩容或调队列配额。Flink Checkpoint 稳定性在 Flink Web UI 的 Checkpoint 页面看最近几小时的成功率和耗时如果频繁失败优先看 HDFS 写入性能和 RocksDB 的磁盘占用。这套检查不需要太复杂但一定要坚持做。大数据集群很少一夜之间坏掉绝大多数故障都有前兆只是没人看日志而已。集群搭建这件事做成一次不难难的是建完以后能让它稳定地跑上几个月不出大问题。我见过太多团队把全部精力花在“搭建”上却在巡检、备份、参数调优上草草收场。如果你刚准备动手优先把这张拓扑图画清楚把资源估算做扎实再开始装系统。后面每一步心里都有了数。