当前位置 博文首页 > lillcol:大数据系列3:Hdfs的HA实现

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

    作者:lillcol 时间:2021-01-30 15:30

    在之前的文章:大数据系列:一文初识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的文章,感兴趣可以关注【兔八哥杂谈】