大数据系列3:Hdfs的HA实现

在之前的文章:大数据系列:一文初识Hdfs
大数据系列2:Hdfs的读写操作 中Hdfs的组成、读写有简单的介绍。

在里面介绍Secondary NameNode和Hdfs读写的流程。
并且在文章结尾也说了,Secondary NameNode并不是我常说的HA,(High Availability)

本文承接之前的内容,对Hdfs的HA实现做个简单的介绍。


NameNode的重要性

先来看看Hdfs读写的流程图:

可以看到无论是读还是写,我们都必须和存储元数据的NameNode进行交互。

一旦NameNode出问题,整个集群的读写基本上就凉凉了。

所以,在设计的时候本就有做一些容错的措施。


容错的措施

如果NameNode出了问题,整个文件Hdfs将不可用。

设想一个场景:
如果以为某种原因我们运行NameNode的机器被删除。

此时所有Hdfs的文件都会丢失,因为我们虽然在DataNode中存储了文件的Block信息,但是我们无法通过这些Block重新构建文件。

所以让NameNode有一定抗拒错误的能力是很重要的。

Hadoop本身提供了两种机制。


备份元数据

第一种机制:
将Hdfs元数据中持久化状态的文件做多个备份。

Hadoop 可以进行相关的配置,把这些持久的状态备份到多个文件系统。这个过程是同步且原子的 。
一般都是配置为本份到本地磁盘,同时远程NFS 挂载

Secondary NameNode

第二种机制:
运行一个Secondary NameNode,负责定期合并名称namespace iamgeedit log,以防止edit log变得过大。

合并流程如下,详细过程之前有介绍过,此处不累赘:


存在问题

虽然有两个方式让Hdfs有一定的容错能力,但是还是存在一些问题。

Secondary NameNode拥有一个合并的namespace iamge像的副本,在NameNode失败时可以使用该iamge

但是Secondary NameNode中的状态是滞后于NameNode的,如果NameNode是宕机的情况下,数据丢失必不可少。

此时通常的做法是将NFS上的NameNode元数据文件复制到Secondary NameNode,并将其作为新的NameNode运行。

因为NFS中的与NameNode的元数据是同步的,所以可以通过NFS中的数据恢复。

在多个文件系统上复制NameNode元数据和使用Secondary NameNode创建checkpoints的组合可以防止数据丢失。

然而这种组合的机制并不能提供Hdfs的高可用性。

主要原因是这种方式恢复太耗时间了。

NameNode是唯一的存储了文件与Blcok映射关系的元数据存储库
,一旦它罢工,所有的Client的作业包括Mapreduce、读、写、列出文件无法进行。
在新的NameNode上线之前,整个集群处于停止服务的状态

如果NameNode要重新上线服务需要经过以下下的步骤:

  1. 加载命名空间到内存
  2. 重放edit log
  3. DataNode接收足够的Block报告并离开安全模式。

在具有许多文件和Block的大型集群上,NameNode的冷启动所需的时间可能是30分钟或更长。

这么拉垮可不太能接受,无论是日常运维还是计划停机之类的操作。

所以,归根结底就是NameNode仍然是存在单点故障(SPOF,Single Point Of Failure)

下面就看看怎么解决这个问题。


HDFS high availability (HA)

一般情况下会有两个场景和HA息息相关。

  1. 在出现计划外事件(如服务器宕机)的情况下,在重新启动NameNode之前,集群将不可用。
  2. 有计划的维护事件,例如NameNode机器上的软件或硬件升级,将导致集群停机。

为了解决上述的问题,Hadoop 提供了HDFS high availability (HA)的支持。

通过active-standby的方式配置两个NameNode(3.0后可以支持超过两个),在Active/Passive动配置中使用热备份。

在机器崩溃的情况下,实现快速故障转移到新的NameNode
或者出于计划维护的目的,允许管理员发起优雅的故障转移。

为了实现这个目标,在架构层面需要做一些改变:

  1. NameNode间必须要通过一些高可用的共享内存共享edit log,一旦standby NameNode接管工作的时候,它通过读取共享edit log 直至结尾以保持它的状态与active NameNode一致,然后继续读取由active NameNode写入的新条目。
  2. DataNodes必须向两个NameNode发送Block报告,因为NameNode映射关系是存储在DataNode的内存而不是磁盘。
  3. 客户端必须使用一种机制来处理NameNode失效问题,让用户对于NameNode的切换是透明的。(就是不需要用户自己去切换NameNode
  4. standby NameNode应该承担之前Secondary NameNode的角色,定期为active NameNode 的命名空间创建检查点。

在典型的HA集群中,会配置两个或更多的NameNodes,但是在任何时候,都只能有一个NameNode处于活动状态,其他NameNode处于备用状态。
Active状态的NameNode负责集群中的所有Client的操作,
Standby状态的NameNode仅需要保存足够的状态的即可。

有两种实现方式


Quorum Journal Manager (QJM)

QJM是一个专用的HDFS实现,设计的唯一目的是提供高度可用的编辑日志,并且是大多数HDFS安装的推荐选择。

QJM作为一组journalnodes(JNs)独立守护进程运行,每次编辑都必须写入大部分JNs节点。

为了使备节点与主节点保持状态同步,两个节点都与JNs进程通信。

Active NameNode执行任何关于名称空间修改时,它会持久地记录修改记录到大部分的JNs

通常,有三个journal节点,因此系统可以容忍其中一个节点的丢失。
尽管这种方式类似ZooKeeper工作方式,但是必须说明的是QJM实现没有使用ZooKeeper
同时还要注意,HDFS的HA确实使用了ZooKeeper来选择active NameNode,后续会介绍

standby NameNode能够从JNs中读取edits ,并不断监视它们对edit log的修改。

standby NameNode````看到这edits时,它将它们应用到自己的名称空间。

standby NameNode必须持有集群中NameNode位置的最新信息。
所以,DataNodes需要配置所有NameNode的位置,并向所有NameNode发送Block 位置信息和心跳。

因为standby NameNode 在内存中有最新的状态:最新的edit log和最新的NameNode映射
所以Active NameNode发生故障时,standby NameNode可以很快接管(在几十秒内)。

实际观察到的故障转移时间要长一些(大约一分钟左右),因为系统需要保守地判断active NameNode是否发生了故障。


NFS

QJM方共享不同,edit log记录到JNs不同,

此处的共享内存一般使用过一个支持NFS(Network File System:网络文件系统)的NAS(Network Attached Storage:网络附属存储)创建应共享文件,通常会挂载在每隔一个NameNode的机器上,NameNode可以对其进行读、写操作。

但是,目前只支持一个共享edit目录。
因此,系统的可用性受到共享edit目录的可用性的限制,
为了消除所有单点故障,共享编辑目录需要有冗余。

具体来说,就是到存储的多条网络路径,以及存储本身的冗余(磁盘、网络和电源)。
所以,建议使用高质量的专用NAS设备作为共享存储服务器,而不是简单的Linux服务器。


故障转移和规避

HA集群中,standby NameNode还执行名称空间状态检查点操作,
因此在HA集群中没有必要运行 Secondary NameNode, CheckpointNode, or BackupNode

同时无论是QJM还是NFS中,
在同一时刻只有一个NameNode处于Active是至关重要的。
否则,名称空间状态将很快在两者之间出现分歧(脑裂),可能导致数据丢失或其他不正确的结果。
为了避免这个情况,同一时间只允许一个NameNode 对 JournalNodes 或 NFS的共享内存写入。

在故障转移期间,即将成为Active状态的NameNode将接管向共享eidt文件写入数据的角色,
这将有效地阻止其他NameNode继续处于Active状态,从而允许新的Active状态安全地进行故障转移。

但是要如何实现呢?


故障转移

从active NameNodestandby NameNode```` 的过渡由系统中称为故障转移控制器(failover controller)的新实体进行管理。

可以理解为active NameNode挂了standby NameNode上位的过程

有各种各样的故障转移控制器,但是默认的实现是使用ZooKeeper来确保只有一个NameNodeactive的。

每个NameNode运行一个轻量级的故障转移控制器进程,
它的任务是监视它的NameNode是否出现故障(使用一个简单的心跳机制),
并在NameNode出现故障时触发故障转移。

故障转移也可以由管理员手动启动,
例如,在例行维护的情况下。
这被称为优雅的故障转移( graceful failover),因为故障转移控制器为两个NameNode安排了有序的角色转换。

但是,
在意料之外的故障转移的情况下,不可能确保失败的NameNode已经停止运行。

例如,网络或网络分区速度较慢的情况下,
即使以前active NameNode仍然在运行
也可能会触发故障转移。

为了避免这种情况,HA实现了确保以前active NameNode不会造成任不良影响的方法称为规避(fencing)。


规避

JNs一次只允许一个NameNode写入编辑日志;
但是,以前活跃的NameNode仍然可能向客户端提供过时的读请求,
因此可以设置一个SSH 规避命令来杀死NameNode进程。

对于使用NFS文件的共享edit log,因为很难控制一次只允许一个NameNode写入,
需要更强有力地规避方法,一般可以选择如下方法:

  1. 撤销NameNode对共享存储目录的访问(通常使用特定于供应商的NFS命令)
  2. 通过远程管理命令屏蔽相应网络端口(port)
  3. 做绝一点可以通过一个叫做“一枪爆头”方式进行规避(STONITH,short the other node in the head),通过一个特定的供电单元对相应的NameNode进行断电操作。

这就是推荐使用QJM的主要原因。

有了上述只是储备,我们很容易就可以把这个架构图画出来,以QJM为例

那么转移过程大致如下:


客户端故障转移

对于客户端而言,我们要让用户对于故障转移是无感的。
可以通过配置文件实现一个故障转移

在配置文件中通过配置几个参数实现故障转移:

dfs.nameservices:Hdfs逻辑名称

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

dfs.ha.NameNodes.[nameservice ID]:nameservice中每个NameNode的唯一标识符

<property>
  <name>dfs.ha.NameNodes.mycluster</name>
  <value>nn1,nn2, nn3</value>
</property>

dfs.NameNode.rpc-address.[nameservice ID].[name node ID]:每个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>
<property>
  <name>dfs.NameNode.rpc-address.mycluster.nn3</name>
  <value>machine3.example.com:8020</value>
</property>

更多的配置可以查看
NameNode HA With QJM
NameNode HA With NFS


关于HA暂时介绍到这里,后续有空再进行详细的介绍。
之后会写一下关于Yarn的文章,感兴趣可以关注【兔八哥杂谈】

posted @ 2021-01-27 18:44  lillcol  阅读(680)  评论(0编辑  收藏  举报