原因
- NameNode是HDFS的核心配置,HDFS又 Hadoop是核心组件, NameNodeHadoop在集群中至关重要
- NameNode宕机,将导致集群不可用,如果 NameNode数据丢失将导致整个集群的数据丢失,而 NameNode的数据的更新又比较频繁,实现 NameNode高可用势在必行
解决方案
官方提供了两种解决方案
- HDES With nes
- HDFS With QJM
两种方案对比
- 都能实现热备
- 都是一个 Active NNStandby和一个 NN
- 都使用 Zookeeper和ZKFC来实现自动失效恢复
- 失效切换都使用 FencinActive配置的方法来active NN
- NFS数据共享变更方案把数据存储在共享存储里,我们还需要考虑NS的高可用设计
- QJM不需要共享存储,但需要让每一个DN都知道两个NN的位置,并把块信息和心跳包发送给 Active和Standby这两个NN
使用QJM
使用原因
- 解决 NameNode单点故障问题
- Hadoop给出了HDFS的高可用HA方案:HDFS通常由两个 NameNode组成,一个处于 Active状态,另一个处于 Standby状态。 Active NameNode对外提供服务,比如 处理来自客户端的RPC请求,而 Standby NameNode则 不对外提供服务,仅同步 Active NameNode的状态,以 便能够在它失败时进行切换
- Namenode会被配置在两台独立的机器上,在任何时 候,一个 NameNode处于活动状态,而另一个 NameNode则处于备份状态
- 活动状态的 NameNode会响应集群中所有的客户端, 备份状态的 Namenode只是作为一个副本,保证在必 要的时候提供一个快速的转移
高可用架构
- 为了让 Standby Node与 Active node保持同步,这两个Node都与一组称为JNS的互相独立的进程保持通信( Journal nodes)。当 Active Node更新了 namespace ,它将记录修改日志发送给JNS的多数派。 Standby Node将从 JNS中读取这些 edits,并持续关注它们对日志的变更
- Standby Node将日志变更应用在自己的 namespace中 ,当 Failover发生时, Standby将会在提升自己为 Active之前确保能够从JNS中读取所有的 edits,即在 Failover发生之前 Standy持有的 namespace与 Active保持完全同步
- NameNode更新很频繁,为了保持主备数据的一致性,为了支持快速Failover,Standby Node持有集群中blocks的最新位置是非常必要的。为了达到这一目的,DataNodes上需要同时配置这两个Namenode的地址,同时和它们都建立心跳连接,并把block位置发送给它们
- 任何时刻,只能有一个Active NameNode,否则会导致集群操作混乱,两个NameNode将会有两种不同的数据状态,可能会导致数据丢失或状态异常,这种情况通常称为"split-brain"(脑裂,三节点通讯阻断,即集群中不同的DataNode看到了不同的ActiveNameNodes)
- 对于JNS而言,任何时候只允许一个NameNode作为writer;在Failover期间,原来的Standby Node将会接管Active的所有职能,并负责向JNS写入日志记录,这种机制阻止了其他NameNode处于Active状态的问题
1)系统规划
主机 | 角色 |
---|---|
192.168.1.60 | NameNode1 |
192.168.1.66 | NameNode2 |
192.168.1.61 | DataNode/journalNode/Zookeeper |
192.168.1.62 | DataNode/journalNode/Zookeeper |
192.168.1.63 | DataNode/journalNode/Zookeeper |
2)配置高可用
- NameNode2配置
- 参考NameNode1
- 其余机器使用之前的配置
- 修改配置文件
[root@nn01 ~] vim /usr/local/hadoop/etc/hadoop/core-site.xml
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
#mycluster是组名,访问的时候访问这个组
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/var/hadoop</value>
</property>
<property>
<name>ha.zookeeper.quorum</name>
<value>node1:2181,node2:2181,node3:2181</value> #zookeepe的地址
</property>
<property>
<name>hadoop.proxyuser.nfs.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.nfs.hosts</name>
<value>*</value>
</property>
</configuration>
[root@nn01 ~] vim /usr/local/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<property>
<name>dfs.ha.namenodes.nsdcluster</name>
#nn1,nn2名称固定,是内置的变量,nsdcluster里面有nn1,nn2
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.nsdcluster.nn1</name>
#声明nn1 8020为通讯端口,是nn01的rpc通讯端口
<value>nn01:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.nsdcluster.nn2</name>
#声明nn2是谁,nn02的rpc通讯端口
<value>nn02:8020</value>
</property>
<property>
<name>dfs.namenode.http-address.nsdcluster.nn1</name>
#nn01的http通讯端口
<value>nn01:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.nsdcluster.nn2</name>
#nn01和nn02的http通讯端口
<value>nn02:50070</value>
</property>
<property>
<name>dfs.namenode.shared.edits.dir</name>
#指定namenode元数据存储在journalnode中的路径
<value>qjournal://node1:8485;node2:8485;node3:8485/nsdcluster</value>
</property>
<property>
<name>dfs.journalnode.edits.dir</name>
#指定journalnode日志文件存储的路径
<value>/var/hadoop/journal</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.nsdcluster</name>
#指定HDFS客户端连接active namenode的java类
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<name>dfs.ha.fencing.methods</name> #配置隔离机制为ssh
<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>
[root@nn01 ~] vim /usr/local/hadoop/etc/hadoop/yarn-site.xml
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<property>
<name>yarn.resourcemanager.ha.rm-ids</name> #rm1,rm2代表nn01和nn02
<value>rm1,rm2</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>
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>node1:2181,node2:2181,node3:2181</value>
</property>
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>yarn-ha</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>nn01</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>nn02</value>
</property>
</configuration>
3)初始化
- 检查zookeeper服务是否可用
- 清理之前的数据信息
rm -rf /var/hadoop
- 清理日志信息
rm -rf /usr/local/hadoop/logs/*
- 同步配置文件到所有主机
- 初始化ZK集群
[root@nn01 ~] /usr/local/hadoop/bin/hdfs zkfc -formatZK
- node启动journalnode
node1,node2,node3:
/usr/local/hadoop/sbin/hadoop-daemon.sh start journalnode
jps #查看角色:JournalNode
- NameNode1格式化hdfs
[root@nn01 ~] /usr/local/hadoop//bin/hdfs namenode -format
- nn02数据同步到本地 /var/hadoop/dfs
[root@nn02 ~] rsync -aXSH --delete nn01:/var/hadoop/ /var/hadoop/
- nn01初始化JNS
[root@nn01 ~] /usr/local/hadoop/bin/hdfs namenode -initializeSharedEdits
- Node节点关闭JournalNode
node1,node2,node3:
/usr/local/hadoop/sbin/hadoop-daemon.sh stop journalnode
4)启动及验证
[root@nn01 ~] /usr/local/hadoop/sbin/start-all.sh
[root@nn02 ~] /usr/local/hadoop/sbin/yarn-daemon.sh start resourcemanager
角色验证:
[root@ansible ~] ansible all -m shell -a 'jps'
node2 | SUCCESS | rc=0 >>
2241 QuorumPeerMain
8322 JournalNode
8226 DataNode
8413 NodeManager
8575 Jps
nn02 | SUCCESS | rc=0 >>
7033 Jps
6731 NameNode
6908 ResourceManager
6830 DFSZKFailoverController
node1 | SUCCESS | rc=0 >>
8388 DataNode
8484 JournalNode
2469 QuorumPeerMain
8733 Jps
8575 NodeManager
node3 | SUCCESS | rc=0 >>
2240 QuorumPeerMain
8321 JournalNode
8225 DataNode
8412 NodeManager
8575 Jps
nn01 | SUCCESS | rc=0 >>
8864 DFSZKFailoverController
9296 Jps
2104 QuorumPeerMain
8968 ResourceManager
8559 NameNode
集群验证:
[root@nn01 ~] /usr/local/hadoop/bin/hdfs dfsadmin -report
Live datanodes (3):
[root@nn01 ~] /usr/local/hadoop/bin/yarn node -list
Total Nodes:3
Node-Id Node-State Node-Http-Address Number-of-Running-Containers
node1:39701 RUNNING node1:8042 0
node3:42280 RUNNING node3:8042 0
node2:40320 RUNNING node2:8042 0
主备验证:
[root@nn01 ~] /usr/local/hadoop/bin/hdfs haadmin -getServiceState nn1
active
[root@nn01 ~] /usr/local/hadoop/bin/hdfs haadmin -getServiceState nn2
standby
[root@nn01 ~] /usr/local/hadoop/bin/yarn rmadmin -getServiceState rm1
active
[root@nn01 ~] /usr/local/hadoop/bin/yarn rmadmin -getServiceState rm2
standby
访问集群文件:
./bin/hadoop fs -mkdir /input
./bin/hadoop fs -ls /
主从切换Active:
[root@nn01 ~] /usr/local/hadoop/sbin/hadoop-daemon.sh stop namenode