HDFS单点故障和线性扩展问题

2019-11-09  本文已影响0人  Michaelhbjian

这篇文章写的很优秀,然后自己稍微整理了下。转自:https://matt33.com/2018/07/15/hdfs-architecture-learn/

1、HDFS 1.0 的问题

在前面的介绍中,关于 HDFS1.0 的架构,首先都会看到 NameNode 的单点问题,这个在生产环境中是非常要命的问题,早期的 HDFS 由于规模较小,有些问题就被隐藏了,但自从进入了移动互联网时代,很多公司都开始进入了 PB 级的大数据时代,HDFS 1.0的设计缺陷已经无法满足生产的需求,最致命的问题有以下两点:

技术难点:如何保持主备NameNode的状态同步,并让Standby在Active挂掉后迅速提供服务,NameNode启动比较耗时,包括FSimage和Editlog(获取file to block),处理所有DataNode第一次blockreport(获取block to DataNode信息),保持NN的状态同步,需要这两部信息同步。

为了解决上面的两个问题,Hadoop2.0 提供一套统一的解决方案:

2、HDFS 2.0 的 HA 实现

关于 HDFS 高可用方案,非常推荐这篇文章:Hadoop NameNode 高可用 (High Availability) 实现解析,IBM 博客的质量确实很高,这部分我这里也是主要根据这篇文章做一个总结,这里会从问题的原因、如何解决的角度去总结,并不会深入源码的实现细节,想有更深入了解还是推荐上面文章。

这里先看下 HDFS 高可用解决方案的架构设计,如下图(下图来自上面的文章)所示:

754a25160047149592b9d6b979a8f6bd

这里与前面 1.0 的架构已经有很大变化,简单介绍一下上面的组件:

3、FailoverController

FC 最初的目的是为了实现 SNN 和 ANN 之间故障自动切换,FC 是独立与 NN 之外的故障切换控制器,ZKFC 作为 NameNode 机器上一个独立的进程启动 ,它启动的时候会创建 HealthMonitorActiveStandbyElector这两个主要的内部组件,其中:

  1. HealthMonitor:主要负责检测 NameNode 的健康状态,如果检测到 NameNode 的状态发生变化,会回调 ZKFailoverController 的相应方法进行自动的主备选举;
  2. ActiveStandbyElector:主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑,一旦 Zookeeper 主备选举完成,会回调 ZKFailoverController 的相应方法来进行 NameNode 的主备状态切换。
img

4 、自动触发主备选举

NameNode 在选举成功后,会在 zk 上创建了一个 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点,而没有选举成功的备 NameNode 会监控这个节点,通过 Watcher 来监听这个节点的状态变化事件,ZKFC 的 ActiveStandbyElector 主要关注这个节点的 NodeDeleted 事件(这部分实现跟 Kafka 中 Controller 的选举一样)。

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

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

5、HDFS 脑裂(split-brain)问题

在实际中,NameNode 可能会出现这种情况,NameNode 在垃圾回收(GC)时,可能会在长时间内整个系统无响应,因此,也就无法向 zk 写入心跳信息,这样的话可能会导致临时节点掉线,备 NameNode 会切换到 Active 状态,这种情况,可能会导致整个集群会有同时有两个 NameNode,这就是脑裂问题。

脑裂问题的解决方案是隔离(Fencing),主要是在以下三处采用隔离措施:

关于这个问题目前解决方案的实现如下:

  1. ActiveStandbyElector 为了实现 fencing,会在成功创建 Zookeeper 节点 hadoop-ha/{dfs.nameservices}/ActiveStandbyElectorLock 从而成为 Active NameNode 之后,创建另外一个路径为 /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 的持久节点,这个节点里面保存了这个 Active NameNode 的地址信息;
  2. Active NameNode 的 ActiveStandbyElector 在正常的状态下关闭 Zookeeper Session 的时候,会一起删除这个持久节点;
  3. 但如果 ActiveStandbyElector 在异常的状态下 Zookeeper Session 关闭 (比如前述的 Zookeeper 假死),那么由于 /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 是持久节点,会一直保留下来,后面当另一个 NameNode 选主成功之后,会注意到上一个 Active NameNode 遗留下来的这个节点,从而会回调 ZKFailoverController 的方法对旧的 Active NameNode 进行 fencing。

在进行 fencing 的时候,会执行以下的操作:

Hadoop 目前主要提供两种隔离措施,通常会选择第一种:

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

NameNode 选举的实现机制与 Kafka 的 Controller 类似,那么 Kafka 是如何避免脑裂问题的呢?

6、第三方存储(共享存储)

上述 HA 方案还有一个明显缺点,那就是第三方存储节点有可能失效,之前有很多共享存储的实现方案,目前社区已经把由 Clouderea 公司实现的基于 QJM 的方案合并到 HDFS 的 trunk 之中并且作为默认的共享存储实现,本部分只针对基于 QJM 的共享存储方案的内部实现原理进行分析。

QJM(Quorum Journal Manager)本质上是利用 Paxos 协议来实现的,QJM 在 2F+1 个 JournalNode 上存储 NN 的 editlog,每次写入操作都通过 Paxos 保证写入的一致性,它最多可以允许有 F 个 JournalNode 节点同时故障,其实现如下(图片来自:Hadoop NameNode 高可用 (High Availability) 实现解析 ):

image-20191109225330735

基于 QJM 的共享存储的数据同步机制

Active NameNode 首先把 EditLog 提交到 JournalNode 集群,然后 Standby NameNode 再从 JournalNode 集群定时同步 EditLog。

还有一点需要注意的是,在 2.0 中不再有 SNN 这个角色了,NameNode 在启动后,会先加载 FSImage 文件和共享目录上的 EditLog Segment 文件,之后 NameNode 会启动 EditLogTailer 线程和 StandbyCheckpointer 线程,正式进入 Standby 模式,其中:

7、HDFS 2.0 Federation 实现

在 1.0 中,HDFS 的架构设计有以下缺点:

而 Federation 的设计就是为了解决这些问题,采用 Federation 的最主要原因是设计实现简单,而且还能解决问题。

8、Federation 架构

Federation 的架构设计如下图所示(图片来自 HDFS Federation):

image-20191109225426086

Federation 的核心设计思想

Federation的核心思想是将一个大的 namespace 划分多个子 namespace,并且每个 namespace 分别由单独的 NameNode 负责,这些 NameNode 之间互相独立,不会影响,不需要做任何协调工作(其实跟拆集群有一些相似),集群的所有 DataNode 会被多个 NameNode 共享。

其中,每个子 namespace 和 DataNode 之间会由数据块管理层作为中介建立映射关系,数据块管理层由若干数据块池(Pool)构成,每个数据块只会唯一属于某个固定的数据块池,而一个子 namespace 可以对应多个数据块池。每个 DataNode 需要向集群中所有的 NameNode 注册,且周期性地向所有 NameNode 发送心跳和块报告,并执行来自所有 NameNode 的命令。

到这里,基本对 HDFS 这部分总结完了,虽然文章的内容基本都来自下面的参考资料,但是自己在总结的过程中,也对 HDFS 的基本架构有一定的了解,后续结合公司 HDFS 团队的 CaseStudy 深入学习这部分的内容,工作中,也慢慢感觉到分布式系统,很多的设计实现与问题解决方案都很类似,只不过因为面对业务场景的不同而采用了不同的实现。

9、参考资料

上一篇 下一篇

猜你喜欢

热点阅读