vlambda博客
学习文章列表

HDFS的NameNode高可用架构和原理详解(NameNode HA)

背景

在Hadoop 1.x的时候,NameNode存在单点故障问题。如果NameNode进程或者所在的机器有故障,会导致整个集群不可用,直到NameNode进程重启或者所在的机器恢复。在hadoop 2.x之后,增加了NameNode的HA机制。即在一个HDFS集群中运行两个NameNode节点,一个是Active状态的,一个是Standby状态的。当Active状态的NameNode挂掉后,Standby状态的NameNode会切换成Active状态。

NameNode的HA架构

HDFS NameNode的HA架构如下图所示:

涉及到几个主要角色如下:

Active NameNode: 与Standby NameNode形成互备,只有处于Active状态的NameNode节点才能对外提供读写服务。Standby NameNode: 承接原来SecondaryNameNode的checkpoint功能。Standby NameNode从JN拉取edit log,合并到自己的fsimage上。在Active NameNode故障时,Standby会切换成Active状态。JournalNode: 必须奇数个节点(3,5,7...),至少3个节点。当有N个JN时,可以允许(N-1)/2个NameNode发生故障。Active NameNode发送edit log到JN的绝大部分节点上。ZKFailoverController:ZKFC作为独立的进程运行,对NameNode的主备切换进行总体控制。每个运行NameNode的机器上,都需要同时运行一个ZKFC。ZKFC定期监测它本机的NameNode的健康状态,会与Zookeeper之间维护一个session,当本机的NameNode是Active状态时,会把某个znode“加锁”(创建znode)。如果session过期,这个znode会被删除。当其他ZKFC看到这个znode不存在,会去请求“加锁”(创建znode),如果成功“加锁”,也就是所谓的赢得了选举(won the election),它所在机器上的NameNode成为了Active状态。当然,NameNode也支持不依赖Zookeeper的手动主备切换。DataNode: 同时向Active NameNode和Standby NameNode上报数据块位置信息和心跳包。Zookeeper:ZKFC和Zookeeper之间维护了一个session,如果NameNode挂掉了,会使session失效,进而导致Zookeeper上保存的lock znode被删除,而ZKFC就是通过这个znode来进行Active NameNode选举。

手动NameNode主备切换

进行一次手动NameNode主备切换,观察Zookeeper上znode的变化,印证一下上面讲的ZKFailoverController的原理。CDH上当前Active NameNode在ctkf02上,在Cloudera Manager上停止该Active NameNode,然后会发现ctkf01上的Standby NameNode切换成了Active状态。Zookeeper上znode的变化如下:

zookeeper-clientget /hadoop-ha/nameservice1/ActiveStandbyElectorLock
HDFS的NameNode高可用架构和原理详解(NameNode HA)

Quorum Journal Manager(QJM)

QJM集群由奇数个JournalNode节点组成。当Active NameNode中有事务提交,Active NameNode会将edit log发给JournalNode集群,JournalNode集群通过paxos协议保证数据一致性(即:超过一半以上的JournalNode节点确认),这个数据就提交到了共享存储。Standby NameNode定期从JournalNode读取edit log,合并到自己的fsimage上。

HDFS的NameNode高可用架构和原理详解(NameNode HA)

处于Standby状态的NameNode转换为Active状态的时候,有可能上一个Active NameNode发生了异常退出,那么 JournalNode集群中各个JournalNode上的edit log就可能会处于不一致的状态,所以首先要做的事情就是让 JournalNode集群中各个节点上的edit log恢复为一致。另外如前所述,当前处于Standby状态的NameNode的内存中的文件系统镜像有很大的可能是落后于旧的Active NameNode的,所以在JournalNode集群中各个节点上的edit log达成一致之后,接下来要做的事情就是从JournalNode集群上拉取补齐落后的edit log。只有在这两步完成之后,当前新的Active NameNode才能安全地对外提供服务。

Quorum Journal Manager(QJM)内部实现

HDFS的NameNode高可用架构和原理详解(NameNode HA)

FSEditLog:这个类封装了对edit log的所有操作,是NameNode对edit log的所有操作的入口。JournalSet:这个类封装了对本地磁盘和JournalNode集群上的edit log的操作,内部包含了两类 JournalManager,一类为FileJournalManager,用于实现对本地磁盘上edit log的操作。一类为QuorumJournalManager,用于实现对JournalNode集群上共享目录的edit log的操作。FSEditLog只会调用 JournalSet的相关方法,而不会直接使用FileJournalManager和QuorumJournalManager。FileJournalManager:封装了对本地磁盘上的edit log文件的操作,不仅NameNode在向本地磁盘上写入edit log的时候使用FileJournalManager,JournalNode在向本地磁盘写入edit log的时候也复用了 FileJournalManager的代码和逻辑。QuorumJournalManager:封装了对JournalNode集群上edit log的操作,它会根据JournalNode集群的URI创建负责与JournalNode集群通信的类AsyncLoggerSet,QuorumJournalManager通过AsyncLoggerSet来实现对JournalNode集群上edit log的写操作。对于读操作,QuorumJournalManager则是通过HTTP接口从JournalNode上的JournalNodeHttpServer读取edit log的数据。AsyncLoggerSet:内部包含了与JournalNode集群进行通信的AsyncLogger列表,每一个AsyncLogger对应于一个JournalNode节点,另外AsyncLoggerSet也包含了用于等待大多数JournalNode返回结果的工具类方法给QuorumJournalManager使用。AsyncLogger:具体的实现类是IPCLoggerChannel,IPCLoggerChannel在执行方法调用的时候,会把调用提交到一个单线程的线程池之中,由线程池线程来负责向对应的JournalNode的JournalNodeRpcServer发送RPC请求。JournalNodeRpcServer:运行在JournalNode节点进程中的RPC服务,接收NameNode端的AsyncLogger的RPC请求。JournalNodeHttpServer:运行在JournalNode节点进程中的Http服务,用于接收处于Standby状态的NameNode和其它JournalNode的同步edit log文件流的请求。

NameNode的切换流程

HDFS的NameNode高可用架构和原理详解(NameNode HA)

NameNode的切换流程分为以下几个步骤:

HealthMonitor初始化完成之后会启动内部的线程来定时调用对应NameNode的HAServiceProtocol RPC接口的方法,对NameNode的健康状态进行检测。HealthMonitor如果检测到NameNode的健康状态发生变化,会回调ZKFailoverController注册的相应方法进行处理。如果ZKFailoverController判断需要进行主备切换,会首先使用ActiveStandbyElector来进行自动的主备选举。ActiveStandbyElector与Zookeeper进行交互完成自动的主备选举。ActiveStandbyElector在主备选举完成后,会回调ZKFailoverController的相应方法来通知当前的NameNode成为主NameNode或备NameNode。ZKFailoverController调用对应NameNode的HAServiceProtocol RPC接口的方法将NameNode转换为Active状态或Standby状态。

Fence

当使用QJM方式时,只允许一个NameNode往JournalNode写edit log,这样可以避免出现脑裂现象。当发生NameNode主备切换时,为了防止出现老NameNode僵死但还在写JournalNode的情况,需要引入fence的概念。有两种fence方法,分别是shell和sshfence。shell的方法,是通过shell命令来对Active NameNode进行fence。而sshfence的方法,是通过SSH连接到Active NameNode,然后杀掉NameNode进程。用户也可以通过org.apache.hadoop.ha.NodeFencer来实现自己的fence方法。

ZKFC的设计

jira HDFS-2185记录了ZKFC的设计。https://issues.apache.org/jira/browse/HDFS-2185

参考文档

https://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.htmlhttps://docs.cloudera.com/documentation/enterprise/5-16-x/topics/cdh_hag_hdfs_ha_intro.htmlhttps://docs.cloudera.com/HDPDocuments/HDP2/HDP-2.6.5/bk_hadoop-high-availability/content/ha-nn-config-cluster.html

引用链接

[1] jwldata.com: https://www.jwldata.com