几个星期前, Cloudera发布了CDH 4.1最新的更新版本,这是第一个真正意义上的独立高可用性HDFS NameNode的hadoop版本,不依赖于特殊的硬件或外部软件。这篇文章从开发者的角度来看这个新功能,解释了内部运作原理。如果你正在寻找有关配置和操作此功能的信息,请参阅 高可用性指南CDH4 。
背景
项目自年初以来,围绕一个非常简单的架构HDFS被设计成:主守护程序,被称为NameNode的存储文件系统 元数据 ,而从守护进程,称为DataNode节点,存储文件系统的 数据 。NameNode要求高度可靠和高效,是存储PB级HDFS中成千上万的集群多年积累的关键生产数据,架构简单。然而,在相当长的一段时间里,HDFS集群安全受到NameNode的单点故障(SPOF)的威胁。在2月份的第一个beta版本CDH4以来,引入Standy NameNode来解决这个问题,它提供了自动热备份的故障转移能力。 。
以前版本的NameNode HA的局限性
在3月的博客文章,描述NameNode的高可用性依赖于 共享存储 ,特别是它需要一个地方来存储HDFS NameNode的日志记录,并同时能被Standy NameNode读取。此外,共享存储本身必须是高度可用。
在版本HDFS的CDH4.1前,我们需要NFS挂载共享存储,通常提供一个企业级形式的NAS设备。对于一些机构,这正好适合与他们现有的运营实践,而事实上,我们能有几个客户在生产环境中配备高可用共享存储设备。正因如此,客户和社区成员发现了基于NFS的共享存储的局限性:
- 特定的硬件
要求一台昂贵的NAS硬件设备。此外,fencing配置可能需要支持远程控制的电源分配单元(PDU)或其他特殊硬件。因财务费用或者是运营成本原因,许多机构不选择将NAS设备或其他定制硬件部署在他们的数据中心。
- 复杂的部署
即使HDFS安装后,管理员必须采取额外的步骤来配置挂载NFS,自定义fencing脚本,增加HA部署的复杂性,如果配置错误,甚至可能导致Hadoop集群无法使用。
- 脆弱的NFS客户端
许多版本的Linux NFS客户端古怪且难以配置。例如,管理员很容易错误配置安装选项,在这样一种情况下,NameNodes在意外中断情况下,整个集群将会瘫痪无法恢复正常运作。
- 依赖外部因素
正因数据存储在NAS设备上,运营商需要监控和维护多一个硬件基础设施。在最低限度,这涉及到额外配置警报和度量,并在某些机构中还可能会推出一个团队间的依赖关系:Hadoop业务运营团队中,需要有一个额外的存储运维组去负责此事。
消除这些局限
鉴于上述局限性和缺点,经过评估创建了一个简短的一个可行的替代需求清单:
- 没有特殊的硬件要求
与Hadoop的其他节点一样,只是集群里普通的一部分,我们应该只依赖于普通商业硬件。
- 没有特定要求的防护配置
Fencing方法如 STONITH 需要定制的硬件,相反,我们应该完全只依赖于软件的方法。
- 没有单点故障(SPOF)
因为这里的目标是高可用性(HA),为避免单点故障和定制的硬件,决定将多个元数据副本存储于多个集群节点上。鉴于此,我们增加了以下额外要求:
- 可配置任意数量的故障点数
设计一个系统,不是仅只容许一个故障点,应该通过添加额外的元数据的副本,给用户可以灵活弹性地选择自己想要的保护级别。
- 不影响业务的延迟副本
因为元数据写入路径是NameNode的业务表现的一个重要组成部分,希望业务处理没有延迟,所以如果副本中的一个故障,不会产生整个集群的业务处理延时。
- 添加日记副本不应增加延迟
如果我们允许管理员配置额外的副本,容忍几个同时发生的故障,这必须不要对性能产生不利影响。
为下列业务要求:
- 保持与其他Hadoop组件的一致性
设计引入任何新的组件和操作方式应类似于现有的组件,例如,他们应该使用基于XML的配置文件,log4j日志,以及相同的度量框架。
- 集中显示状态指标
因为该系统是NameNode工作的一个重要组成部分,能显示出该系统的状态指标尤为重要。新系统需要公开所有重要的指标,它可以运行在一个长寿命的生产集群当中,对出现会导致集群不可用的任何潜在问题都提前预警。
- 安全性
为CDH 提供全面的安全保障,包括线路加密和通过Kerberos加强认证。还有堆栈设计引入任何新的组件必须坚持相同的标准:有加密需求的客户,在对用户数据加密的同时,元数据的加密也同样重要。
Quorum Journal Manager
Cloudera设计一个系统称为Quorum Journal Manager,该系统是基于一个简单的想法:不是将HDFS编辑记录存储在一个单一位置(例如NFS文件管理器),而是使用一个分布式的协议将它们存储在多个地点,以确保这几个地方数据正确且同步。在系统中,远程存储是一种称为 JournalNode的新型HDFS守护进程 。NameNode扮演客户端角色,将变更数据写入到一组JournalNodes,,当大部分的Journal节点成功将变更数据复制过去后,认定变更成功。
同样,当在待机状态下的NameNode需要读取变更数据,它可以从任何存储在JournalNodes副本中读取一直保持热备份的命名空间。
分布式提交协议
上面的描述可以简化:NameNode的只是将变更数据写到三个节点,当将变更数据复制到多数节点上时被认定为变更成功。然而,在这提出了几个有趣的问题:
- 假如一批变更数据发送到一个节点,但是没有发送到其他节点,会发生什么情况,然后NameNode会死机?
- 假如有一个“脑裂”的情景,其中两个NameNodes都试图维护自己的active (writer)状态,会发生什么事?
- 启动时不一致如何恢复系统,这种情形当变更无法被处理会导致几个节点宕机吗?
简短的回答以上问题,系统依赖于著名的Paxos协议实现运行。该协议规定确保了一些在集群中的节点数据一致性值的正确方法。在我们的系统中,我们通过使用Paxos协议去提交每批变更数据,另外由Paxos启用待命NameNode,故障转移后立即清理任何挂起的某批次变更数据。对个中细节和算法感兴趣请参阅 HDFS-3077设计文件 。
Fencing和Epoch号
正如前文所描述的,系统的关键要求之一是为了避免使用任何特殊软硬件防护措施。Fencing机制是,在故障转移后,新的Active NameNode启用后,保证以前的旧NameNode不再能够进行任何系统元数据的更改。换句话说,Fencing是“裂脑综合征”(一个潜在的场景,其中两个节点都认为他们自己是活跃的Namenode并出现系统元数据修改冲突)的特效药。那么,如何通过Quorum Journal Manager实现Fencing机制?
Fencing在Quorum Journal Manager中是概念性的Epoch号。每当一个NameNode会变得活跃,它首先需要生成一个Epoch号 。这些数字是严格遵守递增规律的整数,并保证每次分配是唯一的。命名空间初始化后的第一个活跃NameNode从Epoch号是1,发生任何故障转移或重新启动操作将开始递增Epoch号。在本质上,Epoch号是两个NameNodes之间的一个定序器,如果一NameNode的Epoch号较高,那它被认为是之前NameNode中最新的Epoch号。用一个简单的算法确保NameNodes产生这些Epoch号是完全独一无二的:一个给定的Epoch号将永远不会被重复分配两次。在此算法中的细节也可以在上面提到的HDFS-3077设计文件中找到。
如果两个NameNodes都认为他们是活跃的,如何用自己独特的Epoch号去避免裂脑综合征?答案是出奇的轻松简单:当一个NameNode发送任何消息到JournalNode(或远程过程调用),将Epoch号作为请求的一部分包含在内。每当JournalNode收到这样的消息,请求中包含的Epoch号与本地之前存储的被称为“Epoch确认号”进行比较 。如果请求中的Epoch号较新,则记录并将其作为新的“Epoch确认号”。相反,如果请求来自一个旧的Epoch号,那么就拒绝该请求。