HDFS NameNode的高可用 面试版

NameNode HA架构图

核心组件

NameNode 主备切换主要由 ZKFailoverController、HealthMonitor 和 ActiveStandbyElector 这 3 个组件来协同实现:

  1. ZKFailoverController 作为 NameNode 机器上一个独立的进程启动 (在 hdfs 启动脚本之中的进程名为 zkfc),启动的时候会创建 HealthMonitor 和 ActiveStandbyElector 这两个主要的内部组件,ZKFailoverController 在创建 HealthMonitor 和 ActiveStandbyElector 的同时,也会向 HealthMonitor 和 ActiveStandbyElector 注册相应的回调方法。
  2. HealthMonitor 主要负责检测 NameNode 的健康状态,如果检测到 NameNode 的状态发生变化,会回调 ZKFailoverController 的相应方法进行自动的主备选举。(zk 的选举过程)
  3. ActiveStandbyElector 主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑,一旦 Zookeeper 主备选举完成,会回调 ZKFailoverController 的相应方法来进行 NameNode 的主备状态切换。

 

基于 QJM 的共享存储系统

HDFS使用 QJM(Quorum Journal Manager) 作为默认的共享存储实现。

      基于 QJM 的共享存储系统主要用于保存 EditLog,并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。QJM 共享存储的基本思想来自于 Paxos 算法,采用多个称为 JournalNode 的节点组成的 JournalNode 集群来存储 EditLog。每个 JournalNode 保存同样的 EditLog 副本。每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉。

        当 NameNode 进入 Standby 状态之后,会启动一个 EditLogTailer 线程。这个线程会定期调用 EditLogTailer 类的 doTailEdits 方法从 JournalNode 集群上同步 EditLog,然后把同步的 EditLog 回放到内存之中的文件系统镜像上 (并不会同时把 EditLog 写入到本地磁盘上)。

        处于 Standby 状态的 NameNode 转换为 Active 状态的时候,有可能上一个 Active NameNode 发生了异常退出,那么 JournalNode 集群中各个 JournalNode 上的 EditLog 就可能会处于不一致的状态,所以首先要做的事情就是让 JournalNode 集群中各个节点上的 EditLog 恢复为一致,然后从 JournalNode 集群上补齐落后的 EditLog。只有在这两步完成之后,当前新的 Active NameNode 才能安全地对外提供服务。

 

主备选举

Namenode(包括 YARN ResourceManager) 的主备选举是通过 ActiveStandbyElector 来完成的,ActiveStandbyElector 主要是利用了 Zookeeper 的写一致性和临时节点机制,具体的主备选举实现如下:

临时节点:/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock

持久节点:/hadoop−ha/{dfs.nameservices}/ActiveBreadCrumb

 

1. 创建锁节点

如果 HealthMonitor 检测到对应的 NameNode 的状态正常,那么表示这个 NameNode 有资格参加 Zookeeper 的主备选举。如果目前还没有进行过主备选举的话,那么相应的 ActiveStandbyElector 就会发起一次主备选举,尝试在 Zookeeper 上创建一个临时节点。Zookeeper 的写一致性会保证最终只会有一个 ActiveStandbyElector 创建成功,那么创建成功的 ActiveStandbyElector 对应的 NameNode 就会成为主 NameNode,ActiveStandbyElector 会回调 ZKFailoverController 的方法进一步将对应的 NameNode 切换为 Active 状态。而创建失败的 ActiveStandbyElector 对应的 NameNode 成为备NameNode,ActiveStandbyElector 会回调 ZKFailoverController 的方法进一步将对应的 NameNode 切换为 Standby 状态。

2. 注册 Watcher 监听

不管创建临时节点是否成功,ActiveStandbyElector 随后都会向 Zookeeper 注册一个 Watcher 来监听这个节点的状态变化事件,ActiveStandbyElector 主要关注这个节点的 NodeDeleted 事件

 

3. 自动触发主备选举

如果 ActiveNameNode 对应的 HealthMonitor 检测到 NameNode 的状态异常时, ZKFailoverController 会主动删除当前在 Zookeeper 上建立的临时节点,这样处于 Standby 状态的 NameNode 的 ActiveStandbyElector 注册的监听器就会收到这个节点的NodeDeleted 事件。收到这个事件之后,会马上再次进入到创建临时节点的流程,如果创建成功,这个本来处于 Standby 状态的 NameNode 就选举为主 NameNode 并随后开始切换为 Active 状态。

当然,如果是 Active 状态的 NameNode 所在的机器整个宕掉的话,那么根据 Zookeeper 的临时节点特性,临时节点会自动被删除,从而也会自动进行一次主备切换。

 

脑裂现象

ActiveNameNode 因为网络故障等原因失去了与 Zookeeper 的通信,导致集群选举出新的 ActiveNameNode,随后旧的 ActiveNameNode 重新上线,但是仍然认为自己是 Active,集群中出现两个 ActiveNameNode,这就是脑裂现象。Zookeeper 社区对这种问题的解决方法叫做 fencing。

 

 

隔离机制

为了预防脑裂的情况,HDFS提供了三个级别的隔离(fencing)机制。

■     共享存储隔离:同一时间只允许一个Namenode向JoumalNodes写入editlog数据。

■     客户端隔离:同一时间只允许一个Namenode响应客户端请求。

■     Datanode隔离:同一时间只允许一个Namenode向Datanode下发指令,例 如删除、复制数据块指令等。

 

ActiveStandbyElector为了实现fencing隔离机制,在成功创建临时节点后,会创建另外一个持久节点,这个持久节点保存了ActiveNameNode的地址信息。当ActiveNameNode在正常的状态下断开Zookeeper Session ,会一起删除临时节点和持久节点。但是如果ActiveStandbyElector在异常的状态下关闭Zookeeper Session,那么持久节点会一直保留下来。当另一个NameNode(standy => active)选主成功之后,会注意到上一个Active NameNode遗留下来的持久节点,从而会回调ZKFailoverController的方法对旧的Active NameNode进行fencing。

① 首先ZKFC会尝试调用旧ActiveNameNode的HAServiceProtocol RPC接口的transitionToStandby方法,看能否将状态切换为Standby;

② 如果调用transitionToStandby方法切换状态失败,那么就需要执行Hadoop自带的隔离措施,Hadoop目前主要提供两种隔离措施:

   sshfence:SSH to the Active NameNode and kill the process;

   shellfence:run an arbitrary shell command to fence the Active NameNode;

其中 sshfence通过远程登录到目标节点,利用kill杀死进程。如果sshfence 失败,则会执行用户自定义的shell 命令终止目标节点完成隔离。

只有在成功地执行完成fencing之后,选主成功的ActiveStandbyElector才会回调ZKFCbecomeActive方法将对应的NameNode切换为Active,开始对外提供服务。

 

参考资料

Hadoop NameNode 高可用 (High Availability) 实现解析

https://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-name-node/index.html

NameNode HA实现原理

https://www.jianshu.com/p/8a6cc2d72062