HDFS高可用(HA)之ZKFC详解 | 青训营笔记

846 阅读8分钟

这是我参与「第四届青训营 」笔记创作活动的第14天。

前言

对于NameNode的高可用可以简单分为共享editLog机制和ZKFC对NameNode状态的控制 这篇文章的重点放在ZKFC对于Namenode的控制上。

部署示意图

c493483c1bfb52f56b4ba27580dc7b4.jpg

image.png

FC是要和NN一一对应的,两个NN就要部署两个FC。它负责监控NN的状态,并及时的把状态信息写入ZK。它通过一个独立线程周期性的调用NN上的一个特定接口来获取NN的健康状态。FC也有选择谁作为Active NN的权利,因为最多只有两个节点,目前选择策略还比较简单(先到先得,轮换)。

基本概念

首先我们要明确ZKFC 是什么,有什么作用:

zkfc是什么? ZooKeeperFailoverController

它是什么?是Hadoop中通过ZK实现FC功能的一个实用工具。

主要作用:作为一个ZK集群的客户端,用来监控NN的状态信息。和 ZK、NN 通信,进行 NN 探活和自动主备切换。

谁会用它?每个运行NN的节点必须要运行一个zkfc。一般和 NN 部署在一起的进程,负责定时查询 NN 存活和状态、进行 ZK 侧主备选举、执行调用 NN 接口执行集群的主备状态切换、执行 fence 等能力。

  • ZooKeeper:开源的分布式协调组件,主要功能有节点注册、主节点选举、元数据存储。
  • fence 机制:在新 active NN 上主并正式处理请求之前,先要确保旧 active NN 已经退出主节点的状态。一般做法是先用 RPC 状态检测,发现超时或失败则调用系统命令杀死旧 active NN 的进程。
  • Watch 机制:允许多个 client 一起监听一个 znode 的状态变更,并在状态变化时收到一条消息回调(callback)。

ZKFC的功能

  1. Health monitoring
    zkfc定期对本地的NN发起health-check的命令,如果NN正确返回,那么这个NN被认为是OK的。否则被认为是失效节点。

  2. ZooKeeper Session Management
    当本地NN是健康的时候,zkfc将会在zk中持有一个session。如果本地NN又正好是active的,那么zkfc还有持有一个”ephemeral”的节点作为锁,一旦本地NN失效了,那么这个节点将会被自动删除。

  3. ZooKeeper-based election
    如果本地NN是健康的,并且zkfc发现没有其他的NN持有那个独占锁。那么他将试图去获取该锁,一旦成功,那么它就需要执行Failover,然后成为active的NN节点。Failover的过程是:第一步,对之前的NN执行fence,如果需要的话。第二步,将本地NN转换到active状态。

另外: 如果一个Active因HealthMonitor监控到状态异常,这里会作出判断,先通过Fencing功能关闭它(确保关闭或者不能提供服务),然后在ZK上删除它对应ZNode。

发送上述事件后,在另外一台机器上的ZKFC中的ActiveStandbyElector 会收到事件,并重新进行选举(尝试创建特定ZNode),它将获得成功并更改NN中状态,从而实现Active节点的变更。

基本原理

zk的基本特性: (1) 可靠存储小量数据且提供强一致性 (2) ephemeral node(创建的锁节点), 在创建它的客户端关闭后,可以自动删除 (3) 对于node状态的变化,可以提供异步的通知(watcher)

zk在zkfc中可以提供的功能: (1) Failure detector(通过watcher监听机制实现): 及时发现出故障的NN,并通知zkfc (2) Active node locator: 帮助客户端定位哪个是Active的NN (3) Mutual exclusion of active state(通过加锁): 保证某一时刻只有一个Active的NN

主要模块

(1) ZKFailoverController(DFSZKFailoverController): 驱动整个ZKFC的运转,通过向HealthMonitor和ActiveStandbyElector注册回调函数的方式,subscribe HealthMonitor和ActiveStandbyElector的事件,并做相应的处理

(2)HealthMonitor: 定期check NN的健康状况,在NN健康状况发生变化时,通过回调函数把变化通知给ZKFailoverController

(3) ActiveStandbyElector: 管理NN在zookeeper上的状态,zookeeper上对应node的结点发生变化时,通过回调函数把变化通知给ZKFailoverController

(4) FailoverController: 提供做graceful failover的相关功能(dfs admin可以通过命令行工具手工发起failover)

现阶段,HealthMonitorActiveStandbyElectorFailoverController上述三个组件都在跑在一个JVM中,这个JVM与NN的JVM在同一个机器上。但是是两个独立的进程。一个典型的HA集群,有两个NN组成,每个NN都有自己的ZKFC进程。 image.png

1. HealthMonitor设计

HealthMonitor由HADOOP-7788完成提交,它由一个loop循环的调用一个monitorHealth rpc来检视本地的NN的健康性。如果NN返回的状态信息发生变化,那么它将经由callback的方式向ZKFC发送message。HealthMonitor具有以下状态:

    1.  INITIALIZING:HealthMonitor已经初始化好,但是仍未与NN进行联通
    2.  SERVICE NOT RESPONDING:rpc调用要么timeout,要么返回值未定义。
    3.  SERVICE HEALTHY:RPC调用返回成功
    4.  SERVICE UNHEALTHY:RPC放好事先已经定义好的失败类型
    5.  HEALTH MONITOR FAILED:HealthMonitor由于未捕获的异常导致失败。

2. ActiveStandbyElector设计

ActiveStandbyElector(在hadoop - 7992中提交并在hadoop - 8163,hadoop - 8212中改进)负责协调ZooKeeper。ZKFC主要以两种主要的方式进行互动:

joinElection(…) -- 通知ASE,本地的NN可以被选为活动NN。
quitElection(…) -- 通知ASE,本地的NN不能被选为活动NN(eg: because its health has gone bad)。
  1. 一旦ZKFC调用了joinElection,那么ASE将试图获取ZK中的lock。该锁由一个临时的znode组成,如果ZKFC进程崩溃或节点失去网络连接,它将自动删除。如果ASE成功的创建了该锁,那么它向ZKFC调用becomeActive(),否则调用becameStandby()并且开始监控其他节点的锁。
  2. 在当前lock-holder失效的情况下,另一个监控在这个lock上的ZKFC将被触发,然后试图获取这个lock。如果成功,ASE将同样的调用becomeActive方法来通知ZKFC
  3. 如果ZK的session过期,那么ASE将在本地NN上调用enterNeutralMode而不是调用becomeStandby。因为它无法知道另一个NN是否已经准备好接管。这种情况下,将本地NN转移到Standby状态是由fencing机制来完成。

系统架构

如上图所示,通常情况下Namenode和ZKFC同布署在同一台物理机器上, HealthMonitor, FailoverController, ActiveStandbyElector在同一个JVM进程中(即ZKFC), Namenode是一个单独的JVM进程。如上图所示,ZKFC在整个系统中有几个重要的作用: (1) Monitor and try to take active lock: 向zookeeper抢锁,抢锁成功的zkfc,指导对应的NN成为active的NN; watch锁对应的znode,当前active NN的状态发生变化导致失锁时,及时抢锁,努力成为active NN (2) Monitor NN liveness and health: 定期检查对应NN的状态, 当NN状态发生变化时,及时通过ZKFC做相应的处理 (3) Fences other NN when needed: 当前NN要成为active NN时,需要fence其它的NN,不能同时有多个active NN

ZKFC的状态机图

image.png

线程模型

ZKFC的线程模型总体上来讲比较简单的,它主要包括三类线程,一是主线程;一是HealthMonitor线程; 一是zookeeper客户端的线程。它们的主要工作方式是: (1) 主线程在启动所有的服务后就开始循环等待 (2) HealthMonitor是一个单独的线程,它定期向NN发包,检查NN的健康状况 (3) 当NN的状态发生变化时,HealthMonitor线程会回调ZKFailoverController注册进来的回调函数,通知ZKFailoverController NN的状态发生了变化 (4) ZKFailoverController收到通知后,会调用ActiveStandbyElector的API,来管理在zookeeper上的结点的状态 (5) ActiveStandbyElector会调用zookeeper客户端API监控zookeeper上结点的状态,发生变化时,回调ZKFailoverController的回调函数,通知ZKFailoverController,做出相应的变化

类关系图

image.png

示例场景

1.  ActiveNN产生JVM crash

    -   一旦这种情况发生,HealthMonitor在调用monitorHealth()将失效。然后HM将向ZKFC调用enterState(SERVICE_NOT_RESPONDING),本地ZKFC将退出Election,另外一个ZKFC获取active lock,执行fencing,变成active。

1.  ActiveNN JVM freeze(e.g sigstop)

    -   如果JVM freeze了但是没有crash掉,这与上面情况一直,monitorHealth会由于timeout而引发上述过程。FUTURE-WORK:使用JVMTI来判断NN是否在进行gc,如此可以使用另一个timeout来为gc进行failover。

1.  ActiveNN machine crash

    -   当整个机器crash了,ASE在zk的session将会过期,另一个ZKFC将会获取这个事件,引发failover。

1.  Active ZKFC crash

    -   尽管ZKFC设计简单,但是仍然有可能会crash掉,在这个情况下,failover将会被错误的触发。另一个NN的ZKFC将会对ActiveNN调用transitionToStandby让它放弃active lock,然后进行aggressive fencing。尽管会成功,但是会导致进行了一次没有必要的failover。

1.  Zookeeper crash

    1.  当zk集群crash了,那么所有的ZKFC将收到DISCONNECTED事件。然后ZKFC在本地NN调用enterNeutralMode,除此之外不做任何改变。系统除了不能执行failover之外,与其他情况无异。
    2.  当zk恢复了,clients立马能够重连。而zk能够将之前的session信息重新被各个client进行获取(在启动的一个timeou时间内)。所以所有的nodes将会重新获取session,不需要进行无必要的failover。
    3.  FutureWork:breadcrumb znode在这个情况下可以优先的给予到ActiveNN,在ZK挂掉之前。