HDFS高可用原理
Hadoop1.0在HDFS和MapReduce的高可用、扩展性方面存在问题。
一、Hadoop2.x产生背景
1.HDFS存在的问题:
- Namenode单点故障,难以应用在线场景
- Namenode压力过大,内存受限,影响扩展性
2.MapReduce存在的问题:
- JobTracker访问压力大,影响系统扩展性
- 难以支持除MapReduce之外的计算框架,比如Spark、Storm等
二、Hadoop2.x
- Hadoop2.0对HDFS进行了修复,解决单点故障和内存受限的问题。
- Hadoop2.x仅是架构上发生了变化,使用方式不变
- 对HDFS使用者透明
- HDFS1.x中的命令和API还可以继续使用
①针对单点故障给出的修复方案:HA
HDFS HA高可用:通过主备2个Namenode(3.X上最多5个备,官方推荐3个备。NN太多导致很多数据发送,造成网络压力),主提供服务,备不提供,但是运行着。如果主Namenode发生故障,切换到备Namenode上
②解决内存受限问题:F
HDFS Federation(联邦),水平扩展,支持多个Namenode;同时对外提供服务,分治;
每个Namenode分管一部分目录,所有的Namenode共享所有DataNode存储资源。
三、 Hadoop 2.x-HA
- 1.主备NameNode
- 2.解决单点故障(属性,位置)
- 主NameNode对外提供服务,备NameNode同步主NameNode元数据,以待切换
- 所有DataNode同时向两个NameNode汇报数据块信息(位置)
- JNN:集群(属性)
- standby:备,完成了edits.log文件的合并产生新的image,推送回ANN
- 3.两种切换选择
- 手动切换:通过命令实现主备之间的切换,可以用HDFS升级等场合
- 自动切换:基于Zookeeper实现
- 4.基于Zookeeper自动切换方案
- ZooKeeper Failover Controller:监控NameNode健康状态,并向Zookeeper注册NameNode;NameNode挂掉后,ZKFC为NameNode竞争锁,获得ZKFC 锁的NameNode变为active
现在要解决一个问题,一个集群有1个Namenode,他挂了之后怎么办?引出HA高可用,2个Namenode。一个Namenode挂了,要切换到第2个Namenode,首先要解决数据同步的问题。
1.实现手动HA
先不理会上方的ZKFC,先看这张图的下半部分。有2个NN,分别为Active和Standby。主NN负责和Client交互,备NN负责同步Active上的元数据并随时待命准备切换角色。
Namenode存了两类元数据:客户端要求产生的动态数据,生成目录;Datanode汇报到NN的block位置信息。
那么,Standby怎么同步获取Active上的元数据呢?有以下两种方式可以选择:
- 1阻塞(为了保持数据一致性,丧失可用性)
客户端要求NN Active创建目录,NN Active向NN Standby发送指令创建目录,成功之后standby返回ok给active,active返回ok给客户端。如果standby中途挂掉,后续操作就被阻塞了。 - 2.异步(为了可用性,丧失了一致性)
客户端要求active创建目录,activite向standby传达相同指令。此时,activite不管standby,只要activite它自己创建完成,里面给客户端返回ok。但是standby创建目录的过程中,有可能挂掉
不能为了解决一个问题,从而引入另一个问题!要是想实现数据一致性,就不能使用以上的通信形式,只要保证最终一致性就行。此时,需要借助中间的组件JN。往active写数据,相当于写到了NFS技术,读也是从这里面读。Client往ANN里写入数据,ANN同时还要写入到JN中(2个NN只能ANN往JN写,否则不允许。JN放的就是edits文件,JN可以做到可靠性存储数据,能保证最终一致性。和NFS干的活一样,只不过技术实现不一样。),SNN从JN中读取。哪怕是SNN和JN的Socket连接有网络波动,一旦网络恢复,SNN继续从JN读取数据,最终SNN保存的数据是和ANN一样的,也叫最终一致性!对于JN而言,有一个“过半机制”,在ANN往JN群写数据时,只要有过半的JN写入成功、SNN从过半JN的任意一个读取到了修改的数据,SNN就可以顺利同步全数据。当然了,这个过半机制的JN也不是没有任何问题,一旦JN3个节点有1一个节点挂了,对外提供的服务就会受影响(因为过半机制)。这里的多个JN、主备NN和多个DN的组合,就已经实现了手动HA了。
2.实现自动化HA
自动化HA解决单点故障的关键,就是引入Zookeeper,提供分布式协调服务。
ZKFC(是一个进程,和NN在同一个物理节点上)有两只手,分别拽着NN和Zookeeper。吧唧一开机,集群一启动,2个NN谁是Active?谁又是Standby呢?
2个zkfc先判断自己的NN是否健康,如果健康,2个zkfc会向zoopkeeper集群抢着创建一个节点,结果就是只有1个会最终创建成功,从而决定active地位和standby位置。哪怕是ZKFC1抢到了节点,ZKFC2没有抢到,ZKFC2也会监控watch这个节点。
如果ZKFC1的Active NN异常退出,ZKFC1最先知道,就访问ZK,ZK就会把曾经创建的节点删掉。删除节点就是一个事件,谁监控这个节点,就会调用callback回调,ZKFC2就会把自己的地位上升到active,但在此之前要先确认ZKFC1的节点是否真的挂掉?这就引入了第三只手的概念。
ZKFC2通过ssh远程连接NN1尝试对方降级,判断对方是否挂了。确认真的不健康,才会真的 上升地位之active。所以ZKFC2的步骤是:
- 1.创建新节点。
- 2.第三只手把对方降级。
- 3.把自己升级
那如果NN都没毛病,ZKFC挂掉了呢?Zoopkeeper有一个客户端session机制,集群启动之后,2个ZKFC除了监控自己的NN,还要和Zoopkeeper建立一个tcp长连接,并各自获取自己的session。只要一方的session失效,Zoopkeeper 就会删除该方创建的节点,同时另一方创建节点,上升地位。
这里举一个例子,来帮助理解自动化HA:大家应该都看过一部古装经典大剧《雍正王朝》,也应该都被一干老戏骨的演技折服。老四、老八就是NN,老十三、老十四就是ZKFC,zookeeper可以视为高大威严的皇权(zk的比喻不太恰当,就把皇权当做是n个zk吧)。吧唧一开机,老四、老八这两个皇子都想争夺皇位,老十三、老十四分别手握军权支持他俩。两个皇子都想在焦晃面前表现自己,终于一个机会,“四爷党”下江南查贪腐、赈灾民,深的康熙赏识,最终稳坐江山(此时四爷是ANN,八爷是SNN)。雍正虽即皇帝位,然“八爷党”贼心不死,一直watch皇位。雍正晚年推行新政,举国上下无不阻挠,十三爷也病入膏肓。于是八爷趁虚而入,借“八王议政”的名义,暗中勾结隆科多调兵逼宫,想让雍正从龙椅上掉下来。就在八爷党羽(ZKFC)以为能给雍正“降级”时,十三爷及时赶到、张廷玉据理力争、图里琛誓卫皇驾,八爷这才知道自己输了,“皇帝四哥”的地位还是不可动摇的。
以上这个例子,是笔者灵犀一动想出来的,也许不是很恰当(不能扭曲历史不是?)但重在会意,理解到底什么是自动化HA。
各节点的作用:
- Active NN作用:一个集群只能有1个ANN,接受Client请求,记录edits日志文件(元数据)
- SNN作用:一个集群可能有多个SNN,合并edits和fsimage文件,从而更新fsimage文件,等待ANN死亡
- JN作用:共享edits日志文件,当ANN写入一条日志的同时,也会往JN内写入该日志,且会通知SNN获取该日志
- DN作用:保存、管理block,并且向ANN和SNN汇报block位置信息
- zoopkeeper的作用:负责选举算法。选举一个NN的状态为Active,同时记录每个NN运行状态信息
- 1、维护目录树结构
- 2、对节点监控,事件回调机制
- 3、session机制
ZKFC作用:监控各自的NN,负责NN的状态切换。借助ssh服务切换NN状态
四、HDFS 2.x-Federation
虽然HDFS HA解决了“单点故障”问题,但是在系统扩展性、整体性能和隔离性方面仍然存在问题。
-
系统扩展性方面,元数据存储在NN内存中,受内存上限的制约。
-
整体性能方面,吞吐量受单个NN的影响。
-
隔离性方面,一个程序可能会影响其他运行的程序,如一个程序消耗过多资源导致其他程序无法顺利运行。HDFS HA本质上还是单名称节点。
联邦的引入,是为了解决内存受限的问题。首先明确一下,何为联邦?美国是一个联邦制国家,在这个州持枪合法,在另一个州就非法。
- 通过多个namenode/namespace把元数据的存储和管理分散到多个节点中,使到namenode/namespace可以通过增加机器来进行水平扩展。这些NN分别进行各自命名空间和块的管理,不需要彼此协调。每个DN要向集群中所有的NN注册,并周期性的发送心跳信息和块信息,报告自己的状态。
- 能把单个namenode的负载分散到多个节点中,在HDFS数据规模较大的时候不会也降低HDFS的性能。可以通过多个namespace来隔离不同类型的应用,把不同类型应用的HDFS元数据的存储和管理分派到不同的namenode中。HDFS联邦拥有多个独立的命名空间,其中,每一个命名空间管理属于自己的一组块,这些属于同一个命名空间的块组成一个“块池”。每个DN会为多个块池提供块的存储,块池中的各个块实际上是存储在不同DN中的。