序言
- 在Hadoop 2.X以前的版本,NameNode面临单点故障风险(SPOF),也就是说,一旦NameNode节点挂了,整个集群就不可用了,而且需要借助辅助NameNode来手工干预重启集群,这将延长集群的停机时间。
- Hadoop 2.X版本只支持一个备用节点用于自动恢复NameNode故障,即HDFS 支持一主一备的架构
- Hadoop 3.X版本则支持多个备用NameNode节点,最多支持 5 个,官方推荐使用 3 个
基于Hadoop3.x. 总的来说就是要借助Zookeeper来实现高可用,然后就是编辑Hadoop的配置文件已实现高可用cuiyaonan2000@163.com
High Available Of HDFS
总体架构图如下所示
Hadoop 实现自动故障切换需要用到下面的组件:
- ZooKeeper
- ZKFailoverController 进程(ZKFC)
ZooKeeper
ZooKeeper quorum 是一种集中式服务,主要为分布式应用提供协调、配置、命名空间等功能。它提供组服务和数据同步服务,它让客户端可以实时感知数据的更改,并跟踪客户端故障。HDFS故障自动切换的实现依赖下面两个方面:
-
故障监测:ZooKeeper维护一个和NameNode之间的会话。如果NameNode发生故障,该会话就会过期,会话一旦失效了,ZooKeeper将通知其他NameNode启动故障切换进程。
-
活动NameNode选举:ZooKeeper提供了一种活动节点选举机制。只要活动的NameNode发生故障失效了,其他NameNode将从ZooKeeper获取一个排它锁,并把自身声明为活动的NameNode。
ZKFailoverController(ZKFC)
ZKFC 是 ZooKeeper 的监控和管理 namenode 的一个客户端。所以每个运行 namenode 的机器上都会有 ZKFC。
那ZKFC具体作用是什么?主要有以下3点:
状态监控:ZKFC 会定期用 ping 命令监测活动的 NameNode,如果 NameNode 不能及时响应ping 命令,那么 ZooKeeper 就会判断该活动的 NameNode 已经发生故障了。
ZooKeeper会话管理:如果 NameNode 是正常的,那么它和 ZooKeeper 会保持一个会话,并持有一个 znode 锁。如果会话失效了,那么该锁将自动释放。
基于ZooKeeper的选举:如果 NameNode 是正常的,ZKFC 知道当前没有其他节点持有 znode 锁,那么 ZKFC 自己会试图获取该锁,如果锁获取成功,那么它将赢得选举,并负责故障切换工作。这里的故障切换过程其实和手动故障切换过程是类似的;先把之前活动的节点进行隔离,然后把 ZKFC 所在的机器变成活动的节点。
要求
- 如此来讲NameNode之间不会有直接的交互,NameNode只通过ZKFC跟ZooKeeper连接,以此来保证可用性,
- 各个NameNode会实时监控JournalNode,查看是否有新的变化,如果有就自动更新到自己的环境中,以此来保证一致性.cuiyaonan2000@163.com
NameNode服务器:运行NameNode的服务器应该有相同的硬件配置。
JournalNode服务器:运行的JournalNode进程非常轻量,可以部署在其他的服务器上。注意:必须允许至少3个节点。当然可以运行更多,但是必须是奇数个,如3、5、7、9个等等。
当运行N个节点时,系统可以容忍至少(N-1)/2(N至少为3)个节点失败而不影响正常运行。
在HA集群中,standby状态的NameNode可以完成checkpoint操作,因此没必要配置Secondary NameNode、CheckpointNode、BackupNode。如果真的配置了,还会报错。
配置
core-site.xml
<!-- 指定 NameNode 的地址 单节点
<property>
<name>fs.defaultFS</name>
<value>hdfs://centos1:8020</value>
</property>
-->
<!-- Namenode高可用配置-自定义集群名称,且不用指定端口号 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<!-- 指定 hadoop 数据的存储目录,最大的作用就是可以被其他地方引用这个公用的开头路径,比如hdfs.xml中就用到了 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/opt/hadoop3.2/data</value>
</property>
<!-- 配置ZKFC进程连接zookeeper的地址 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>centos1:2181,centos2:2181,centos3:2181</value>
</property>
<!--如下的内容可以不用配置cuiyaonan2000@163.com -->
<!-- 配置 HDFS 网页登录使用的静态用户为 bigdata -->
<property>
<name>hadoop.http.staticuser.user</name>
<value>bigdata</value>
</property>
<!--置超级代理-->
<property>
<name>hadoop.proxyuser.bigdata.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.bigdata.groups</name>
<value>*</value>
</property>
hdfs-site.xml
<!--
nn web 端访问地址
<property>
<name>dfs.namenode.http-address</name>
<value>centos1:9870</value>
</property>
2nn web 端访问地址
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>centos3:9868</value>
</property>
-->
<!-- namenode服务逻辑id ,注意跟core.xml中的名称要一致-->
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<!-- namenode服务mycluster下3个节点 -->
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2,nn3</value>
</property>
<!-- 节点通讯地址 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>centos1:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>centos2:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn3</name>
<value>centos3:8020</value>
</property>
<!-- web ui地址 -->
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>centos1:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>centos2:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn3</name>
<value>centos3:9870</value>
</property>
<!-- journalnode edits读取写入地址 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://centos1:8485;centos2:8485;centos3:8485/mycluster</value>
</property>
<!-- the Java class that HDFS clients use to contact the Active NameNode -->
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- a list of scripts or Java classes which will be used to fence the Active NameNode during a failover
这个目前我感觉其实是不需要的,等后面验证吧cuiyaonan2000@163.com
-->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
<value>shell(/bin/true)</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/bigdata/.ssh/id_rsa</value>
</property>
<property>
<name>dfs.ha.nn.not-become-active-in-safemode</name>
<value>true</value>
</property>
<!-- 故障情况自动切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- Namenode 数据存储目录-->
<property>
<name>dfs.namenode.name.dir</name>
<value>${hadoop.tmp.dir}/name</value>
</property>
<!-- Datanode 数据存储目录-->
<property>
<name>dfs.namenode.data.dir</name>
<value>${hadoop.tmp.dir}/data</value>
</property>
<!-- journalnode 数据存储目录-->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>${hadoop.tmp.dir}/jn</value>
</property>
yarn-site.xml
yarn也支持高可用,同时依赖于zookeeper
<!-- 指定 ResourceManager 的地址 单节点 -->
<!--
<property>
<name>yarn.resourcemanager.hostname</name>
<value>centos2</value>
</property>
-->
<!-- 指定 MR 走 shuffle -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!-- 环境变量的继承 这段我觉得可以不用弄-->
<property>
<name>yarn.nodemanager.env-whitelist</name>
<value>
JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
</property>
<!-- hadoop classpath输出以下路径 这段我觉得可以不用弄 -->
<property>
<name>yarn.application.classpath</name>
<value>
/opt/hadoop3.2/etc/hadoop:/opt/hadoop3.2/share/hadoop/common/lib/*:/opt/hadoop3.2/share/hadoop/common/*:/opt/hadoop3.2/share/hadoop/hdfs:/opt/hadoop3.2/share/hadoop/hdfs/lib/*:/opt/hadoop3.2/share/hadoop/hdfs/*:/opt/hadoop3.2/share/hadoop/mapreduce/lib/*:/opt/hadoop3.2/share/hadoop/mapreduce/*:/opt/hadoop3.2/share/hadoop/yarn:/opt/hadoop3.2/share/hadoop/yarn/lib/*:/opt/hadoop3.2/share/hadoop/yarn/*</value>
</property>
<!-- 开启resourcemanager HA-->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 自定义一个resourcemanager的逻辑集群id-->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>yarn-cluster</value>
</property>
<!-- 指定resourcemanager集群的逻辑节点名称列表-->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2,rm3</value>
</property>
<!-- rm1的节点信息-->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>centos1</value>
</property>
<!-- yarn web页面地址 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm1</name>
<value>centos1:8088</value>
</property>
<!-- rm1 对客户端暴露的地址,客户端通过该地址向RM提交任务等 -->
<property>
<name>yarn.resourcemanager.address.rm1</name>
<value>centos1:8032</value>
</property>
<!-- rm1 与 applicationMaster的通信地址 -->
<property>
<name>yarn.resourcemanager.scheduler.address.rm1</name>
<value>centos1:8030</value>
</property>
<!-- rm1 与 nm的通信地址 -->
<property>
<name>yarn.resourcemanager.resource-tracker.address.rm1</name>
<value>centos1:8031</value>
</property>
<!-- rm2的节点信息-->
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>centos2</value>
</property>
<!-- yarn web页面地址 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm2</name>
<value>centos2:8088</value>
</property>
<!-- rm2 对客户端暴露的地址,客户端通过该地址向RM提交任务等 -->
<property>
<name>yarn.resourcemanager.address.rm2</name>
<value>centos2:8032</value>
</property>
<!-- rm2 与 applicationMaster的通信地址 -->
<property>
<name>yarn.resourcemanager.scheduler.address.rm2</name>
<value>centos2:8030</value>
</property>
<!-- rm2 与 nm的通信地址 -->
<property>
<name>yarn.resourcemanager.resource-tracker.address.rm2</name>
<value>centos2:8031</value>
</property>
<!-- rm3的节点信息-->
<property>
<name>yarn.resourcemanager.hostname.rm3</name>
<value>centos3</value>
</property>
<!-- yarn web页面地址 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm3</name>
<value>centos3:8088</value>
</property>
<property>
<name>yarn.resourcemanager.address.rm3</name>
<value>centos3:8032</value>
</property>
<!-- rm3 与 applicationMaster的通信地址 -->
<property>
<name>yarn.resourcemanager.scheduler.address.rm3</name>
<value>centos3:8030</value>
</property>
<!-- rm3 与 nm的通信地址 -->
<property>
<name>yarn.resourcemanager.resource-tracker.address.rm3</name>
<value>centos3:8031</value>
</property>
<!-- 配置zookeeper信息 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>centos1:2181,centos2:2181,centos3:2181</value>
</property>
<!-- 启动自动恢复 -->
<property>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>true</value>
</property>
<!-- 配置将recourcemanager的状态信息存储在zookeeper中 -->
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
</property>
<!-- 开启日志聚集功能 -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<!-- 设置日志聚集服务器地址 -->
<property>
<name>yarn.log.server.url</name>
<value>http://centos1:19888/jobhistory/logs</value>
</property>
<!-- 设置日志保留时间为 7 天 -->
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>
mapred-site.xml
这个配置文件没什么变动
<!-- 指定 MapReduce 程序运行在 Yarn 上 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<!-- 历史服务器端地址 -->
<property>
<name>mapreduce.jobhistory.address</name>
<value>centos1:10020</value>
</property>
<!-- 历史服务器 web 端地址 -->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>centos1:19888</value>
</property>
workers------从主节点上启动,会去启动这里面包含的子节点。
这里增加工作节点的ip或者机器名,如:
centos1
centos2
centos3
启动
启动zookeeper集群
初始化ZKFC
ZKFC用于监控active namenode节点是否挂掉,通知其它节点上的ZKFC强行杀死自己ZKFC节点上的namenode(防止其假死状态产生集群namenode脑裂的发生),然后选举出其他namenode为active节点。首次在主节点执行
shell ${HADOOP_HOME}/bin/hdfs zkfc -formatZK
启动journalnode进程
每个节点执行
${HADOOP_HOME}/bin/hdfs --daemon start journalnode
启动namenode
主节点执行
/bin/hdfs namenode -format
/bin/hdfs --daemon start namenode
其它节点执行
/bin/hdfs namenode -bootstrapStandby
验证
测试HDFS高可用
kill -9 active namenode进程,查看页面状态,可发现另外某个namenode自动切换成active状态。
验证YARN高可用
访问任意resourcemanager节点的8088都会跳转到固定的一个resourcemanager节点上,说明高可用配置成功。