1. 为什么 ResourceManager 必须高可用——从一次生产事故说起
某公司上线了一个实时日志分析平台,底层用 YARN 调度 Spark Streaming 任务。系统平稳运行两周后,凌晨三点,监控告警突然炸开:所有正在运行的 Streaming 作业延迟飙升,新提交的 Application 直接卡在 ACCEPTED 状态超过 15 分钟,Dashboard 上 ApplicationMaster 列表一片空白。运维同学紧急登录集群,发现 ResourceManager 进程已消失,jps输出里没有ResourceManager进程;yarn rmadmin -getServiceState rm1返回standby,而yarn rmadmin -getServiceState rm2报错Connection refused。此时距上次手动重启 RM 已过去 47 小时——原来备用节点因磁盘满导致 JournalNode 写入失败,ZKFC 未能及时触发故障转移,主节点崩溃后整个 YARN 调度层彻底失能。
这不是理论推演,是真实发生过的调度中枢瘫痪事件。YARN 的 ResourceManager(RM)本质是整个集群的“交通指挥中心”:它不执行任何计算任务,但掌管着所有 Container 的分配、ApplicationMaster 的生命周期管理、资源抢占策略、队列配额控制、甚至安全凭证的分发。一旦 RM 宕机,新任务无法提交,正在运行的任务若需申请新 Container(比如 Spark shuffle 溢写后重试、Flink checkpoint 后恢复),就会无限等待。更致命的是,RM 自身状态(如 Application 状态、NodeManager 注册信息、Container 分配记录)默认只保存在内存中,重启即丢失——这意味着即使你快速拉起一个新的 RM 进程,它也“不认识”之前跑着的任何任务,只能把它们全部标记为 FAILED。
这就是为什么 Hadoop 2.4.0 引入 ResourceManager HA(High Availability)机制,不是锦上添花,而是生产环境的生存底线。它通过引入Active-Standby 架构+共享状态存储+自动故障转移(Automatic Failover)三重保障,确保 RM 单点故障时,备用节点能在秒级内接管全部职责,且对上层应用完全透明。很多团队误以为“我集群小、任务少、停几分钟无所谓”,但实际中,一次 RM 故障往往触发连锁反应:ETL 流水线中断、报表生成失败、实时推荐模型数据断流、下游依赖服务超时熔断……损失远超运维人力成本。本文将完全基于 Hadoop 官方文档与多年线上集群调优经验,手把手带你构建一个真正可靠、可验证、可运维的 RM HA 集群,不讲虚概念,只拆解每一个配置项背后的物理意义、每一个命令的实际效果、每一个日志行的关键线索。
2. HA 架构的三大支柱:状态存储、故障检测与角色切换
要让两个 RM 进程协同工作,核心在于解决三个根本问题:状态如何同步?谁来判断主节点挂了?挂了之后怎么无缝接班?Hadoop 的设计非常务实,它没有试图让两个 RM 实时双写,而是采用“主写备读+异步复制”的模式,将复杂性分解到三个独立组件上。理解这三者的关系,是避免后续配置踩坑的前提。
2.1 共享状态存储:不是 HDFS,而是 ZK 或 FileSystem
很多人第一反应是“把 RM 状态存到 HDFS 不就行了?”——这是典型误区。HDFS 是为大文件顺序读写优化的分布式文件系统,其元数据操作(如创建/删除文件)由 NameNode 串行处理,吞吐量有限,且存在单点瓶颈。而 RM 的状态更新是高频、细粒度、强一致性的:每次 Container 分配、Application 状态变更、NodeManager 心跳响应,都需要原子性地写入状态存储。HDFS 无法满足这种低延迟、高并发的元数据操作需求。
因此,YARN HA 强制要求使用ZooKeeper(推荐)或基于文件系统的共享存储(如 NFS)作为状态存储后端。ZooKeeper 是为协调服务设计的,其 ZNode 的创建、删除、Watch 机制天然支持强一致性、高可用和事件通知,完美匹配 RM HA 的需求。具体来说,Active RM 会将以下关键状态持久化到 ZooKeeper 的指定路径(如/yarn-leader-election和/rmstore):
- Leader 选举锁:通过在
/yarn-leader-election下创建临时顺序节点实现。哪个 RM 成功创建了序号最小的节点,就成为 Active。 - RM 状态快照(RMStateStore):包括 Application、Attempt、Reservation 等所有运行时状态,序列化后存入
/rmstore。Standby RM 会持续监听该路径变化,并将新状态加载到本地内存。
提示:ZooKeeper 集群本身必须是高可用的,至少 3 个节点。如果 ZK 集群不可用,RM HA 将完全失效,因为连 Leader 选举都无法进行。切勿将 ZK 与 RM 部署在同一台物理机上,否则单机故障会同时击穿两者。
2.2 故障检测:ZKFC 是如何“盯梢”的?
ZooKeeper 本身不负责监控 RM 进程是否存活,它只提供一个“心跳注册处”。真正的健康检查由ZKFailoverController(ZKFC)完成。每个运行 RM 的节点上,都必须启动一个 ZKFC 进程,它与同节点的 RM 是伴生关系(通常由yarn-daemon.sh自动拉起)。ZKFC 的工作流程高度结构化:
Health Monitor(健康监控器):ZKFC 会定期(默认每 5 秒)向本机的 RM 发送一个轻量级 RPC 请求(
checkHealth)。这个请求不涉及任何业务逻辑,只验证 RM 的 RPC Server 是否响应正常。如果连续 3 次(即 15 秒内)无响应,Health Monitor 就判定本机 RM “疑似死亡”。ActiveStandbyElector(主备选举器):一旦 Health Monitor 报告异常,ActiveStandbyElector 就会介入。它首先尝试在 ZooKeeper 中释放本节点持有的 Leader 锁(删除自己的临时节点)。然后,它会主动去
/yarn-leader-election路径下查看当前所有子节点,找到序号最小的那个——如果发现不是自己,说明已有其他节点当选 Active,它就安心进入 Standby 状态;如果发现自己仍是序号最小,它就再次尝试获取锁,成为新的 Active。Graceful Shutdown(优雅关闭):当 ZKFC 决定让本节点 RM 从 Active 切换为 Standby 时,它会先向 RM 发送一个
transitionToStandbyRPC 命令。RM 收到后,会停止接受新请求、完成正在处理的 RPC、并清理本地缓存,最后才真正退出 Active 角色。这个过程确保了状态的一致性。
注意:ZKFC 的健康检查是“本地视角”,它只知道自己节点上的 RM 是否存活。它无法感知网络分区(Network Partition)问题。例如,如果 RM1 和 ZK 集群之间的网络中断,但 RM1 本身仍在运行,ZKFC1 可能会错误地认为 RM1 死亡,从而触发不必要的故障转移。因此,ZK 集群的网络稳定性至关重要。
2.3 自动故障转移:从 Standby 到 Active 的“登基仪式”
当 Standby RM 接收到 ZKFC 发来的transitionToActive命令后,它不会立刻开始工作,而是执行一套严格的“登基”流程,确保自身状态与集群最新状态完全一致:
状态同步(State Synchronization):Standby RM 首先会从 ZooKeeper 的
/rmstore路径下,拉取最新的 RMStateStore 快照,并反序列化加载到内存。这一步是耗时最长的,决定了故障转移的 RTO(Recovery Time Objective)。如果快照很大(比如有数千个长期运行的 Flink 作业),加载可能需要数秒甚至数十秒。服务启动(Service Activation):状态加载完成后,RM 才会真正启动其内部的各个服务模块:
ClientRMService:开始监听客户端(如yarn application -submit)的 RPC 请求。ApplicationMasterService:开始处理来自 AM 的心跳和 Container 请求。ResourceTrackerService:开始接收来自 NodeManager 的心跳和资源汇报。
对外宣告(Announcement):最后,RM 会向 ZooKeeper 写入一条明确的状态声明,表明自己已成为 Active。此时,ZKFC 才会向客户端返回成功响应。
整个过程是原子性的。如果在状态同步或服务启动的任意一步失败,RM 会立即回滚,并重新进入 Standby 状态,绝不会以一个“半残废”的状态对外提供服务。这也是为什么 RM HA 的配置中,yarn.resourcemanager.zk-state-store.address和yarn.resourcemanager.store.class这两个参数必须精确无误——它们直接决定了“登基仪式”能否顺利完成。
3. 配置文件逐行精解:yarn-site.xml的 12 个关键参数
yarn-site.xml是 RM HA 的心脏配置文件,其中任何一个参数的微小偏差,都可能导致故障转移失败、状态不同步,甚至集群陷入“脑裂”(Split-Brain)状态。下面我将结合生产环境的真实配置,逐行解释每一个与 HA 直接相关的核心参数,不仅告诉你“填什么”,更告诉你“为什么这么填”。
3.1 HA 基础开关与标识
<!-- 启用 ResourceManager HA --> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <!-- 为这个 RM 集群定义一个唯一的逻辑名称 --> <property> <name>yarn.resourcemanager.cluster-id</name> <value>yarn-cluster-prod</value> </property> <!-- 定义 RM 的逻辑 ID 列表,必须与下面的 rm1, rm2 对应 --> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property>yarn.resourcemanager.ha.enabled是总开关,设为false则所有 HA 相关配置均被忽略。yarn.resourcemanager.cluster-id是一个全局唯一标识符,用于区分不同的 YARN 集群。如果你的公司有多个 Hadoop 集群(如 dev、staging、prod),它们的cluster-id必须完全不同,否则 ZooKeeper 中的状态会互相污染。yarn.resourcemanager.ha.rm-ids则是本集群内所有 RM 实例的逻辑 ID 列表,ID 名称可以自定义(如rm-a,rm-b),但必须与后续的rm1,rm2等配置项严格对应。
3.2 RM 实例的具体地址与端口
<!-- 为 rm1 指定其 Web UI 地址 --> <property> <name>yarn.resourcemanager.webapp.address.rm1</name> <value>rm1-host:8088</value> </property> <!-- 为 rm1 指定其 RPC 地址 --> <property> <name>yarn.resourcemanager.address.rm1</name> <value>rm1-host:8032</value> </property> <!-- rm1 的调度器地址 --> <property> <name>yarn.resourcemanager.scheduler.address.rm1</name> <value>rm1-host:8030</value> </property> <!-- rm1 的资源跟踪器地址 --> <property> <name>yarn.resourcemanager.resource-tracker.address.rm1</name> <value>rm1-host:8031</value> </property> <!-- rm1 的管理员地址 --> <property> <name>yarn.resourcemanager.admin.address.rm1</name> <value>rm1-host:8033</value> </property> <!-- rm2 的所有地址配置,完全镜像 rm1 --> <property> <name>yarn.resourcemanager.webapp.address.rm2</name> <value>rm2-host:8088</value> </property> <property> <name>yarn.resourcemanager.address.rm2</name> <value>rm2-host:8032</value> </property> <property> <name>yarn.resourcemanager.scheduler.address.rm2</name> <value>rm2-host:8030</value> </property> <property> <name>yarn.resourcemanager.resource-tracker.address.rm2</name> <value>rm2-host:8031</value> </property> <property> <name>yarn.resourcemanager.admin.address.rm2</name> <value>rm2-host:8033</value> </property>这是最容易出错的部分。rm1-host和rm2-host必须是主机名(Hostname),而不是 IP 地址。因为 YARN 的内部通信大量依赖于主机名解析,如果这里填了 IP,可能会导致 NodeManager 在向 RM 注册时,无法正确识别 RM 的身份,进而影响 Container 分配。所有端口(8030-8033, 8088)必须与core-site.xml和hdfs-site.xml中的其他服务端口不冲突。特别注意yarn.resourcemanager.address,这是客户端(如yarn application命令)连接 RM 的主 RPC 端口,必须保证该端口在防火墙中开放。
3.3 ZooKeeper 集成与状态存储
<!-- 指定 ZooKeeper 集群的连接字符串 --> <property> <name>yarn.resourcemanager.zk-address</name> <value>zk1-host:2181,zk2-host:2181,zk3-host:2181</value> </property> <!-- 指定 RM 状态存储的实现类,必须是 ZK-based --> <property> <name>yarn.resourcemanager.store.class</name> <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value> </property> <!-- 指定 ZK 中存储 RM 状态的根路径 --> <property> <name>yarn.resourcemanager.zk-state-store.address</name> <value>zk1-host:2181,zk2-host:2181,zk3-host:2181</value> </property>yarn.resourcemanager.zk-address是 ZKFC 连接 ZooKeeper 进行 Leader 选举的地址。yarn.resourcemanager.zk-state-store.address是 Standby RM 连接 ZooKeeper 读取状态快照的地址。虽然两者通常指向同一个 ZK 集群,但它们的用途完全不同,因此配置项也不同。yarn.resourcemanager.store.class必须设置为ZKRMStateStore,这是启用 HA 的硬性要求。任何其他值(如FileSystemRMStateStore)都会导致 HA 失效。
3.4 故障转移与超时控制
<!-- 启用自动故障转移 --> <property> <name>yarn.resourcemanager.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- ZKFC 健康检查的间隔(毫秒) --> <property> <name>yarn.resourcemanager.zkfc.health-monitor.check-interval-ms</name> <value>5000</value> </property> <!-- ZKFC 健康检查的超时时间(毫秒) --> <property> <name>yarn.resourcemanager.zkfc.health-monitor.rpc-timeout.ms</name> <value>20000</value> </property> <!-- ZKFC 在判定 RM 死亡前,允许的连续失败次数 --> <property> <name>yarn.resourcemanager.zkfc.health-monitor.max-attempts</name> <value>3</value> </property>yarn.resourcemanager.ha.automatic-failover.enabled是自动故障转移的总开关。如果设为false,则必须手动执行yarn rmadmin -transitionToActive rm2命令才能切换,这在生产环境中是不可接受的。后面三个参数共同决定了故障检测的灵敏度。check-interval-ms=5000表示每 5 秒检查一次;rpc-timeout.ms=20000表示每次检查最多等待 20 秒;max-attempts=3表示连续 3 次失败才判定为宕机。这意味着,从 RM 真正宕机,到 ZKFC 发起故障转移,最长可能需要5 * 3 = 15秒。你可以根据集群规模和业务容忍度调整这些值,但切忌将max-attempts设为 1,这会导致网络抖动时频繁误切。
4. 部署与验证全流程:从零开始搭建一个可信赖的 HA 集群
纸上谈兵终觉浅,绝知此事要躬行。下面我将模拟一个标准的 5 节点集群(2 个 RM、3 个 ZK),完整复现从环境准备、配置分发、服务启动到最终验证的全过程。所有命令和步骤均来自某大型互联网公司的标准化部署手册,经过数百次线上集群升级验证。
4.1 环境准备与前置检查
在开始配置前,必须确保以下基础环境已就绪,任何一项不满足,后续步骤都将失败:
- ZooKeeper 集群已稳定运行:3 个节点(
zk1-host,zk2-host,zk3-host),zkServer.sh status显示均为Mode: follower或Mode: leader,且echo stat | nc zk1-host 2181能返回完整的状态信息。 - 所有节点时间已同步:使用
chrony或ntpd,date命令在所有节点上输出的时间差必须小于 1 秒。时间不同步会导致 ZK 的 Session 超时异常。 - 主机名解析正常:在
rm1-host上执行ping rm2-host和ping zk1-host必须成功;同样,在rm2-host上执行ping rm1-host和ping zk1-host也必须成功。建议在/etc/hosts中静态配置所有主机名,避免 DNS 单点故障。 - Java 版本统一:所有节点
java -version输出必须一致,推荐 OpenJDK 11。不同版本的 JVM 可能导致序列化兼容性问题。
提示:在
rm1-host和rm2-host上,分别创建专用的yarn用户,并确保该用户对$HADOOP_HOME目录有完全读写权限。切勿使用root用户启动 YARN 服务,这是严重的安全风险。
4.2 配置文件分发与校验
将上一节配置好的yarn-site.xml文件,通过scp或配置管理工具(如 Ansible)分发到所有节点的$HADOOP_HOME/etc/hadoop/目录下。分发完成后,必须在每个节点上执行以下校验命令:
# 在 rm1-host 上校验 $HADOOP_HOME/bin/yarn --config $HADOOP_HOME/etc/hadoop checknative -a # 输出应包含 "zlib: true" 和 "snappy: true",表示本地库加载正常 # 检查配置语法 $HADOOP_HOME/bin/yarn --config $HADOOP_HOME/etc/hadoop getconf -confKey yarn.resourcemanager.ha.enabled # 应输出 "true" # 检查 ZK 连通性(在 rm1-host 上) echo "ls /" | $HADOOP_HOME/bin/zkCli.sh -server zk1-host:2181 # 应返回 ZooKeeper 根节点下的子节点列表这一步看似繁琐,却是避免“配置已改,服务未启”这类低级错误的关键。我曾见过一个集群,因为yarn-site.xml文件权限被误设为600(只有 owner 可读),导致yarn-daemon.sh启动时无法读取配置,默默以非 HA 模式运行,直到发生故障才暴露问题。
4.3 服务启动与初始状态观察
启动顺序至关重要,必须严格遵循:先启动 ZooKeeper,再启动 ResourceManager,最后启动 NodeManager。
# 在所有 ZooKeeper 节点上启动 ZK $ZOOKEEPER_HOME/bin/zkServer.sh start # 在 rm1-host 上启动 RM 和 ZKFC $HADOOP_HOME/sbin/yarn-daemon.sh start resourcemanager $HADOOP_HOME/sbin/yarn-daemon.sh start zkfc # 在 rm2-host 上启动 RM 和 ZKFC $HADOOP_HOME/sbin/yarn-daemon.sh start resourcemanager $HADOOP_HOME/sbin/yarn-daemon.sh start zkfc启动后,不要急于验证,先耐心等待 30 秒,让 ZKFC 完成初始的 Leader 选举。然后,执行以下命令观察状态:
# 查看两个 RM 的当前角色 $HADOOP_HOME/bin/yarn rmadmin -getServiceState rm1 # 应输出 "active" 或 "standby" $HADOOP_HOME/bin/yarn rmadmin -getServiceState rm2 # 应输出另一个状态 # 查看 ZK 中的 Leader 锁节点 echo "ls /yarn-leader-election" | $HADOOP_HOME/bin/zkCli.sh -server zk1-host:2181 # 应看到类似 "[rm1-host_0000000000, rm2-host_0000000001]" 的输出,序号小的为 Active # 查看 RM 的 Web UI # 访问 http://rm1-host:8088 和 http://rm2-host:8088 # Active 节点的 UI 应显示 "Active" 状态,Standby 节点的 UI 应显示 "Standby" 状态,并提示 "This is a standby ResourceManager"如果getServiceState命令报错Connection refused,说明 RM 进程根本没有起来,应立即检查$HADOOP_HOME/logs/yarn-*-resourcemanager-*.log日志。最常见的错误是java.net.UnknownHostException,这直接指向了主机名解析失败。
4.4 故障转移实战演练:模拟宕机与秒级恢复
验证 HA 的唯一方法,就是亲手制造一次故障。我们来模拟最典型的场景:Active RM 进程被kill -9强制终止。
# 首先确认 rm1 是 Active $HADOOP_HOME/bin/yarn rmadmin -getServiceState rm1 # 假设输出 "active" # 在 rm1-host 上,强制杀死 RM 进程 pkill -f "ResourceManager" # 立即在 rm2-host 上观察 watch -n 1 '$HADOOP_HOME/bin/yarn rmadmin -getServiceState rm2' # 你会看到输出从 "standby" 在 5-10 秒内变为 "active" # 同时,在 rm1-host 上观察 ZKFC 日志 tail -f $HADOOP_HOME/logs/yarn-*-zkfc-*.log # 会看到类似 "Transitioning to standby state..." 和 "Successfully transitioned to standby" 的日志整个过程应该在 10 秒内完成。此时,你可以在rm2-host:8088的 Web UI 上,看到所有之前在rm1-host上运行的应用程序(Application)都完好无损地迁移了过来,状态依然是RUNNING。这才是一个真正可靠的 HA 集群。
注意:不要在生产环境随意
kill -9,应在预发或测试环境充分演练。演练后,务必手动启动rm1-host的 RM 进程,并观察它是否会自动降级为 Standby,这是验证“自动故障恢复”能力的关键。
5. 运维监控与排错指南:读懂日志里的每一行真相
HA 集群上线后,运维工作才刚刚开始。一个优秀的运维工程师,不仅要会部署,更要能从海量日志中,精准定位问题的根源。下面我将分享几个最常遇到的 HA 故障场景,以及对应的日志分析方法和解决方案。
5.1 场景一:ZKFC 无法连接 ZooKeeper —— “Connection refused”
现象:yarn-daemon.sh start zkfc启动后,jps能看到ZKFC进程,但yarn rmadmin -getServiceState rm1始终报错Connection refused,或者zkfc日志中反复出现java.net.ConnectException: Connection refused。
日志线索:打开$HADOOP_HOME/logs/yarn-*-zkfc-*.log,搜索ConnectException,你会看到类似以下的堆栈:
2023-10-05 14:23:42,123 INFO ipc.Client (Client.java:handleConnectionFailure(842)) - Retrying connect to server: zk1-host/192.168.1.101:2181. Already tried 0 time(s); maxRetries=45 ... 2023-10-05 14:24:02,123 ERROR ha.ActiveStandbyElector (ActiveStandbyElector.java:becomeActive(822)) - Exception while trying to become active. java.io.IOException: Failed on local exception: java.net.ConnectException: Connection refused; Host Details : local host is: "rm1-host/192.168.1.100"; destination host is: "zk1-host":2181;根因分析:这几乎 100% 是网络层面的问题。ZKFC进程在rm1-host上,试图连接zk1-host:2181,但连接被拒绝。原因可能是:
zk1-host上的 ZooKeeper 服务根本没有启动。zk1-host的防火墙(iptables或firewalld)阻止了 2181 端口的入站连接。rm1-host和zk1-host之间存在网络 ACL 或安全组规则,禁止了通信。
解决方案:
- 在
zk1-host上执行netstat -tuln | grep 2181,确认 ZooKeeper 进程确实在监听0.0.0.0:2181。 - 在
rm1-host上执行telnet zk1-host 2181或nc -zv zk1-host 2181,测试端口连通性。 - 如果
telnet失败,检查zk1-host的防火墙设置:sudo systemctl status firewalld,并临时关闭sudo systemctl stop firewalld进行测试。 - 如果
telnet成功,但ZKFC仍报错,则检查yarn-site.xml中的yarn.resourcemanager.zk-address配置,确认主机名拼写无误。
5.2 场景二:Standby RM 无法加载状态 —— “No data in ZK node”
现象:yarn rmadmin -getServiceState rm2显示standby,但rm2-host:8088的 Web UI 上,Application 列表为空,且yarn application -list命令没有任何输出。rm2的日志中,反复出现WARN org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore: No data in ZK node。
日志线索:在$HADOOP_HOME/logs/yarn-*-resourcemanager-*.log中搜索ZKRMStateStore,你会看到:
2023-10-05 14:30:15,678 WARN recovery.ZKRMStateStore (ZKRMStateStore.java:loadState(456)) - No data in ZK node /rmstore/RMAppRoot 2023-10-05 14:30:15,679 INFO recovery.ZKRMStateStore (ZKRMStateStore.java:loadState(457)) - Loaded 0 applications from ZK根因分析:ZKRMStateStore在 ZooKeeper 的/rmstore路径下,找不到任何有效的状态数据。这通常是因为:
- Active RM (
rm1) 从未成功将状态写入 ZooKeeper。可能的原因是yarn.resourcemanager.store.class配置错误,或者yarn.resourcemanager.zk-state-store.address指向了错误的 ZK 集群。 - ZooKeeper 的
/rmstore路径被手动清空过。 - Active RM 在写入状态时发生了异常,但进程并未崩溃,导致状态处于不一致的中间态。
解决方案:
- 首先,在
rm1-host上,确认yarn.resourcemanager.store.class的值确实是org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore。 - 然后,在
rm1-host上,检查yarn.resourcemanager.zk-state-store.address的配置,并用zkCli.sh连接到该地址,执行ls /rmstore,确认路径存在且有子节点。 - 如果
/rmstore下确实为空,可以尝试手动触发一次状态保存:在rm1-host上执行yarn rmadmin -refreshQueues,这是一个轻量级的 RPC,会强制 RM 将当前队列配置写入状态存储。 - 如果以上都无效,最稳妥的办法是:停止所有 RM,清空 ZooKeeper 中的
/yarn-leader-election和/rmstore路径,然后重新启动两个 RM,让它们从头开始选举和同步。
5.3 场景三:脑裂(Split-Brain)—— 两个 RM 同时为 Active
现象:这是最危险的故障。yarn rmadmin -getServiceState rm1和yarn rmadmin -getServiceState rm2都返回active。Web UI 上,两个节点都显示 “Active”,且都能接受新任务提交。这会导致资源被重复分配,Container 被错误地 kill,应用程序行为完全不可预测。
日志线索:在rm1和rm2的zkfc日志中,会看到大量关于Failed to become active和Failed to become standby的警告,以及Session expired的错误。
根因分析:脑裂的根本原因是ZooKeeper 集群出现了网络分区(Network Partition)。例如,rm1-host和zk1-host之间的网络中断,但rm1-host和zk2-host、zk3-host依然连通;与此同时,rm2-host与zk1-host连通,但与zk2-host、zk3-host中断。结果,rm1和rm2各自都认为自己是唯一存活的节点,从而都成为了 Active。
解决方案:
- 立即止损:手动将其中一个 RM 降级为 Standby。例如,在
rm2-host上执行yarn rmadmin -transitionToStandby rm2。这会强制rm2退出 Active 状态。 - 修复网络:这是治本之策。检查
rm1-host和rm2-host与所有 ZooKeeper 节点之间的网络连通性,修复中断的链路。 - 预防措施:在生产环境中,ZooKeeper 集群的节点应部署在不同的物理机架(Rack)上,并使用跨机架的网络链路。同时,为 ZKFC 配置更长的 Session Timeout(
yarn.resourcemanager.zkfc.ha.zk-session-timeout-ms,默认 5000ms),给网络恢复留出缓冲时间。
最后分享一个个人心得:在每次重大版本升级(如 Hadoop 3.x)后,我都会在预发环境,用
tc(Traffic Control)工具人为注入网络延迟和丢包,模拟各种弱网场景,对 RM HA 进行压力测试。这比任何理论分析都更能暴露配置的脆弱点。真正的高可用,不是写在文档里的承诺,而是经受住混沌工程考验后的底气。