HDFS中的NameNode的HA怎么实现?(一言以蔽之)
在Hadoop集群中配置并启动两个NameNode进程,一个作为Active节点对外提供服务,另一个作为Standby的节点,两个NameNode启动的时候都会在Zookeeper中创建一个临时有序的节点,Zookeeper会取最小的节点对应的NameNode作为Active,而其他的作为Standby。一旦Active节点宕掉了,那么这个NameNode在Zookeeper中对应的临时节点就删除了,那么Standby对应的Zookeeper的临时节点就成为最小的节点,所以它自动的成为Active节点对外提供服务。HA集群中使用某种方式来实现两个NameNode之间的数据共享(其实就是NameNode中的edits log文件
实现NameNode的HA有两种方式:第一种方式是journal node + Zookeeper。另一种方式是NFS + Zookeeper
两种方式的不同点就在于同步两个NameNode之间数据的方式不一样而已,NFS + Zookeeper是通过NFS(Network File System)远程共享目录的方式同步两个NameNode之间的数据,而journal node + Zookeeper是通过journal node来实现两个NameNode之间edits数据的同步

HA概述

1)所谓HA(High Available),即高可用(7*24小时不中断服务)。

2)实现高可用最关键的策略是消除单点故障。HA严格来说应该分成各个组件的HA机制:HDFS的HA和YARN的HA。

3)Hadoop2.0之前,在HDFS集群中NameNode存在单点故障(SPOF)。

4)NameNode主要在以下两个方面影响HDFS集群

NameNode机器发生意外,如宕机,集群将无法使用,直到管理员重启

NameNode机器需要升级,包括软件、硬件升级,此时集群也将无法使用

HDFS HA功能通过配置Active/Standby两个NameNodes实现在集群中对NameNode的热备来解决上述问题。如果出现故障,如机器崩溃或机器需要升级维护,这时可通过此种方式将NameNode很快的切换到另外一台机器。

HDFS-HA工作机制

通过双NameNode消除单点故障

HDFS-HA工作要点

1.    元数据管理方式需要改变

内存中各自保存一份元数据;

Edits日志只有Active状态的NameNode节点可以做写操作;

两个NameNode都可以读取Edits;

共享的Edits放在一个共享存储中管理(qjournal和NFS两个主流实现);

2.    需要一个状态管理功能模块

实现了一个zkfailover,常驻在每一个namenode所在的节点,每一个zkfailover负责监控自己所在NameNode节点,利用zk进行状态标识,当需要进行状态切换时,由zkfailover来负责切换,切换时需要防止brain split现象的发生。

3.    必须保证两个NameNode之间能够ssh无密码登录

4.    隔离(Fence),即同一时刻仅仅有一个NameNode对外提供服务

HDFS-HA自动故障转移工作机制

前面学习了使用命令hdfs haadmin -failover手动进行故障转移,在该模式下,即使现役NameNode已经失效,系统也不会自动从现役NameNode转移到待机NameNode,下面学习如何配置部署HA自动进行故障转移。自动故障转移为HDFS部署增加了两个新组件:ZooKeeper和ZKFailoverController(ZKFC)进程,如图3-20所示。ZooKeeper是维护少量协调数据,通知客户端这些数据的改变和监视客户端故障的高可用服务。HA的自动故障转移依赖于ZooKeeper的以下功能:

1)故障检测:集群中的每个NameNode在ZooKeeper中维护了一个持久会话,如果机器崩溃,ZooKeeper中的会话将终止,ZooKeeper通知另一个NameNode需要触发故障转移。

2)现役NameNode选择:ZooKeeper提供了一个简单的机制用于唯一的选择一个节点为active状态。如果目前现役NameNode崩溃,另一个节点可能从ZooKeeper获得特殊的排外锁以表明它应该成为现役NameNode。

ZKFC是自动故障转移中的另一个新组件,是ZooKeeper的客户端,也监视和管理NameNode的状态。每个运行NameNode的主机也运行了一个ZKFC进程,ZKFC负责:

1)健康监测:ZKFC使用一个健康检查命令定期地ping与之在相同主机的NameNode,只要该NameNode及时地回复健康状态,ZKFC认为该节点是健康的。如果该节点崩溃,冻结或进入不健康状态,健康监测器标识该节点为非健康的。

2)ZooKeeper会话管理:当本地NameNode是健康的,ZKFC保持一个在ZooKeeper中打开的会话。如果本地NameNode处于active状态,ZKFC也保持一个特殊的znode锁,该锁使用了ZooKeeper对短暂节点的支持,如果会话终止,锁节点将自动删除。

3)基于ZooKeeper的选择:如果本地NameNode是健康的,且ZKFC发现没有其它的节点当前持有znode锁,它将为自己获取该锁。如果成功,则它已经赢得了选择,并负责运行故障转移进程以使它的本地NameNode为Active。故障转移进程与前面描述的手动故障转移相似,首先如果必要保护之前的现役NameNode,然后本地NameNode转换为Active状态。

在Hadoop 1.x 中,Namenode是集群的单点故障,一旦Namenode出现故障,整个集群将不可用,重启或者开启一个新的Namenode才能够从中恢复。值得一提的是,Secondary Namenode并没有提供故障转移的能力。集群的可用性受到影响表现在:

  • 当机器发生故障,如断电时,管理员必须重启Namenode才能恢复可用。
  • 在日常的维护升级中,需要停止Namenode,也会导致集群一段时间不可用。

架构

Hadoop HA(High Available)通过同时配置两个处于Active/Passive模式的Namenode来解决上述问题,分别叫Active Namenode和Standby Namenode. Standby Namenode作为热备份,从而允许在机器发生故障时能够快速进行故障转移,同时在日常维护的时候使用优雅的方式进行Namenode切换。Namenode只能配置一主一备,不能多于两个Namenode。

主Namenode处理所有的操作请求(读写),而Standby只是作为slave,维护尽可能同步的状态,使得故障时能够快速切换到Standby。为了使Standby Namenode与Active Namenode数据保持同步,两个Namenode都与一组Journal Node进行通信。当主Namenode进行任务的namespace操作时,都会确保持久会修改日志到Journal Node节点中的大部分。Standby Namenode持续监控这些edit,当监测到变化时,将这些修改应用到自己的namespace。

当进行故障转移时,Standby在成为Active Namenode之前,会确保自己已经读取了Journal Node中的所有edit日志,从而保持数据状态与故障发生前一致。

为了确保故障转移能够快速完成,Standby Namenode需要维护最新的Block位置信息,即每个Block副本存放在集群中的哪些节点上。为了达到这一点,Datanode同时配置主备两个Namenode,并同时发送Block报告和心跳到两台Namenode。

确保任何时刻只有一个Namenode处于Active状态非常重要,否则可能出现数据丢失或者数据损坏。当两台Namenode都认为自己的Active Namenode时,会同时尝试写入数据(不会再去检测和同步数据)。为了防止这种脑裂现象,Journal Nodes只允许一个Namenode写入数据,内部通过维护epoch数来控制,从而安全地进行故障转移。

有两种方式可以进行edit log共享:

  • 使用NFS共享edit log(存储在NAS/SAN)
  • 使用QJM共享edit log

使用NFS共享存储

如图所示,NFS作为主备Namenode的共享存储。这种方案可能会出现脑裂(split-brain),即两个节点都认为自己是主Namenode并尝试向edit log写入数据,这可能会导致数据损坏。通过配置fencin脚本来解决这个问题,fencing脚本用于:

  • 将之前的Namenode关机
  • 禁止之前的Namenode继续访问共享的edit log文件

使用这种方案,管理员就可以手工触发Namenode切换,然后进行升级维护。但这种方式存在以下问题:

  • 只能手动进行故障转移,每次故障都要求管理员采取措施切换。
  • NAS/SAN设置部署复杂,容易出错,且NAS本身是单点故障。
  • Fencing 很复杂,经常会配置错误。
  • 无法解决意外(unplanned)事故,如硬件或者软件故障

因此需要另一种方式来处理这些问题:

  • 自动故障转移(引入ZooKeeper达到自动化)
  • 移除对外界软件硬件的依赖(NAS/SAN)
  • 同时解决意外事故及日常维护导致的不可用

Quorum-based 存储 + ZooKeeper

QJM(Quorum Journal Manager)是Hadoop专门为Namenode共享存储开发的组件。其集群运行一组Journal Node,每个Journal 节点暴露一个简单的RPC接口,允许Namenode读取和写入数据,数据存放在Journal节点的本地磁盘。当Namenode写入edit log时,它向集群的所有Journal Node发送写入请求,当多数节点回复确认成功写入之后,edit log就认为是成功写入。例如有3个Journal Node,Namenode如果收到来自2个节点的确认消息,则认为写入成功。

而在故障自动转移的处理上,引入了监控Namenode状态的ZookeeperFailController(ZKFC)。ZKFC一般运行在Namenode的宿主机器上,与Zookeeper集群协作完成故障的自动转移。整个集群架构图如下:

QJM

Namenode使用QJM 客户端提供的RPC接口与Namenode进行交互。写入edit log时采用基于仲裁的方式,即数据必须写入JournalNode集群的大部分节点。
在Journal Node节点上(服务端)
服务端Journal运行轻量级的守护进程,暴露RPC接口供客户端调用。实际的edit log数据保存在Journal Node本地磁盘,该路径在配置中使用dfs.journalnode.edits.dir属性指定。
Journal Node通过epoch数来解决脑裂的问题,称为JournalNode fencing。具体工作原理如下:
1)当Namenode变成Active状态时,被分配一个整型的epoch数,这个epoch数是独一无二的,并且比之前所有Namenode持有的epoch number都高。

2)当Namenode向Journal Node发送消息的时候,同时也带上了epoch。当Journal
Node收到消息时,将收到的epoch数与存储在本地的promised
epoch比较,如果收到的epoch比自己的大,则使用收到的epoch更新自己本地的epoch数。如果收到的比本地的epoch小,则拒绝请求。

3)edit log必须写入大部分节点才算成功,也就是其epoch要比大多数节点的epoch高。

这种方式解决了NFS方式的3个问题:

  • 不需要额外的硬件,使用原有的物理机
  • Fencing通过epoch数来控制,避免出错。
  • 自动故障转移:Zookeeper处理该问题。

使用Zookeeper进行自动故障转移

前面提到,为了支持故障转移,Hadoop引入两个新的组件:Zookeeper Quorum和ZKFailoverController process(简称ZKFC)。

Zookeeper的任务包括:

  • 失败检测: 每个Namnode都在ZK中维护一个持久性session,如果Namnode故障,session过期,使用zk的事件机制通知其他Namenode需要故障转移。
  • Namenode选举:如果当前Active namenode挂了,另一个namenode会尝试获取ZK中的一个排它锁,获取这个锁就表名它将成为下一个Active NN。

在每个Namenode守护进程的机器上,同时也会运行一个ZKFC,用于完成以下任务:

  • Namenode健康健康
  • ZK Session管理
  • 基于ZK的Namenode选举

如果ZKFC所在机器的Namenode健康状态良好,并且用于选举的znode锁未被其他节点持有,则ZKFC会尝试获取锁,成功获取这个排它锁就代表获得选举,获得选举之后负责故障转移,如果有必要,会fencing掉之前的namenode使其不可用,然后将自己的namenode切换为Active状态。

部署与配置

硬件资源

为了允许HA集群,需要以下资源:
1)Namenode机器:运行Active Namenode和Standby Namenode的机器配置应保持一样,也与不使用HA情况下的配置一样。
2)JournalNode机器:运行JournalNode的机器,这些守护进程比较轻量级,所以可以将其部署在Namenode或者YARN
ResourceManager。至少需要部署3个Journalnode节点,以便容忍一个节点故障。通常配置成奇数,例如总数为N,则可以容忍(N-1)/2台机器发生故障后集群仍然可以正常工作。

需要注意的是,Standby Namenode同时完成了原来Secondary namenode的checkpoint功能,因此不需要独立再部署Secondary namenode。

HA配置

Nameservices: 服务的逻辑名称

<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>

Namenode配置:
dfs.ha.namenodes.[nameservices]: nameserviecs对应的namenode:

<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value> <!--目前最大只能2个-->
</property>

Namenode RPC地址:

<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>machine1.example.com:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>machine2.example.com:8020</value>
</property>

Namenode HTTP Server配置:

<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>machine1.example.com:50070</value>
</property> <!--如果启用了Hadoop security,需要使用https-address-->
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>machine2.example.com:50070</value>
</property>

edit log保存目录,也就是Journal Node集群地址,分号隔开:

<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster</value>
</property>
 

客户端故障转移代理类,目前只提供了一种实现:

<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>

edit日志保存路径:

<property>
<name>dfs.journalnode.edits.dir</name>
<value>/path/to/journal/node/local/data</value>
</property>

fencing方法配置:

<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property> <property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/exampleuser/.ssh/id_rsa</value>
</property>

虽然在使用QJM作为共享存储时,不会出现同时写入的脑裂现象。但是旧的namenode依然可以接受读请求,这可能会导致数据过时,直到原有namenode尝试写入journal node时才关机。因此也推荐配置一种合适的fencing方法。

部署启动

配置完成之后,使用如下命令启动JQM集群:

hadoop-daemon.sh  start  journalnode

配置并启动Zookeeper集群,与常规的而配置方式完成一样,主要包括数据保存位置、节点id、时间配置等,在zoo.cfg中配置。这里不列出详细步骤。使用之前,需要格式化zk的文件:

hdfs zkfc -formatZK

格式化Namenode:

hdfs  namenode -format

启动两个namenode:

//master
hadoop-daemon.sh start namenode27
//备用namenode上
hdfs namenode -bootstrapStandby

其他组件的启动方式与常规方式一样。

最新文章

  1. C++进程间通信
  2. Java中serialVersionUID的解释及两种生成方式的区别(转载)
  3. 烂泥:python2.7和python3.5源码安装
  4. js⑥
  5. 【Python】实现简单循环
  6. java -json()
  7. java中i=i++字节码分析
  8. [转载]浅谈组策略设置IE受信任站点
  9. Transact-SQL
  10. Android检测是否安装了指定应用
  11. 八大排序方法汇总(选择排序,插入排序-简单插入排序、shell排序,交换排序-冒泡排序、快速排序、堆排序,归并排序,计数排序)
  12. Linux 设置系统时间和日期 API
  13. Nginx 变量漫谈(二)
  14. 关于querySelector 和 document.getElementsByTagName 选中集合问题
  15. Judy Beta 第八天
  16. eclipse 把鼠标指针放在错误的语句上 提示快速修正 不见了的解决方法
  17. 【刷题】LOJ 6226 「网络流 24 题」骑士共存问题
  18. JZYZOJ 2041 快速数论变换 NTT 多项式
  19. Python3 数字保留后几位
  20. 网络 私有IP和子网掩码设置

热门文章

  1. kali无法安装nvidia显卡驱动
  2. JSON -------- json与字符串之间的转换
  3. java ---- 认识类和对象
  4. [转帖]Kubesphere all-in-one 安装方式.
  5. [转帖]kubernetes 常见问题整理
  6. mysql密码中有特殊字符&amp;在命令行下登录
  7. 百度前端技术学院task13源代码
  8. C# 使用SuperSocket
  9. tiny-spring 分析
  10. 2019 拉卡拉java面试笔试题 (含面试题解析)