在Hadoop中,ZooKeeper主要用于实现HA(High Availability),这部分逻辑主要集中在Hadoop Common的HA模块中,HDFS的NameNode与YARN的ResourceManger都是基于此HA模块中,HDFS的NameNode与YARN的ResourceManger都是基于此HA模块来实现自己的HA功能的。同时,在YARN中又特别提供了ZooKeeper来存储应用的运行状态。
YARN介绍
YARN是Hadoop为了提高计算节点Master(JT)的扩展性,同时为了支持多计算模型和提供资源的细粒度调度而引入的全新一代分布式调度框架。其上可以支持MapReduce计算引擎,也支持其他的一些计算引擎,如Tez、Spark、Storm、Imlala和Open MPI等。其架构体系如下图所示。
从上图中可以看出,YARN主要由ResourceManager(RM)、NodeManager(NM)、ApplicationMaster(AM)和Container四部分组成。其中最为核心的就是ResourceManger,他作为全局的资源管理器,负责整个系统的资源管理和分配。
ResourceManager单点问题
看完YARN的架构体系之后,相信细心的读者也已经看出了上述架构体系中存在的一个明显的缺陷:ResourceManager的单点问题。ResourceManager是YARN中非常复杂的一个组件,负责集群中所有资源的统一管理和分配,同时接收来自各个节点(NodeManager)的资源汇报信息,并把这些信息按照一定的策略分配给各个应用程序(Application Manager),其内部维护了各个应用程序的ApplicationMaster信息、NodeManager信息以及资源使用信息等。因此,ResourceManager的工作状态直接决定了整个YARN框架是否可以正常运转。
ResourceManager HA
为了解决ResourceManager的这个单点问题,YARN设计了一套Active/Standby模式的ResourceManager HA架构,如下图所示。
从上图中可以看出,在运行期间,会有多个ResourceManager并存,并且其中只有一个ResourceManager处于Active状态,另外的一些(允许一个或者多个)则是处于Standby状态,当Active节点无法正常工作(如机器挂掉或重启等)时,其余处于Standby状态的节点则会通过竞争选举产生新的Active节点。
主备切换
下面我们就来看看YARN是如何实现多个ResourceManager之间的主备切换的ResourceManager使用基于ZooKeeper实现的ActiveStandbyElector组件来确定ResourceManager的状态:Active或Standby。具体做法如下。
- 创建锁节点。
在ZooKeeper上会有一个类似于/yarn-leader-election/pseudo-yarn-rm-cluster的锁节点,所有的ResourceManager在启动的时候,都会去竞争写一个Lock子节点:/yarn-leader-election/pseudo-yarn-rm-cluster/ActiveStandbyElectorLock,同时需要注意的是,该子节点的类型是临时节点。ZooKeeper能够为我们保证最终只有一个ResourceManager能够创建成功。创建成功的那个ResourceManager就切换为Active状态,没有成功的那些ResouorceManager则切换为Standby状态。
- 注册Watcher监听。
所有Standby状态的ResourceManager都会向/yarn-leader-election/pseudo-yarn-rm-cluster/ActiveStandbyElectorLock节点注册一个节点变更的Watcher监听,利用临时节点的特性,能够快速感知到Active状态的ResourceManager的运行情况。
- 主备切换。
当Active状态的ResourceManager出现诸如重启或挂掉的异常情况时,其在ZooKeeper上创建的Lock节点也会随之被删除。此时其余各个Standby状态的ResourceManager都会接收到来自ZooKeeper服务端的Watcher事件通知,然后会重复进行步骤1的操作。
以上就是利用ZooKeeper来实现ResourceManager的主备切换的过程。ActiveStandbyElector组件位于Hadoop-Common工程的org.apache.hadoop.ha包中,其封装了ResourceManager和ZooKeeper之间的通信与交互过程,下图展示了ActiveStandbyElector的概要类图。
HDFS中的NameNode和ResourceManager模块都是使用该组件来实现各自的HA的。
Fencing(隔离)
在分布式环境中,经常会出现诸如单机“假死”的情况。所谓的“假死”是指机器由于网络闪断或是其自身由于负载过高(常见的有GC占用时间过长或CPU的负载过高等)而导致无法正常的对外进行及时响应。在上述主备切换过程中,我们假设RM集群由ResourceManager1和ResourceManager2两台机器组成,且ResourceManager1为Active状态,ResourceManager2为Standby状态。某一时刻,ResourceManager1发生了“假死”现象,此时ZooKeeper认为ResourceManger1挂了,根据上述主备切换逻辑,ResourceManager2就会成为Active状态。这就是我们常说的分布式“脑裂”(Brain-Split)现象,即存在了多个处于Active状态的ResourceManger各司其职。那么该如何解决这样的问题呢?
YARN中引入了Fencing机制,借助ZooKeeper数据节点的ACL权限控制机制来实现不同RM之间的隔离。具体做法其实非常简单,在上文的“主备切换”部分中我们讲到,多个RM之间通过竞争创建锁节点来实现主备状态的确定。这个地方需要改进的一点是,创建的根节点必须携带ZooKeeper的ACL信息,目的是为了独占该根节点,以防止其他RM对该节点进行更新。
经过上述改进后,我们再回过头来看,在主备切换过程中,Fencing机制是如何避免“脑裂”现象出现的。延续上述提到的实例,RM1出现假死后,ZooKeeper就会将其创建的锁节点移除掉,此时RM2会创建相应的锁节点,并切换为Active状态。RM1恢复之后,会试图去更新ZooKeeper的相关数据,但是此时发现其没有权限更新ZooKeeper的相关节点数据,也就是说,RM1发现ZooKeeper上的相关节点不是自己创建的,于是就自动切换为Standby状态,这样就避免了“脑裂”现象的出现。
ResourceManger状态存储
在ResourceManger中,RMStateStore能够存储一些RM的内部状态信息,包括Application以及他们的Attempts信息、Delegation Token及Version Information等。需要注意的是,RMStateStore中的绝大多数状态信息都是不需要持久化存储的,因为很容易从上下文信息中将其重构出来,如资源的使用情况。在存储的设计方案中,提供了三种可能的实现,分别如下。
- 基于内存实现,一般是用于日常开发测试。
- 基于文件系统的实现,如HDFS。
- 基于ZooKeeper的实现。
由于这些状态信息的数据量都不是特别大,因此Hadoop官方建议基于ZooKeeper来实现状态信息的存储。在ZooKeeper上,ResourceManger的状态信息都被存储在/rmstore这个根节点下面,其数据节点的组织结构如下图所示。
通过上图我们可以大致了解RMStateStore状态信息在ZooKeeper上的存储结构,其中RMAppRoot节点下存储的是与各个Application相关的信息,RMDTSecretManagerRoot存储的是与安全相关的Token等信息。每个Active状态的ResourceManger在初始化阶段都会从ZooKeeper上读取到这些状态信息,并根据这些状态信息继续进行相应的处理。
小结
ZooKeeper一开始是Hadoop的子项目,因此很多设计之初的原始需求都是为了解决Hadoop系统中碰到的一系列分布式问题。虽然Hadoop的架构几经变迁后,ZooKeeper在Hadoop的使用场景也有所变化,但其出色的分布式协调功能依然是Hadoop解决单点和状态信息存储的重要组件。