☰
Hadoop完全分布式+ZooKeeper+NameNode HA+YARN HA集群搭建与高可用验证实战
2026/10/19 17:08:41 网站建设 项目流程

1. 为什么这套组合拳值得每个大数据从业者死磕

刚入行那会儿,我觉得伪分布式跑通了就算入门了,直到第一次面对生产环境的机房断电演练,才发现单点故障有多要命。hadoop完全分布式+zookeeper集群+NameNode HA+yarn HA这套组合,说白了就是把Hadoop从"能跑"推到"敢用"的必经之路。它解决的核心问题只有一个:当关键进程挂掉时,整个集群不能跟着陪葬。

这套方案适合谁?如果你已经能独立搭建伪分布式环境,能看懂core-site.xml和hdfs-site.xml的基本配置,但一提到JournalNode、ZKFC、ResourceManager状态切换就发怵,那这篇内容就是写给你的。我会把每个组件的角色、每个配置项背后的意图、以及我踩过的坑,全部摊开来讲。

先给个全局认知:完全分布式解决的是"数据分散存储和计算"的问题,ZooKeeper解决的是"分布式协调"的问题,NameNode HA解决的是"元数据单点"的问题,YARN HA解决的是"资源调度单点"的问题。四者叠加,才构成一个具备基础高可用能力的大数据平台底座。缺了任何一环,你的集群在真实故障面前都是纸糊的。

我见过太多人把HA配好了,但从来没真正触发过主备切换,也不知道切换后客户端要做什么。这种"配置层面的HA"毫无意义。所以下面我会重点讲怎么验证HA真的生效,以及切换过程中哪些细节会让你前功尽弃。

2. 集群规划与基础环境准备

2.1 节点角色分配与硬件考量

先定节点。我建议至少5台机器,3台用于ZooKeeper和JournalNode,2台作为NameNode和ResourceManager的主备。如果资源紧张,可以压缩到3台,但ZooKeeper必须保持奇数节点,这是Paxos类协议的基本要求。

节点别名角色分配最低配置
node01NameNode(Active)、ResourceManager(Active)、ZKFC、ZK、JN4C8G
node02NameNode(Standby)、ResourceManager(Standby)、ZKFC、ZK、JN4C8G
node03DataNode、NodeManager、ZK、JN2C4G
node04DataNode、NodeManager2C4G
node05DataNode、NodeManager2C4G

注意:JournalNode至少需要3台,且必须是奇数。它存储的是NameNode的EditLog,相当于元数据变更的"账本"。如果JN挂到只剩1台,NameNode会停止写入,整个HDFS变成只读。

硬件上有个容易忽略的点:NameNode对内存的需求远大于DataNode。因为整个文件系统的元数据都加载在内存里,1000万个文件大约消耗3GB堆内存。所以node01和node02的内存不能省,否则GC停顿会让你误以为是HA切换了。

2.2 系统层基础配置

所有节点统一执行以下操作,别偷懒,我见过因为时间不同步导致ZK选主失败的案例。

# 关闭防火墙(内网环境) systemctl stop firewalld && systemctl disable firewalld # 设置主机名,以node01为例 hostnamectl set-hostname node01 # 配置hosts,所有节点保持一致 cat >> /etc/hosts << EOF 192.168.1.101 node01 192.168.1.102 node02 192.168.1.103 node03 192.168.1.104 node04 192.168.1.105 node05 EOF # 时间同步 yum install -y chrony systemctl start chronyd && systemctl enable chronyd # 关闭swap,Hadoop对内存交换极其敏感 swapoff -a sed -i '/swap/s/^/#/' /etc/fstab

JDK选择上,Hadoop 3.x推荐JDK 8或JDK 11。我实测下来JDK 11在G1 GC下表现更稳,但部分老版本Hadoop对JDK 11支持不完整,所以生产环境建议JDK 8,省心。

# 配置JDK环境变量 cat >> /etc/profile << EOF export JAVA_HOME=/usr/local/jdk1.8.0_381 export PATH=$JAVA_HOME/bin:$PATH EOF source /etc/profile

2.3 SSH免密与集群分发脚本

NameNode需要能免密登录到所有节点,这是启动和停止集群的前提。

# 在node01生成密钥对 ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa # 分发公钥到所有节点 for i in {101..105}; do ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.1.$i done

写一个xsync脚本,后面分发配置文件全靠它:

#!/bin/bash if [ $# -lt 1 ]; then echo "Usage: xsync file..." exit 1 fi for host in node01 node02 node03 node04 node05; do if [ "$host" != "$(hostname)" ]; then rsync -av --delete $@ $host:$PWD fi done

提示:rsync的--delete参数会删除目标端多余文件,分发目录时慎用,分发单个文件时没问题。

3. ZooKeeper集群搭建与核心参数调优

3.1 为什么HA离不开ZooKeeper

很多人把ZooKeeper当成"配HA时顺便装的东西",这是误解。在Hadoop HA架构里,ZK承担三个关键职责:Active节点选举、状态存储、故障通知。ZKFC(ZooKeeper Failover Controller)会持续向ZK发送心跳,谁先抢到/hadoop-ha/<nameservice>/ActiveStandbyElectorLock这个临时节点,谁就是Active。一旦Active失联,临时节点自动消失,Standby的ZKFC收到通知后发起新一轮选举。

所以ZK集群的稳定性直接决定HA的可靠性。ZK挂了,HA就瞎了。

3.2 ZooKeeper配置实操

# 解压到/opt/module tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz -C /opt/module/ cd /opt/module && mv apache-zookeeper-3.8.4-bin zookeeper # 创建数据目录和日志目录 mkdir -p /opt/module/zookeeper/zkData mkdir -p /opt/module/zookeeper/zkLogs # 配置zoo.cfg cat > /opt/module/zookeeper/conf/zoo.cfg << EOF tickTime=2000 initLimit=10 syncLimit=5 dataDir=/opt/module/zookeeper/zkData dataLogDir=/opt/module/zookeeper/zkLogs clientPort=2181 maxClientCnxns=60 autopurge.snapRetainCount=3 autopurge.purgeInterval=1 server.1=node01:2888:3888 server.2=node02:2888:3888 server.3=node03:2888:3888 EOF # 在node01上创建myid echo 1 > /opt/module/zookeeper/zkData/myid

tickTime是ZK的基本时间单元,initLimit=10意味着Follower启动时最多等待10个tick(20秒)与Leader同步。如果集群数据量大,这个值要调大,否则Follower会因为超时被踢出。

autopurge这两个参数我强烈建议加上。ZK的snapshot和事务日志会无限增长,我见过一个跑了半年的ZK,dataDir占了80GB,最后磁盘满了导致整个集群不可用。

分发到node02和node03,分别修改myid为2和3:

xsync /opt/module/zookeeper # 在node02执行 echo 2 > /opt/module/zookeeper/zkData/myid # 在node03执行 echo 3 > /opt/module/zookeeper/zkData/myid

3.3 启动验证与常见坑

# 三个节点分别启动 zkServer.sh start # 验证状态 zkServer.sh status # 应该看到一台leader,两台follower

注意:如果三台都显示standalone,说明没读到集群配置,检查zoo.cfg里server.x的IP和myid是否对应。如果一直选不出leader,先看时间是否同步,再看2888/3888端口是否被占用。

我踩过最坑的一次是:dataDir路径写错,ZK在错误路径下创建了myid,导致每台机器都认为自己是1号,选主永远失败。所以启动前一定确认myid文件在dataDir指定的目录下。

4. Hadoop完全分布式与HA配置全流程

4.1 core-site.xml:定义集群身份

<configuration> <!-- 指定HDFS的nameservice,这是HA的核心标识 --> <property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property> <!-- 临时目录,NameNode和DataNode共用 --> <property> <name>hadoop.tmp.dir</name> <value>/opt/module/hadoop/data/tmp</value> </property> <!-- ZooKeeper集群地址,ZKFC和RM都依赖 --> <property> <name>ha.zookeeper.quorum</name> <value>node01:2181,node02:2181,node03:2181</value> </property> </configuration>

fs.defaultFS写mycluster而不是具体主机名,这是HA的关键。客户端拿到这个逻辑名后,会通过dfs.nameservices找到对应的NameNode列表,再逐个尝试连接。如果写死node01,那HA就形同虚设。

4.2 hdfs-site.xml:NameNode HA核心配置

<configuration> <!-- nameservice逻辑名,与core-site对应 --> <property> <name>dfs.nameservices</name> <value>mycluster</value> </property> <!-- 两个NameNode的别名 --> <property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property> <!-- nn1的RPC和HTTP地址 --> <property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>node01:8020</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>node01:9870</value> </property> <!-- nn2的RPC和HTTP地址 --> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>node02:8020</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>node02:9870</value> </property> <!-- JournalNode地址列表 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://node01:8485;node02:8485;node03:8485/mycluster</value> </property> <!-- 故障转移代理类,客户端靠它找到Active --> <property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <!-- 隔离机制,防止脑裂 --> <property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/root/.ssh/id_rsa</value> </property> <!-- 自动故障转移 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> </configuration>

dfs.ha.fencing.methods这个配置极其重要。当ZKFC决定切换时,它必须先确保旧的Active真的死了,否则两个NameNode同时写EditLog会导致数据损坏。sshfence会通过SSH登录到旧Active节点执行kill命令。如果SSH不通,切换会失败,集群进入"无Active"状态。

提示:生产环境建议用shell或自定义fencing脚本,sshfence在SSH服务异常时会成为单点。

4.3 yarn-site.xml:ResourceManager HA配置

<configuration> <!-- 开启RM HA --> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.cluster-id</name> <value>yarn-cluster</value> </property> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>node01</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>node02</value> </property> <!-- ZK存储RM状态 --> <property> <name>yarn.resourcemanager.zk-address</name> <value>node01:2181,node02:2181,node03:2181</value> </property> <property> <name>yarn.resourcemanager.recovery.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.store.class</name> <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value> </property> <!-- NodeManager辅助服务 --> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration>

yarn.resourcemanager.recovery.enabled必须为true,否则RM切换后之前提交的任务状态全丢。ZKRMStateStore把应用状态存在ZK里,新Active RM启动后能从ZK恢复。

4.4 workers与分发配置

# workers文件列出所有DataNode和NodeManager节点 cat > /opt/module/hadoop/etc/hadoop/workers << EOF node03 node04 node05 EOF # 分发Hadoop目录到所有节点 xsync /opt/module/hadoop

5. 集群初始化与HA切换验证

5.1 启动顺序不能乱

启动顺序错了,会出现各种诡异问题。正确顺序是:ZK → JournalNode → NameNode → ZKFC → DataNode → ResourceManager → NodeManager。

# 1. 启动ZK(三个节点分别执行) zkServer.sh start # 2. 启动JournalNode(node01/02/03) hdfs --daemon start journalnode # 3. 格式化HDFS(仅在node01执行一次) hdfs namenode -format # 4. 启动node01的NameNode hdfs --daemon start namenode # 5. 在node02同步元数据 hdfs namenode -bootstrapStandby # 6. 启动node02的NameNode hdfs --daemon start namenode # 7. 格式化ZKFC(仅在node01执行一次) hdfs zkfc -formatZK # 8. 启动ZKFC(node01和node02) hdfs --daemon start zkfc # 9. 启动DataNode(node03/04/05) hdfs --daemon start datanode # 10. 启动YARN start-yarn.sh # node02手动启动RM yarn --daemon start resourcemanager

注意:hdfs zkfc -formatZK只能在第一次搭建时执行。如果重复执行,会清空ZK里的HA状态,导致两个NameNode都变成Standby。

5.2 验证NameNode HA真的生效

别只看Web UI显示Active/Standby就完事,要实际触发一次切换。

# 查看当前状态 hdfs haadmin -getServiceState nn1 hdfs haadmin -getServiceState nn2 # 手动切换 hdfs haadmin -transitionToStandby nn1 hdfs haadmin -transitionToActive nn2 # 验证切换后HDFS仍可读写 hdfs dfs -mkdir /ha-test hdfs dfs -put /etc/profile /ha-test/ hdfs dfs -cat /ha-test/profile | head -5

如果切换后读写正常,说明HA配置成功。如果报Connection refused,检查dfs.client.failover.proxy.provider是否配错。

5.3 验证YARN HA

# 查看RM状态 yarn rmadmin -getServiceState rm1 yarn rmadmin -getServiceState rm2 # 手动切换 yarn rmadmin -transitionToStandby rm1 yarn rmadmin -transitionToActive rm2 # 提交一个任务验证 yarn jar /opt/module/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi 2 4

任务能正常跑完,说明RM切换后状态恢复没问题。

6. 常见故障排查与避坑指南

6.1 HA切换失败排查表

现象可能原因排查方法
两个NN都是StandbyZKFC未启动或ZK连接失败jps查看ZKFC进程,检查ha.zookeeper.quorum
切换后HDFS只读JournalNode不足半数hdfs dfsadmin -report查看JN状态
脑裂(两个Active)fencing未生效检查dfs.ha.fencing.methods和SSH免密
RM切换后任务丢失recovery未开启确认yarn.resourcemanager.recovery.enabled=true
ZKFC反复重启时间不同步chronyc sources检查时间源

6.2 我踩过的三个深坑

第一个坑:hadoop.tmp.dir用了默认值。默认是/tmp/hadoop-${user.name},而/tmp在某些系统上会被定期清理。结果某次重启后,NameNode的元数据全没了。所以一定要改成持久化路径,并且确保该路径在所有节点上存在且权限正确。

第二个坑:JournalNode磁盘写满。JN的EditLog会持续增长,如果没配清理策略,磁盘满了之后NameNode无法写入,整个HDFS挂起。解决方案是监控JN目录大小,或者配置dfs.journalnode.edits.dir到独立的大容量磁盘。

第三个坑:ZKFC的SSH fencing超时。默认SSH连接超时时间较短,网络抖动时fencing失败,切换卡住。可以在dfs.ha.fencing.ssh.connect-timeout里调大超时时间,比如30000毫秒。

6.3 日常运维检查清单

  • 每天检查ZK集群是否有Leader,zkServer.sh status
  • 每周检查JournalNode磁盘使用率,超过70%要清理
  • 每月手动触发一次HA切换,确保fencing和恢复机制正常
  • 关注NameNode的Heap使用率,超过80%要考虑扩容或优化

7. 性能调优与扩展建议

7.1 NameNode JVM调优

NameNode的GC停顿直接影响HA切换速度。我建议用G1 GC,并设置合理的堆大小。

# 在hadoop-env.sh中配置 export HDFS_NAMENODE_OPTS="-Xmx8g -Xms8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+PrintGCDetails -Xloggc:/opt/module/hadoop/logs/nn-gc.log"

-Xmx和-Xms设成一样,避免堆动态扩展带来的抖动。MaxGCPauseMillis=200是目标值,G1会尽量达成,但不是硬保证。

7.2 ZooKeeper会话超时调整

默认tickTime=2000,会话超时是2*tickTime=4秒。在网络不稳定的环境里,4秒太短,容易误判节点死亡。可以在zoo.cfg里加:

maxSessionTimeout=60000 minSessionTimeout=4000

同时在Hadoop侧调整ha.zookeeper.session-timeout.ms,保持两边一致。

7.3 后续扩展方向

这套架构跑稳之后,可以往两个方向扩展:一是加HDFS Federation,用多个nameservice分担元数据压力;二是引入Router-based Federation,实现跨集群的透明访问。不过那是另一个话题了,先把当前这套的HA切换练熟再说。

我个人在实际操作中的体会是:HA配置本身不难,难的是验证它真的能在故障时生效。很多人配完看到Web UI显示Active/Standby就以为万事大吉,结果真出故障时发现fencing没配、ZK连不上、JN磁盘满了。所以我的建议是,每季度至少做一次故障演练,手动kill掉Active NameNode,观察集群是否在30秒内完成切换,客户端是否无感知。这个习惯,比任何配置都重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询