news 2026/10/17 11:42:49

Hadoop 3.1.4 HA集群搭建实战:NameNode和ResourceManager高可用配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hadoop 3.1.4 HA集群搭建实战:NameNode和ResourceManager高可用配置

做大数据基础架构的人早晚都要过一关:给 Hadoop 配 HA。我最早搭的是单点 NameNode,数据量小的时候还勉强能撑,等真正接手每天跑几十个定时任务的集群时才发现,单点真的是拿运气赌服务可用性——一次断电、一次磁盘满、一次进程假死,整个集群就得停摆。这篇文章记录的是一套能直接抄作业的 Hadoop 3.1.4 完全分布式 HA 搭建流程,NameNode 和 ResourceManager 都做高可用,附带历史服务器和日志聚合,覆盖从环境准备、核心机制、配置文件、启动顺序到故障切换演练的完整链路。

文章不会停留在“把配置贴出来”就完事,我会把每个关键参数为什么要这么写、哪个环节最容易翻车、启动报错怎么排查都讲清楚。适合两类读者:一类是刚接触大数据、想在一周内把 HA 集群跑起来的初学者,另一类是已经在用单点 Hadoop、准备把生产集群改造成高可用架构的运维同学。无论你手头是三台虚拟机还是五台物理机,按这个思路调整角色分配就行。

1. 动手前先想清楚:节点规划、版本选择和 HA 的基本盘

1.1 为什么选 Hadoop 3.1.4 而不是更高版本

版本选择这事,我的建议很务实:生产环境求稳,不带第一个吃螃蟹。Hadoop 3.1.4 属于 3.x 里非常成熟的版本,社区踩坑记录多、网上资料全、兼容性好,配套组件也好找。3.2 和 3.3 系列虽然功能更新,但某些发行版环境下的依赖冲突和处理 MapReduce 任务时的行为差异会让新人排查起来很痛苦。

如果你的集群跑的是 Spark、Hive、Flink 这类上层计算框架,一定要先确认它们官方支持的 Hadoop 版本范围。我见过有人装 Hadoop 3.3 之后发现某版本 Flink 连接器不兼容,最后不得不降级重搭,白白浪费两天时间。3.1.4 对主流计算框架的适配度很高,这也是我推荐它的原因。

本教程基于 OpenJDK 1.8 环境测试。Hadoop 3.x 对 Java 版本有明确要求,JDK 1.8 是兼容性最好、出问题最少的选择。用更高版本的 JDK 出发时可能遇到编译级别的报错,新手不建议折腾。

1.2 五节点角色分配与硬件规划

HA 集群的最小可用规模是 3 台机器,但对于想彻底模拟生产环境的场景,我更推荐 5 台。理由很简单:NameNode 和 ResourceManager 做 HA 各占两个节点,JournalNode 需要奇数个(3 个),ZooKeeper 也需要奇数个(3 个),如果机器太少,这些角色会互相挤占,出问题的时候不太好判断是谁在拖后腿。

我这里给出的规划是一套五节点方案,每台机器建议 4 核 8G 以上,硬盘 500G 起步:

节点角色分配说明
hadoop01NameNode(Active)、ZKFC、ResourceManager(Active)、ZooKeeper主控节点,负载较重
hadoop02NameNode(Standby)、ZKFC、ResourceManager(Standby)、ZooKeeper备控节点,和 01 互为备份
hadoop03JournalNode、DataNode、ZooKeeper三台 JournalNode 之一
hadoop04JournalNode、DataNode、JobHistoryServer同时承载历史服务器
hadoop05JournalNode、DataNode纯存储节点

一个很容易被忽略的点:JournalNode 和 ZooKeeper 都要求奇数个,因为选举机制需要“多数派”才能工作。5 节点方案里放 3 个 JournalNode、3 个 ZooKeeper,正好满足要求。如果你的机器只有 3 台,也可以把 ZooKeeper、JournalNode、NameNode、DataNode 全部压到 3 台上,能跑通,但故障隔离性就差了,生产环境不推荐。

安装前还有三件事必须确认:

  • 所有节点的主机名和 /etc/hosts 映射已经配置好,后面所有配置都基于主机名,不写死 IP;
  • 所有节点的系统时间已经同步。这一点经常被无视,但 JournalNode 和 NameNode 之间的通信对时间漂移非常敏感,偏差超过秒级就可能出现无法同步 edit log 的诡异报错;
  • 防火墙关闭或者至少把 Hadoop 相关端口放通。在实验环境里,我建议直接 systemctl stop firewalld && systemctl disable firewalld,避免排查问题时被“端口不通”干扰。

2. HA 高可用到底在解决什么问题:故障切换的核心机制

2.1 单节点“死”在生产链路里的真实场景

没有 HA 的 Hadoop 集群里,NameNode 是唯一的核心,它维护着整个文件系统的元数据(Namespace、Block 位置信息等)。一旦 NameNode 所在机器宕机,整个集群立即失联,所有计算任务中断,数据无法读写。

你可能想说“恢复机器重启不就行了”?但问题是,如果宕机是因为硬盘损坏而不是系统崩溃,元数据就难说能保住多少了。更何况,重启期间跑批任务完全停摆,业务方会拿着工单追你到怀疑人生。HA 的核心目的就是让备用节点在几十秒内自动接管,把停机时间压缩到业务无感。

2.2 NameNode 高可用的四个关键组件

NameNode 的高可用不是单靠“两台机器跑同一个进程”就行的,它涉及四个协作组件:

  • ZooKeeper:承担“选主大脑”的角色。两个 NameNode 启动后都会去 ZooKeeper 注册并争取 Active 身份,ZooKeeper 通过分布式锁保证同一时刻只有一个 NameNode 成为 Active;
  • 共享存储(JournalNode):Active NameNode 会把写操作产生的 edit log 实时写入 JournalNode 集群,Standby NameNode 持续从 JournalNode 读取这些日志并应用到自己的内存中,保证两个 NameNode 的元数据时刻对齐;
  • ZKFC(DFSZKFailoverController):每个 NameNode 节点上都有一个独立的 ZKFC 进程,它负责监控本机 NameNode 状态,往 ZooKeeper 里写健康信息,并在检测到异常时主动触发切换;
  • Fencing(隔离)机制:这是很多人会忽略但至关重要的一环。切换时要确保旧的 Active NameNode 真正退出工作,防止两个节点同时写元数据导致数据错乱,也就是所谓的“脑裂”。常见配置是 sshfence,通过 SSH 登录旧节点执行 kill 命令,或者配置 shell 命令做隔离。

2.3 ResourceManager 的 HA 思路有什么不同

ResourceManager 的高可用逻辑和 NameNode 类似,也是两个 RM 注册到 ZooKeeper,Active 节点负责接收客户端请求和分配资源。区别在于 RM 的状态存储方式:它不需要 JournalNode 这样的共享日志集群,而是依赖 ZooKeeper + 本地状态目录。

配置时需要设置yarn.resourcemanager.recovery.enabled=true,这样 Active RM 会把应用状态、调度器状态保存下来,切换后新的 Active RM 可以从 ZooKeeper 和本地文件系统恢复。所以 RM 的高可用对 ZooKeeper 的依赖更直接,ZooKeeper 挂了,RM 的选主也会瘫痪。

2.4 理解这几行配置背后的逻辑

如果你已经看过了不少 HA 教程,会发现配置里总有几个固定的名字:hacluster、nn1、nn2、mycluster、rm1、rm2。这些不是随便写的:

  • fs.defaultFS=hdfs://hacluster告诉客户端,这个集群的名称服务叫hacluster,它会去查找hacluster对应的 NameNode 地址列表;
  • dfs.client.failover.provider指定的ConfiguredFailoverProxyProvider负责让客户端自动探测哪个 NameNode 是 Active,并自动实现“先连 nn1,不行再连 nn2”的重试逻辑;
  • yarn.resourcemanager.cluster-id标识的是 YARN 集群 ID,客户端拿到这个 ID 才能从 ZooKeeper 中找到正确的 RM 主从信息。

理解这些名字的含义比背配置更重要,因为以后你要是加第三个 NameNode 或迁移集群,只需要改这些名字对应的清单,而不是大段重写配置文件。

3. 基础环境搭建:JDK、主机映射、免密登录、时钟同步与 ZooKeeper 集群

3.1 JDK 安装与 hosts 映射

在所有节点上安装 OpenJDK 1.8,可以走最简单的方式:

yum install -y java-1.8.0-openjdk

安装后验证一下版本:

java -version # 至少是 1.8.0 某一版本,不是更高版本就行

然后配置每台机器的 /etc/hosts。我这里用内网模拟网段 192.168.109.x:

192.168.109.101 hadoop01 192.168.109.102 hadoop02 192.168.109.103 hadoop03 192.168.109.104 hadoop04 192.168.109.105 hadoop05

这里用 IP 还是主机名有讲究:集群内部通信尽量用主机名,因为后续机器的内网 IP 可能变化,而主机名配置一次就不会变。我之前出过一次事故,某台机器 DHCP 分配到新 IP,所有依赖旧 IP 的 DataNode 全部成为“孤儿节点”,改 hosts 之后重启整个集群才恢复。

3.2 SSH 免密登录:一定要做到“双向免密”

Hadoop 启动脚本会通过 SSH 从主控节点登录到其他节点启动进程,所以ssh 必须免密。这里最容易犯的错是只做了主节点到其他节点的免密,结果从 standby 节点执行 start-dfs.sh 时又卡住。

最简单的做法是让每台机器都生成密钥,然后把所有机器的公钥互相同步:

# 在所有节点执行 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa # 然后把每台机器的公钥汇总到 authorized_keys,或者用 ssh-copy-id 互相拷贝 ssh-copy-id hadoop01 ssh-copy-id hadoop02 # 其他节点同理,保证任意两台机器之间都能免密

测试的时候不要只测一条链路,我习惯在每台机器上循环执行:

for host in hadoop01 hadoop02 hadoop03 hadoop04 hadoop05; do ssh $host hostname; done

如果哪一台输出了 hostname,说明免密没问题。另外要确认每台机器对自身也能免密,因为有些 Hadoop 脚本会 SSH 到本机。

3.3 ZooKeeper 集群安装与 zoo.cfg 配置

HA 依赖 ZooKeeper,所以在配置 Hadoop 之前,先把 ZooKeeper 集群搭好。我在实际部署中用的是稳定版 ZooKeeper 3.4.14(3.1.4 对应的 Hadoop 自带的 ZKFC 对它支持很完善)。

解压后每台节点的conf/zoo.cfg基本一致:

tickTime=2000 initLimit=10 syncLimit=5 dataDir=/opt/zookeeper/data clientPort=2181 server.1=hadoop01:2888:3888 server.2=hadoop02:2888:3888 server.3=hadoop03:2888:3888

然后在各节点的 dataDir 里创建myid文件:

# hadoop01 echo 1 > /opt/zookeeper/data/myid # hadoop02 echo 2 > /opt/zookeeper/data/myid # hadoop03 echo 3 > /opt/zookeeper/data/myid

这里有一个我踩过三次的坑:dataDir如果没有手动创建,ZooKeeper 会启动失败。另外myid必须和 zoo.cfg 里的 server 编号一一对应,写错了会出现该节点一直处于 unknow state 的怪现象。

启动 ZooKeeper 并确认状态:

# 三台节点分别启动 /opt/zookeeper/bin/zkServer.sh start # 在任意一台执行 /opt/zookeeper/bin/zkServer.sh status # 应该看到 leader/follower 输出,且 mode 不是 standalone

判断 ZooKeeper 集群是否成功,不要只看进程有没有起来,要看 zkServer.sh status 的输出。如果显示 standalone 或连接被拒绝,说明集群选举和通信有问题,后面 Hadoop 的 HA 一定起不来。

4. 关键配置逐项拆解:五大 XML 文件抄作业版

4.1 配置文件总览与路径约定

Hadoop 3.1.4 的核心配置在$HADOOP_HOME/etc/hadoop/目录下。你需要关注 5 个文件:core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml、workers(3.x 里 workers 替代了旧版本的 slaves)。下面我把每个文件的关键项拆开解释,方便你理解而不是死记。

4.2 core-site.xml:全局入口配置

<configuration> <!-- 指定使用的文件系统地址,hacluster 是自定义的 nameservice --> <property> <name>fs.defaultFS</name> <value>hdfs://hacluster</value> </property> <!-- 临时目录,最好单独分配,避免 /tmp 被系统清理 --> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> <!-- 指定 ZooKeeper 集群地址 --> <property> <name>ha.zookeeper.quorum</name> <value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value> </property> <!-- 连接 NameNode 的超时时间,调大点避免 HA 切换时客户端报连接超时 --> <property> <name>ipc.client.connect-timeout</name> <value>10000</value> </property> </configuration>

hadoop.tmp.dir这个路径很关键,NameNode 格式化产生的元数据就放在这里。如果你把默认的/tmp/hadoop-${user.name}留着,系统重启时清空 /tmp,集群直接瘫痪。生产环境务必指定到独立数据盘。

4.3 hdfs-site.xml:NameNode HA 的核心战场

<configuration> <!-- 名称服务标识 --> <property> <name>dfs.nameservices</name> <value>hacluster</value> </property> <!-- 这个 nameservice 下有两个 NameNode 逻辑名 --> <property> <name>dfs.ha.namenodes.hacluster</name> <value>nn1,nn2</value> </property> <!-- nn1 的 RPC 和 HTTP 通信地址 --> <property> <name>dfs.namenode.rpc-address.hacluster.nn1</name> <value>hadoop01:8020</value> </property> <property> <name>dfs.namenode.http-address.hacluster.nn1</name> <value>hadoop01:9870</value> </property> <!-- nn2 的 RPC 和 HTTP 通信地址 --> <property> <name>dfs.namenode.rpc-address.hacluster.nn2</name> <value>hadoop02:8020</value> </property> <property> <name>dfs.namenode.http-address.hacluster.nn2</name> <value>hadoop02:9870</value> </property> <!-- JournalNode 共享 edits 目录 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://hadoop03:8485;hadoop04:8485;hadoop05:8485/hacluster</value> </property> <!-- 客户端自动故障转移代理 --> <property> <name>dfs.client.failover.proxy.provider.hacluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <!-- 隔离方法,先 ssh 到旧节点强制杀掉进程 --> <property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <property> <name>fs.ssh.connect-timeout</name> <value>30000</value> </property> <!-- 每个 JournalNode 存储 edit log 的目录 --> <property> <name>dfs.journalnode.edits.dir</name> <value>/data/hadoop/journal</value> </property> <!-- 允许 HDFS 自动故障转移 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> </configuration>

关于sshfence多说一句:它要求执行 failover 的节点能免密 SSH 到目标 NameNode,并且有权限 kill 进程。如果无法配置 root 免密,可以换成shell(/bin/true)这种宽松的隔离方式,但这就等于放弃了强隔离,生产环境不推荐。

4.4 yarn-site.xml:ResourceManager 高可用配置

<configuration> <!-- 启用 RM HA --> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.cluster-id</name> <value>mycluster</value> </property> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <!-- rm1 配置 --> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>hadoop01</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>hadoop02</value> </property> <!-- 客户端访问 RM 的地址,HA 前走实际机器,HA 后走逻辑地址 --> <property> <name>yarn.resourcemanager.address.rm1</name> <value>hadoop01:8032</value> </property> <property> <name>yarn.resourcemanager.address.rm2</name> <value>hadoop02:8032</value> </property> <!-- Web UI 端口 --> <property> <name>yarn.resourcemanager.webapp.address.rm1</name> <value>hadoop01:8088</value> </property> <property> <name>yarn.resourcemanager.webapp.address.rm2</name> <value>hadoop02:8088</value> </property> <!-- RM 依赖 ZooKeeper 完成选主 --> <property> <name>yarn.resourcemanager.zk-address</name> <value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value> </property> <!-- 开启状态恢复,切换后新 RM 能接管旧状态 --> <property> <name>yarn.resourcemanager.recovery.enabled</name> <value>true</value> </property> <!-- 启用 NodeManager 的辅助服务,MapReduce 的 shuffle 依赖它 --> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <!-- 日志聚合,配合历史服务器查看日志 --> <property> <name>yarn.log-aggregation-enable</name> <value>true</value> </property> <property> <name>yarn.log-aggregation.retain-seconds</name> <value>604800</value> </property> </configuration>

这里注意,yarn.resourcemanager.cluster-id在同一个 HA 组内必须一致,否则两个 RM 无法确认彼此属于同一集群。另外,NodeManager 的 aux-services 不配置的话,MapReduce 的 Map 输出无法传输给 Reduce,任务会卡在 shuffle 阶段。

4.5 mapred-site.xml:MapReduce 框架与历史服务器

<configuration> <!-- 指定 MR 运行在 yarn 上 --> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <!-- 历史服务器地址,用于查询已完成的作业 --> <property> <name>mapreduce.jobhistory.address</name> <value>hadoop04:10020</value> </property> <!-- 历史服务器 Web UI --> <property> <name>mapreduce.jobhistory.webapp.address</name> <value>hadoop04:19888</value> </property> </configuration>

历史服务器在这套方案里单独放在 hadoop04,是为了让主控节点轻量化。如果你机器紧张,放在 hadoop02 上也完全可以,只是要注意别让所有核心服务挤在一台。

4.6 workers 文件:声明 DataNode 部署范围

$HADOOP_HOME/etc/hadoop/workers文件用于指定哪些节点启动 DataNode:

hadoop03 hadoop04 hadoop05

这里不要写 hadoop01 和 hadoop02,因为它们专职做 NameNode 和 ResourceManager。当然,如果你机器少想额外增加数据节点,也可以把 01、02 加进来当 DataNode,但生产环境通常会让主控节点和存储节点分离。

5. 集群启动的完整顺序:格式化、同步元数据与常见首启报错

5.1 启动顺序为什么不能乱

第一次启动 HA 集群最容易出错的部分不是配置,而是“顺序”。我见过很多人直接跑start-dfs.sh,结果 NameNode 格式化的元数据没同步、JournalNode 为空、ZKFC 没起来,报错五花八门。

正确的首次启动顺序是:

  1. 启动 ZooKeeper 集群(3 台);
  2. 在 3 台 JournalNode 节点分别启动 journalnode;
  3. 在 hadoop01 执行hdfs namenode -format格式化主 NameNode;
  4. 在 hadoop02 用hdfs namenode -bootstrapStandby同步元数据;
  5. 启动 hadoop01 的 NameNode 和两台 NameNode 节点上的 ZKFC;
  6. 启动 DataNode;
  7. 启动 YARN 组件(RM、NodeManager);
  8. 启动历史服务器。

5.2 JournalNode 与首个 NameNode 格式化

在 hadoop03、hadoop04、hadoop05 上分别执行:

hdfs --daemon start journalnode

用 jps 检查,进程里应当看到JournalNode。然后回到 hadoop01,执行格式化命令:

hdfs namenode -format

格式化完成后,检查/data/hadoop/tmp下是否生成了dfs/name和dfs/nameservice1之类目录,确认元数据落盘。

一个新手最容易犯的错:格式化完成后,直接对 hadoop02 也执行hdfs namenode -format。这会生成一个新的、独立的 namespace,两个 Namenode 的 clusterId 不一样,后面同步一定会失败。正确做法是用bootstrapStandby从主节点拉取元数据。

5.3 bootstrapStandby 把元数据同步到备用节点

在 hadoop02 上执行:

hdfs namenode -bootstrapStandby

该命令会从shared.edits.dir的 JournalNode 集群读取命名空间状态,并把它保存在本地的 dfs/name 目录中。执行之前务必确保 hadoop01 已经完成 format,并且 JournalNode 已经启动,否则它会提示找不到可用的 edit log。

如果 bootstrapStandby 时报错提示storage directory already contains an unknown storage type,多半是你之前曾手动格式化过 hadoop02。解决方法:把/data/hadoop/tmp目录清空后重新执行。

5.4 启动 NameNode 与 ZKFC,然后拉起整个集群

在 hadoop01 上启动 Active 侧的 NameNode 进程:

hdfs --daemon start namenode

然后在 hadoop01 和 hadoop02 分别启动 ZKFC:

hdfs --daemon start zkfc

ZKFC 启动后会立刻连接 ZooKeeper,并尝试给对应 NameNode 注册锁。此时在 hadoop02 上也可以直接把 Standby NameNode 拉起来,或者直接执行完整的脚本一次到位:

# 在 hadoop01 上执行 start-dfs.sh

脚本会读取 workers 文件启动所有 DataNode,同时启动 JournalNode 和 ZKFC(如果还没启动)。启动完成后,用 jps 检查各节点进程:

  • hadoop01:NameNode、DFSZKFailoverController、ResourceManager(下一步)、QuorumPeerMain
  • hadoop02:NameNode、DFSZKFailoverController、ResourceManager(下一步)、QuorumPeerMain
  • hadoop03/04/05:DataNode、JournalNode、QuorumPeerMain

在这里我要特意强调一个高发问题:如果 start-dfs.sh 执行时报“Cannot connect to journal node”或类似的连接 JournalNode 错误,绝大多数是 JournalNode 没起全或防火墙没关。先逐台 jps 确认三个 JournalNode 都在,再排查防火墙,不要一上来就怀疑配置写错。

5.5 启动 YARN 与历史服务器

接着在 hadoop01 上执行:

start-yarn.sh

脚本会读取 yarn-site.xml 中的 RM 配置,在 hadoop01 和 hadoop02 分别启动 rm1 和 rm2,并读取 workers 文件启动 NodeManager。

最后在 hadoop04 上启动历史服务器:

mapred --daemon start historyserver

全部启动完成后,一个完整的 HA 集群就站起来了。接下来进入验证环节,这一步千万别省。

6. 高可用验证与手动故障切换:检验 HA 不是“配了就完事”

6.1 进程和端口层面的检查清单

走到这一步,你已经完成了 90% 的工作,但还差关键的验证。我列一张检查表,你可以照着逐项打勾:

检查项命令/地址预期结果
HDFS 主备状态hdfs haadmin -getAllServiceState一个 active,一个 standby
YARN RM 状态yarn rmadmin -getServiceState rm1输出 active/standby
Web UI 主 NameNodehttp://hadoop01:9870正常显示,且有 Standby 提示
Web UI RMhttp://hadoop01:8088显示 Active 的 RM 节点
历史服务器 UIhttp://hadoop04:19888正常打开
DataNode 数量hdfs dfsadmin -report3 个 DataNode 可见

hdfs haadmin -getAllServiceState是检查 HA 是否生效的最直观手段:

[hdfs@hadoop01 ~]$ hdfs haadmin -getAllServiceState hadoop01:8020 active hadoop02:8020 standby

6.2 手动故障切换演练:kill 掉 Active 进程

配置了 automatic-failover 之后,理论上无需人工干预,但我强烈建议你主动演练一次故障切换,确认 ZKFC 能正常接管。方案如下:

  1. 在 hadoop01 上 kill 掉 Active NameNode 进程:
kill -9 $(jps | grep NameNode | awk '{print $1}')
  1. 等待大约 30 秒(取决于 ZKFC 会话超时时间),再查询状态:
hdfs haadmin -getAllServiceState

正常情况下 hadoop02 的 NameNode 会自动从 standby 变成 active。也可以在故障切换后用hdfs dfs -ls /测试读写是否正常。

  1. 手动触发的另一种方式是通过命令在不 kill 进程的情况下切换:
hdfs haadmin -failover hadoop01 hadoop02

这个命令会把 Active 身份强制交给 hadoop02。注意:执行 failover 前,务必确保 hadoop01 真的有能力退出 Active,否则脑裂风险会让共享编辑日志写入冲突。

6.3 客户端行为与 ZKFC 超时参数

这里要补充一个生产经验:自动切换的时效性与 ZKFC 的 session timeout 强相关。如果 ZooKeeper 的tickTime是默认 2000ms,ZKFC 默认超时大约是 10 秒左右。测试时你会发现切换并不算“秒级”,大概要十几秒;这个正常,不用怀疑配置。

但如果你发现切换后客户端仍然一直报“Connection refused”,请检查客户端是否配置了ipc.client.connect.timeout和故障代理重试次数。默认的重试次数可能不够,导致客户端在第一次连接失败后直接异常退出。我通常会在 core-site.xml 里补上:

<property> <name>ipc.client.connect.max.retries.on.timeouts</name> <value>10</value> </property>

这样在 HA 切换的几十秒窗口内,客户端不会直接崩溃。

6.4 ResourceManager 的切换验证

RM 的切换验证相对简单,kill 掉 hadoop01 的 ResourceManager 进程:

kill -9 $(jps | grep ResourceManager | awk '{print $1}')

然后查询:

yarn rmadmin -getServiceState rm2

如果 hadoop02 的 RM 显示 active,说明 RM HA 正常。之后如果你再跑一个 MapReduce 作业,可以看到任务能正常提交和完成。

7. 历史服务器与日志聚合:排障时最难少的辅助设施

7.1 为什么历史服务器在 HA 集群里那么重要

很多新手搭完 Hadoop 就完事,觉得历史服务器可有可无。但实际使用中,散落在各个 NodeManager 上的日志几乎是排查失败任务的第一手资料,没有历史服务器,你只能 ssh 到每一台节点上翻 yarn 聚合目录,费时又痛苦。

历史服务器解决两个问题:

  • 作业跑完后,作业详情可以在 Web UI 上回看(跑完的 MR 任务在 ResourceManager 页面会从“RUNNING”转为“History”,点击进去能看到 Map/Reduce 各阶段的指标);
  • 日志集中归档。配合yarn.log-aggregation-enable=true,容器日志会上传到 HDFS 特定目录,通过历史服务器 Web UI 直接浏览,不再需要逐台登录。

7.2 历史服务器配置与验证

历史服务器只有三个关键配置,前面 mapred-site.xml 已经写好了mapreduce.jobhistory.address和mapreduce.jobhistory.webapp.address。启动方式:

mapred --daemon start historyserver

确认进程存在后,打开http://hadoop04:19888就能看到历史任务页面。之后随便跑一个测试任务,比如自带 wordcount 或用 Pi 算法验证:

hadoop jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.4.jar pi 4 1000

任务跑完后,去历史服务器 UI 看这个作业的记录是否存在。如果看到“Application History”里能点开作业,说明整套链路通了。

7.3 一个容易漏掉的细节:MapReduce 作业的历史数据存储在 fs.defaultFS

历史服务器默认会把完成的作业归档到 HDFS 的/tmp/hadoop-yarn/staging/history/下。在 HA 集群里,它依赖fs.defaultFS指向 HDFS,所以历史服务器要能读取hdfs://hacluster的元数据。如果历史服务器启动时报无法访问 HDFS,多半是 core-site.xml 里fs.defaultFS没有配置成 HA 地址,或者没有把这份配置同步到 hadoop04。

另一个常见问题:跑完的任务在历史服务器页面显示“UNKNOWN”,这是 YARN 的 Timeline Server 和 History Server 之间状态不同步导致的。最简单的规避方案是配置yarn.timeline-service.enabled=false,或者接受这种显示差异——它不影响任务实际结果查看。

8. 运维中踩过的坑:从“起不来”到“悄悄切主”

8.1 不可重复格式化:元数据一致性是红线

在 HA 集群里,“格式化”这件事只能做一次。如果因为某种原因想推倒重来,必须把所有节点的dfs/name、dfs/journal、/data/hadoop/journal目录全部清空,同时清空 ZooKeeper 中 Hadoop 相关的 znodes,否则会出现两个 NameNode 各持一个 clusterId 的情况,状态看起来是 active/standby,实际上根本无法同步 edits 日志。

我同事第一次搭 HA 时就是没清 ZooKeeper 里残留的 HA 状态,执行start-dfs.sh后 ZKFC 反复把 NameNode 踢来踢去,日志里全是 “Failover failed”。最后我把客户端连接到 ZooKeeper,删除/hadoop-ha/hacluster整个目录,重新初始化才算平息。

8.2 “双向免密”不到位导致的 fencing 失败

sshfence 这种隔离方式依赖 SSH 登录对方机器执行 fuser。如果你只配置了主节点到备用节点的单向免密,当备用节点需要隔离主节点时,就会卡在“no passphrase”或“Host key verification failed”。这个问题很隐蔽,因为第一次启动主备切换可能正好不需要隔离,等到真正触发 failover 时才爆出来。

解决方式:

# 在两台 NameNode 之间 hadoop01 -> hadoop02 免密 hadoop02 -> hadoop01 免密

并且在/etc/ssh/sshd_config中允许 root 登录(或者用部署用户免密),同时把 StrictHostKeyChecking 设置为 no,避免首次 SSH 询问确认导致脚本超时。

8.3 JournalNode 说崩就崩:先看磁盘和文件句柄

JournalNode 进程挂掉的原因里,磁盘写满占比最大。edit log 是连续写入的,如果/data/hadoop/journal所在分区满了,JournalNode 会直接异常退出,紧接着 NameNode 报 “Unable to write edits to journal”。日常运维建议盯三个指标:

  • 分区使用率,控制在 80% 以下;
  • 文件句柄数,ulimit -n调到 65535 以上;
  • 系统时间偏差,建议启用 NTP 同步,偏差超过 1 秒就可能引发同步异常。

8.4 切主之后的“僵尸 ResourceManager”现象

还有一种故障模式比较隐蔽:Active RM 被 kill 或网络分区后,备用 RM 成功接管,但旧 RM 其实没死透。两个 RM 同时认为自己是 Active,会导致客户端随机连接到一个“假主”,提交任务后卡住不执行。

这个场景我建议你直接看 YARN 日志,定位是否出现Duplicate RMID或Unable to elect active。根本解法是配置yarn.resourcemanager.auto-recovery和 ZooKeeper 的隔离超时,并严格保证 RM 进程被 kill 干净。在做故障演练时,一定要 kill 进程而不是直接断网——断网会让新旧 RM 同时“存活”,最容易诱发脑裂。

8.5 日常巡检:三个命令判断集群健康度

集群搭好后,不要等出故障再爬起来看。我在日常运维里固定保持三个巡检命令:

hdfs haadmin -getAllServiceState # 查看 NN 主备状态 yarn rmadmin -getServiceState rm1 # 查看 RM 状态 hdfs dfsadmin -report # 查看 DataNode 数量和容量

如果某一天你发现 HA 集群“一切正常但任务跑得极慢”,先看看是不是有节点 JVM 内存打满。NameNode 的内存通常给 8G~16G,DataNode 给 4G~8G,这些都要在hadoop-env.sh里显式设置,默认参数在真实负载下顶不住。

9. 最后分享一点个人建议:把集群搭完后的“验证脚本”固化下来

如果你照着前面步骤走,正常半天到一天就能让 Hadoop 3.1.4 的完全分布式 HA 集群跑起来。但我最想提醒你的是:搭起来只是第一步,把“验证手段”固化成习惯才是长期收益。

我现在每搭完一套 HA 集群,都会做三件事:

  1. 写一个一屏的快捷检查脚本,把第 6 节的检查项全部跑一遍,输出状态摘要;
  2. 主动做一次hdfs haadmin -failover切换演练,确认隔离机制确实能杀干净旧节点;
  3. 在/data/hadoop下留一份当次部署的配置版本备份,改任何参数前先cp一份快照。

这套集群能不能在生产环境扛住,关键不在配置有多漂亮,而在于你平时对“主备是否一致、日志是否正常滚动、磁盘是否够用”这三件事心里有没有数。每次出问题,先查 ZooKeeper 的选举状态和 JournalNode 的磁盘用量,大部分故障都能很快定位。希望你这次搭建少踩坑,多跑几年不宕机。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 11:42:35

Kafka基础架构全解析:从Broker到消费组,生产环境避坑指南

做了这么多年消息中间件&#xff0c;见过太多团队把 Kafka 用成"性能很好的MySQL"——数据是塞进去了&#xff0c;但一遇到消费堆积、分区倾斜、重启后重复消费&#xff0c;就抓瞎。其实这些问题九成以上都能回溯到对基础架构的理解偏差上。Kafka 这套架构设计出来就…

作者头像 李华
网站建设 2026/10/17 11:42:22

从零搭建 artcraft 创意工具站:Canvas 图像处理与性能优化实战

1. 从零搭建一个 artcraft 创意工具站&#xff1a;我的完整踩坑记录第一次看到 artcraft 这个词&#xff0c;脑子里蹦出来的画面是“把艺术和手艺揉在一起”。后来真正动手做一个 artcraft 项目&#xff0c;才发现它远不止字面意思那么简单。简单说&#xff0c;artcraft 是一个…

作者头像 李华
网站建设 2026/10/17 11:42:18

USB转232驱动安装指南:识别芯片、解决COM口问题

简介&#xff1a;串口通信是工业控制与嵌入式开发的基础技术&#xff0c;而USB转RS232接口是实现现代电脑与老式串口设备互联的桥梁。其核心在于一颗转换芯片与对应的驱动程序&#xff0c;驱动负责将USB枚举映射为虚拟COM口。选择合适的驱动需先识别芯片方案&#xff08;如CH34…

作者头像 李华
网站建设 2026/10/17 11:40:44

Flutter入门实战:状态管理、网络请求、路由与主题切换全解析

前两篇笔记里&#xff0c;我已经把 Flutter 的基础跑通了&#xff1a;环境怎么搭、Widget 怎么套、布局怎么写、几个常用组件怎么用。但真到自己动手做一个完整的小应用时&#xff0c;我发现事情没这么简单。比如列表页的数据从哪来&#xff0c;详情页的参数怎么传&#xff0c;…

作者头像 李华
网站建设 2026/10/17 11:39:35

LexiLaw中文法律大模型:本地部署与推理优化实战

简介&#xff1a;LexiLaw 是一套面向法律从业者、法学院学生及对法律知识有需求的普通用户的中文法律大模型资源&#xff0c;基于 ChatGLM-6B 架构在法律领域数据集上微调而成&#xff0c;可用于法律咨询、条款解读、案例解析与法规查询等场景&#xff0c;帮助读者理解大模型在…

作者头像 李华
网站建设 2026/10/17 11:39:19

Flutter跨端开发实战:在OpenHarmony上构建维修状态看板

1. 项目概述1.1 能做什么&#xff1a;维修车间里的“数字仪表盘”先说结论&#xff1a;这个项目&#xff0c;是给汽车维修门店/售后车间做的一个跨端“状态总览屏”。它解决的核心问题&#xff0c;是让前台接待、车间主管、维修技师都能在一块屏幕上随时掌握每辆在修车辆的实时…

作者头像 李华