2025年4月2日 星期三 乙巳(蛇)年 正月初三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 服务器 > 网络服务

HDFS 的高可用性(High Availability)

时间:12-14来源:作者:点击数:7
城东书院 www.cdsy.xyz

通过联合使用在多个文件系统中备份 NameNode 的元数据和通过备用 NameNode 创建监测点能防止数据丢失,但是依旧无法实现文件系统的高可用性。NameNode依旧存在单点失效(SPOF)的问题。如果 NameNode 失效了,那么所有的客户端,包括MapReduce作业,均无法读、写或列举文件,因为NameNode是唯一存储元数据与文件到数据块映射的地方,对于一个大型并拥有大量文件和数据块的集群,NameNode 的冷启动需要30分钟,甚至更长时间,系统恢复时间太长了,也会影响到日常维护。在这一情况下,Hadoop系统无法提供服务直到有新的 NameNode 上线。

在这样的情况下要向从一个失效的 NameNode 恢复,系统管理员得启动一个拥有文件系统元数据副本得新的NameNode,并配置DataNode和客户端以便使用这个新的 NameNode。新的 NameNode 直到满足以下情形才能相应服务:

  • 将命名空间镜像文件导入内存中;
  • 重演编辑日志;
  • 接收到足够多的来自DataNode的数据块报告并退出安全模式

Hadoop 2.X 以上版本针对上述问题增加了对 HDFS 高可用性(HA)的支持。在这一实现中,配置了一对活动-备用(active-standby) NameNode。当活动NameNode失效,备用 NameNode 就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断。实现这一目标需要在架构上做如下修改。HDFS HA架构图如下所示:

  • NameNode 之间需要通过高可用共享存储实现编辑日志的共享。当备用NameNode接管工作之后,它将通读共享编辑日志直至末尾,以实现与活动NameNode的状态同步,并继续读取由活动NameNode写入的新条目。
  • DataNode需要同时向两个NameNode发送数据块处理报告,因为数据块的映射信息存储在NameNode的内存中,而非磁盘。
  • 客户端需要使用特定的机制来处理NameNode的失效问题,这一机制对用户是透明的。
  • 辅助NameNode的角色被备用NameNode所包含,备用NameNode为活动的NameNode命名空间设置周期性检查点。

有两种高可用性共享存储可以做出选择:NFS 过滤器或群体日志管理器(QJM, quorum journal manager)。QJM是一个专用的HDFS实现,为提供一个高可用的编辑日志而设计,被推荐用于大多数HDFS部署中,同时,QJM 的实现并没使用 Zookeeper,但在HDFS HA选取活动的NameNode时使用了Zookeeper技术。QJM以一组日志节点(journalnode)的形式运行,一般是奇数点结点组成,每个 JournalNode 对外有一个简易的RPC接口,以供NameNode读写EditLog到JN本地磁盘。当写EditLog时,NameNode会同时向所有JournalNode并行写文件,只要有 N/2+1 结点写成功则认为此次写操作成功,遵循Paxos协议。其内部实现框架如下:

从图中可看出,主要是涉及 EditLog 的不同管理对象和输出流对象,每种对象发挥着各自不同作用:

  • FSEditLog:所有EditLog操作的入口。
  • JournalSet:集成本地磁盘和JournalNode集群上EditLog的相关操作。
  • FileJournalManager:实现本地磁盘上EditLog操作。
  • QuorumJournalManager:实现JournalNode集群EditLog操作。
  • AsyncLoggerSet:实现JournalNode集群EditLog的写操作集合。
  • AsyncLogger:发起RPC请求到JN,执行具体的日志同步功能。
  • JournalNodeRpcServer:运行在JournalNode节点进程中的RPC服务,接收NameNode端的AsyncLogger的RPC请求。## HDFS的读写原理
  • JournalNodeHttpServer:运行在JournalNode节点进程中的Http服务,用于接收处于Standby状态的NameNode和其它JournalNode的同步EditLog文件流的请求。

QJM 写过程分析

上面提到EditLog,NameNode会把EditLog同时写到本地和JournalNode。写本地由配置中参数dfs.namenode.name.dir控制,写JN由参数dfs.namenode.shared.edits.dir控制,在写EditLog时会由两个不同的输出流来控制日志的写过程,分别为:EditLogFileOutputStream(本地输出流)和QuorumOutputStream(JN输出流)。写EditLog也不是直接写到磁盘中,为保证高吞吐,NameNode会分别为EditLogFileOutputStream和QuorumOutputStream定义两个同等大小的Buffer,大小大概是512KB,一个写Buffer(buffCurrent),一个同步Buffer(buffReady),这样可以一边写一边同步,所以EditLog是一个异步写过程,同时也是一个批量同步的过程,避免每写一笔就同步一次日志。

这个是怎么实现边写边同步的呢,这中间其实是有一个缓冲区交换的过程,即bufferCurrent和buffReady在达到条件时会触发交换,如bufferCurrent在达到阈值同时bufferReady的数据又同步完时,bufferReady数据会清空,同时会将bufferCurrent指针指向bufferReady以满足继续写,另外会将bufferReady指针指向bufferCurrent以提供继续同步EditLog。上面过程用流程图就是表示如下:

问题一 

既然EditLog是异步写的,怎么保证缓存中的数据不丢呢,其实这里虽然是异步,但实际所有日志都需要通过logSync同步成功后才会给client返回成功码,假设某一时刻NameNode不可用了,其内存中的数据其实是未同步成功的,所以client会认为这部分数据未写成功。还有EditLog怎么在多个JN上保持一致的呢?

解决方案:隔离双写

在 ANN 每次同步 EditLog 到 JN 时,先要保证不会有两个NN同时向JN同步日志,也就是说同一时间QJM仅允许一个NameNode向编辑日志中写入数据。这个隔离是怎么做的。这里面涉及一个很重要的概念Epoch Numbers,很多分布式系统都会用到。Epoch有如下几个特性:

  • 当NN成为活动结点时,其会被赋予一个EpochNumber。
  • 每个EpochNumber是唯一的,不会有相同的EpochNumber出现。
  • EpochNumber有严格顺序保证,每次NN切换后其EpochNumber都会自增1,后面生成的EpochNumber都会大于前面的EpochNumber。

但QJM是怎么保证上面的特性的呢,主要有以下几点:

  • 在对EditLog作任何修改前,QuorumJournalManager(NameNode上)必须被赋予一个EpochNumber;
  • QJM把自己的EpochNumber通过newEpoch(N)的方式发送给所有JN结点
  • 当JN收到newEpoch请求后,会把QJM的EpochNumber保存到一个lastPromisedEpoch变量中并持久化到本地磁盘;
  • ANN同步日志到JN的任何RPC请求(如logEdits(),startLogSegment()等),都必须包含ANN的EpochNumber;
  • JN在收到RPC请求后,会将之与lastPromisedEpoch对比,如果请求的EpochNumber小于lastPromisedEpoch,将会拒绝同步请求,反之,会接受同步请求并将请求的EpochNumber保存在lastPromisedEpoch。

这样就能保证主备NN发生切换时,就算同时向JN同步日志,也能保证日志不会写乱,因为发生切换后,原ANN的EpochNumber肯定是小于新ANN的EpochNumber,所以原ANN向JN的发起的所有同步请求都会拒绝,实现隔离功能,防止了脑裂。

恢复 in-process 日志

如果在写过程中写失败了,可能各个JN上的EditLog的长度都不一样,需要在开始写之前将不一致的部分恢复。恢复机制如下:

  • ANN先向所有JN发送getJournalState请求;
  • JN会向ANN返回一个Epoch(lastPromisedEpoch);
  • ANN收到大多数JN的Epoch后,选择最大的一个并加1作为当前新的Epoch,然后向JN发送新的newEpoch请求,把新的Epoch下发给JN;
  • JN收到新的Epoch后,和lastPromisedEpoch对比,若更大则更新到本地并返回给ANN自己本地一个最新EditLogSegment起始事务Id,若小则返回NN错误;
  • ANN收到多数JN成功响应后认为Epoch生成成功,开始准备日志恢复;
  • ANN会选择一个最大的EditLogSegment事务ID作为恢复依据,然后向JN发送prepareRecovery; RPC请求,对应Paxos协议2p阶段的Phase1a,若多数JN响应prepareRecovery成功,则可认为Phase1a阶段成功;
  • ANN选择进行同步的数据源,向JN发送acceptRecovery RPC请求,并将数据源作为参数传给JN。
  • JN收到acceptRecovery请求后,会从JournalNodeHttpServer下载EditLogSegment并替换到本地保存的EditLogSegment,对应Paxos协议2p阶段的Phase1b,完成后返回ANN请求成功状态。
  • ANN收到多数JN的响应成功请求后,向JN发送finalizeLogSegment请求,表示数据恢复完成,这样之后所有JN上的日志就能保持一致。

数据恢复后,ANN上会将本地处于in-process状态的日志更名为finalized状态的日志,形式如edits[start-txid][stop-txid]。

日志同步

日志从ANN同步到JN的过程,具体如下:

  • 执行logSync过程,将ANN上的日志数据放到缓存队列中;
  • 将缓存中数据同步到JN,JN有相应线程来处理logEdits请求
  • JN收到数据后,先确认EpochNumber是否合法,再验证日志事务ID是否正常,将日志刷到磁盘,返回ANN成功码;
  • ANN收到JN成功请求后返回client写成功标识,若失败则抛出异常。

通过上面一些步骤,日志能保证成功同步到JN,同时保证JN日志的一致性,进而备NN上同步日志时也能保证数据是完整和一致的。

QJM 读过程分析

这个读过程是面向备NN(SNN)的,SNN定期检查JournalNode上EditLog的变化,然后将EditLog拉回本地。SNN上有一个线程StandbyCheckpointer,会定期将SNN上FSImage和EditLog合并,并将合并完的FSImage文件传回主NN(ANN)上,就是所说的Checkpointing过程。下面我们来看下Checkpointing是怎么进行的。

在2.x版本中,已经将原来的由SecondaryNameNode主导的Checkpointing替换成由SNN主导的Checkpointing。下面是一个CheckPoint的流向图:

总的来说,就是在SNN上先检查前置条件,前置条件包括两个方面:距离上次Checkpointing的时间间隔和EditLog中事务条数限制。前置条件任何一个满足都会触发Checkpointing,然后SNN会将最新的NameSpace数据即SNN内存中当前状态的元数据保存到一个临时的fsimage文件( fsimage.ckpt)然后比对从JN上拉到的最新EditLog的事务ID,将fsimage.ckpt_中没有,EditLog中有的所有元数据修改记录合并一起并重命名成新的fsimage文件,同时生成一个md5文件。将最新的fsimage再通过HTTP请求传回ANN。通过定期合并fsimage有什么好处呢,主要有以下几个方面:

  • 可以避免EditLog越来越大,合并成新fsimage后可以将老的EditLog删除;
  • 可以避免主NN(ANN)压力过大,合并是在SNN上进行的;
  • 可以避免fsimage保存的是一份最新的元数据,故障恢复时避免数据丢失。

HDFS HA 如何实现故障切换与规避?

在活动namenode(ANN)失效之后,备用namenode(SNN)能够快速(几十秒的时间)实现任务接管,因为最新的状态存储在内存中:包括最新的编辑日志条目和最新的数据块映射信息。实际观察到的失效时间略长一点(需要1分钟左右),这是因为系统需要保守确定活动namenode是否真的失效了。活动namenode失效且备用namenode也失效的情况下,当然这类情况发生的概率非常低非常低的,现在Hadoop 3.X发行版本已经支持运行更多备用namenode来提供更高的容错性。

系统中有一个称为故障转移控制器(failover controller)的新实体,管理着将活动namenode转移为备用namenode的转换过程。有多种故障转移控制器,但默认一种是使用了Zookeeper来确保有且仅有一个活动namenode。每一个namenode运行着一个轻量级的故障转移控制器,其工作就是监视宿主namenode是否失效(通过一个简单的心跳机制实现)并在namenode失效时进行故障转移,这就是HA的主备切换机制,主备选举依赖于Zookeeper。下面是主备切换的状态图:

从图中可以看出,整个切换过程是由ZKFC(即故障转移控制器,全称Zookeeper Failover Controller)来控制的,具体又可分为HealthMonitor、ZKFailoverController和ActiveStandbyElector三个组件。

  • ZKFailoverController:是HealthMontior和ActiveStandbyElector的母体,执行具体的切换操作。
  • HealthMonitor:监控NameNode健康状态,若状态异常会触发回调ZKFailoverController进行自动主备切换。
  • ActiveStandbyElector:通知ZK执行主备选举,若ZK完成变更,会回调ZKFailoverController相应方法进行主备状态切换。

在故障切换期间,Zookeeper主要是发挥什么作用呢,有以下几点:

  • 失败保护:集群中每一个NameNode都会在Zookeeper维护一个持久的session,机器一旦挂掉,session就会过期,故障迁移就会触发。
  • Active NameNode选择:Zookeeper有一个选择ActiveNN的机制,一旦现有的ANN宕机,其他NameNode可以向Zookeeper申请成为下一个Active节点。
  • 防脑裂:ZK本身是强一致和高可用的,可以用它来保证同一时刻只有一个活动节点。

在哪些场景会触发自动切换呢,从HDFS-2185中归纳了以下几个场景:

  • ANN JVM崩溃:ANN上HealthMonitor状态上报会有连接超时异常,HealthMonitor会触发状态迁移至SERVICE_NOT_RESPONDING,然后ANN上的ZKFC会退出选举,SNN上的ZKFC会获得Active Lock,作相应隔离后成为Active节点。
  • ANN JVM冻结:这个是JVM没崩溃,但也无法响应,同崩溃一样,会触发自动切换。
  • ANN 机器宕机:此时ActiveStandbyElector会失去同ZK的心跳,会话超时,SNN上的ZKFC会通知ZK删除ANN的活动锁,作相应隔离后完成主备切换。
  • ANN 健康状态异常:此时HealthMonitor会收到一个HealthCheckFailedException,并触发自动切换。
  • Active ZKFC崩溃:虽然ZKFC是一个独立的进程,但因设计简单也容易出问题,一旦ZKFC进程挂掉,虽然此时NameNode是正常运行的,但系统也认为需要切换,此时SNN会发一个请求到ANN要求ANN放弃主节点位置,ANN收到请求后,会触发自动切换。
  • Zookeeper集群崩溃:如果ZK集群崩溃了,主备NN上的ZKFC都会感知并断连,此时主备NN会进入一个NeutralMode模式,同时不改变主备NN的状态,继续发挥作用,只不过此时,如果ANN也故障了,那集群无法发挥Failover,也就无法使用集群了,所以对于此种场景,ZK集群一般是不允许挂掉到多台,至少要有(N / 2 + 1)台保持服务才算是安全的。

管理员也可以通过手动发起故障转移,例如在进行日常维护时,这称为”平稳的故障转移“(graceful failover),因为故障转移控制器可以组织两个namenode有序地切换角色。命令参考如下所示。

将 active 状态由 nn1 切换到 nn2

  • # hdfs haadmin -failover --forcefence --forceactive nn1 nn2

在启用自动故障转移的集群上 --forcefence -- forceactive 参数不起作用

使用以下方法检查名称节点状态(假设 nn1 为 active,nn2 standby):

  • # hdfs haadmin -getServiceState nn1 active # hdfs haadmin -getServiceState nn2 standby

于是我们人为制造故障,在 nn1 上查看 NameNode 进程

  • # jps
  • # kill -9 [进程ID]

自动故障转移将会激活 nn2 节点,状态从 standby 转换为 active

但在非平稳故障转移的情况下,无法确切直到失效NameNode是否已经停止运行。例如网速较慢或者网络被分割的情况下,可能激发故障转移,但Active NameNode依然运行着并且依旧是Active NameNode。高可用实现做了更一步的优化,以确保先前Active NameNode不会执行危害系统并导致系统崩溃的操作,该方法称为”规避“。

规避机制包括:撤销 NameNode 访问共享存储目录的权限(通常使用供应商指定的NFS命令)、通过远程管理命令屏蔽相应的网络端口。最不行的话,可以通过一枪爆头(断电关机)等制造人为故障技术。

城东书院 www.cdsy.xyz
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐