HDFS High Availability

1. 背景

Hadoop 2.0.0 之前,namenode 一直是单节点运行,存在单点故障。若是在namenode 节点出现问题,则会导致整个hdfs 集群均不可用。直到namenode进程恢复,或是在另一备用节点上启动namenode进程。

HDFS 的高可用(high availability)提供了配置一主(active)一备(standbynamenode 的功能,用于在主节点故障(或是手动维护时),快速的进行failover,将业务切换到standby namenode

 

2. 架构

在一个典型的HA集群中,会有两个独立的机器被配置为两个Namenode。在任何时刻,两者中仅会有一个处于active 状态,而另一个处于standby状态。Active Namenode 负责处理集群中所有客户端的请求,而StandBy 仅是作为 slave 存在,维护状态信息,以供failover使用。

为了使得StandBy节点的状态与Active节点同步,当前的实现方式是:需要两个节点均有访问同一个共享存储设备(如NFS)的权限(这个限制在之后的版本中可能会有部分宽松)。

Active 节点上发生任意一个namespace的修改(如put操作,更新了namenode的元数据)时,对应的修改会被写入到存储在共享存储的 edits 日志中。StandBy 节点持续监控此目录下的文件,如果看到有edits 记录,则将操作应用的自己的namespace。在发生failover时,Standby被提升为active状态,在此之前它会确保它已经读了共享目录下所有的edits。这个是为了确保namespace的状态在failover发生时已经完全同步到了Standby节点。

为了提供一个快速的failoverStandBy 节点还需要知道句群中数据块的位置信息。为了实现此目的,所有DataNode上均会配置两个namenode的地址,并且发送block块信息及heartbeats 到这两个namenode

HA中很重要的一点是:每个时刻仅能有一个NameNodeactive 状态。否则namespace 状况会迅速分裂成两部分,导致数据丢失风险。为了确保这个性质,以及防止“脑裂”的场景,管理员必须为共享存储配置至少一个 “fencing method”。在一个failover发生时,如果无法确认前一个Active node 已经放弃了它的Active 状态,fencing process会用于切断前一个Active 访问个共享edits文件的权限。这样可以防止前一个Active namenode 之后对namespace进行任何修改,使新Active namenode安全的过度到failover

 

3. 硬件资源

为了部署一个HA集群,若是使用的是NFS作为共享文件存储,则需要准备以下两部分资源:

1.       两台配置基本一致的 NameNode机器

2.       共享存储:两台NameNode均能读写此存储。一般来说,会使用一个NFS,并挂载到每个NameNode上。

使用 NFS 时,文件的高可用以及fault tolerance 须由NFS组件提供。

 

若是使用的Quorum Journal Manager 管理的edits 日志,则需要准备以下两部分资源:

1.       两台配置基本一致的 NameNode机器

2.       JournalNode 机器:即运行JournalNode进程的机器。

JournalNode进程是相对较轻量级的进程,所这些守护进程可以与namenode进程、yarn ResourceManager进程等运行在同一个机器上。需要注意的是:至少需要有三个JournalNode守护进程运行,对edit log 的修改必须写到大多数JournalNode中,这样才可以避免单节点故障导致的问题。当然也可以设置超过3个的JournalNode,不过最好是基数个JNs,这样可以增加系可容错次数。需要注意的是,若正在运行的有NJNs,则系统可同时容忍最多 (N-1)/2 次故障,并继续正常提供服务。

 

这里需要注意的是,在 HA 模式下,StandBy NameNode也可以处理namespace statecheckpoint(合并editsfsimage文件)。所以在HA集群中,并不需要运行Secondary NameNodeCheckpointNode、或是BackupNode。而且这么做也会导致报错。

 

4. 配置

为了配置 HA NameNodes,必须要在hdfs-site.xml 文件增加一些配置项。

1. dfs.nameservices

nameservice的逻辑名称,可任取。与非HA模式不同,HA模式下使用此属性用于标识整个集群的namespace。在emr中(三个主节点均是)配置如下:

<property>

    <name>dfs.nameservices</name>

    <value>ha-nn-uri</value>

  </property>

 

通过Web UI 界面,我们看一下非 HA 模式与 HA模式的NameNode对比:

Fig.4.1 HA模式

 

 

Fig.4.2 HA模式

Fig.4.1 Fig.4.2 可以看到,在 HA模式下,多了两个属性,分别是Namespace 以及 Namenode ID。其中Namespace 为集群的标识符,Namenode ID 为各个Namenode 节点的 id

 

2. dfs.ha.namenodes.[nameservice ID]

配置每个nameservice 下的namenode节点信息。DataNodes 据此获取集群中NameNodes的信息。当前最多仅可配置两个节点。

例如:

<property>

    <name>dfs.ha.namenodes.ha-nn-uri</name>

    <value>nn1,nn2</value>

  </property>

这里表示ha-nn-urinamespace下有两个namenode,分别名为nn1nn2

 

3. dfs.namenode.rpc-address.[nameservice ID].[name node ID]

每个namenode监听的RPC服务地址。例如:

<property>

    <name>dfs.namenode.rpc-address.ha-nn-uri.nn1</name>

    <value>ip-xx.xx.xx.xx-.com:8020</value>

  </property>

 

  <property>

    <name>dfs.namenode.rpc-address.ha-nn-uri.nn2</name>

    <value> ip-xx.xx.xx.xx-.com:8020</value>

  </property>

这里要列出集群中所有 namenodeRPC 服务地址。

 

4. dfs.namenode.http-address.[nameservice ID].[name node ID]

每个 namenode 监听的 http 服务地址。例如:

<property>

    <name>dfs.namenode.http-address.ha-nn-uri.nn1</name>

    <value> ip-xx.xx.xx.xx-.com:50070</value>

</property>

 

<property>

    <name>dfs.namenode.http-address.ha-nn-uri.nn2</name>

    <value> ip-xx.xx.xx.xx-.com:50070</value>

  </property>

类似于 rpc-address,这里要列出集群中所有 namenode http 地址。如果集群使用了安全配置,则同时还需要设置 https-address

 

5. dfs.namenode.shared.edits.dir

共享存储目录的地址,若是使用的 JournalNode进程,则需要列出所有JNs的地址,但是url必须只配置一个(这里url指的是 /ha-nn-uri)。例如:

<property>

    <name>dfs.namenode.shared.edits.dir</name>

    <value>qjournal://ip-xx.xx.xx.xx:8485;ip-yy.yy.yy.yy:8485;ip-zz.zz.zz.zz:8485/ha-nn-uri</value>

  </property>

 

 

6. dfs.client.failover.proxy.provider.[nameservice ID]

HDFS 客户端用于与Active NameNode交互的Java class。这个classDFS Client用于决定哪个NameNode是当前Active的。Hadoop自带了两种实现方式:ConfiguredFailoverProxyProvider以及 RequestHedgingProxyProvider(对于第一个call,并发invoke所有namenodes,并判断哪个是active的,然后后续的requests会发送到此active namenode,直到下次failover发生),例如:

<property>

    <name>dfs.client.failover.proxy.provider.ha-nn-uri</name>

<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>

  </property>

 

7. dfs.ha.fencing.methods

一组脚本或是Java classes,用于在failover发生时隔离 Active NameNode。在HA中很重要的一点是:在任何时间点,仅能有一个activenamenode。所以在Failover发生后,standby namenode转换成active状态前,我们首先确保当前active namenode 要么处于Standby 状态,要么它的进程已被终止。为了实现此功能,我们必须配置至少一个 fencing method(隔离方法)。可以配置多个fencing脚本,按逗号隔开,并按顺序执行,直到其中某个执行成功。在Hadoop中提供了两种fencing方法:shell sshfence

1. sshfence:使用fuser用户sshactive namenode,并killnamenode进程。这里由于是使用ssh的方式,所以必须要能够直接ssh到目标节点而不使用密码。对此,我们还要必须配置 dfs.ha.fencing.ssh.private-key-files 参数,提供以逗号隔开的ssh 私钥文件,例如:

    <property>

      <name>dfs.ha.fencing.methods</name>

      <value>sshfence</value>

    </property>

 

    <property>

      <name>dfs.ha.fencing.ssh.private-key-files</name>

      <value>/home/exampleuser/.ssh/id_rsa</value>

    </property>

 

(也可以选择非标准的用户名或端口登录,以及timeout的配置等,详情参考:

https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithNFS.html

2. shell:使用 shell 脚本隔离active namenode。也就是在fencing时,执行此shell脚本,例如:

<property>
  <name>dfs.ha.fencing.methods</name>
  <value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>
</property>

 

EMR 中,使用的是 shell 脚本模式:

<property>
    <name>dfs.ha.fencing.methods</name>
    <value>shell(/bin/true)</value>
  </property>

 

8. fs.defaultFS

core-site.xml 中,HA模式与非HA模式的另一个区别是hdfs的地址。默认在非HA模式下,我们会使用 hdfs://namenode-addres:8020 地址作为默认 hdfs 地址。而在HA模式下,提供的是nameservice地址,例如:

<property>

    <!-- URI of NN. Fully qualified. No IP.-->

    <name>fs.defaultFS</name>

    <value>hdfs://ha-nn-uri</value>

  </property>

 

9. dfs.journalnode.edits.dir

JournalNode存储自身状态以及其他JNs状态的目录,只配置一条路径。例如:

<property>

    <name>dfs.journalnode.edits.dir</name>

    <value>/mnt/journalnode</value>

  </property>

 

查看两个namenode节点上的 /mnt/journalnode/ 路径下文件,可以看到此目录下的edits log 大小与命名完全一致。在VERSION文件下,storageType的值为JOURNAL_NODE

 

5. 管理命令

namenode 节点,可以使用 hdfs haadmin 命令管理 HA 模式,主要有以下几种命令:

1.-getAllServiceState
返回所有namenode的状态。连接到所有配置的namenode并判断它们当前的状态。例如:

[hadoop@ip-10-0-2-245 ~]$ hdfs haadmin -getAllServiceState

ip-10-0-2-245.cn-north-1.compute.internal:8020     active

ip-10-0-2-150.cn-north-1.compute.internal:8020     standby

 

2.-getServiceState <serviceId>

获取某个namenode的状态。连接到单个namenode并判断它们状态,例如:

[hadoop@ip-10-0-2-245 ~]$ hdfs haadmin -getServiceState nn1

Active

 

3.-transitionToActive <serviceId> 以及-transitionToStandby <serviceId>
将某个特定的namenode节点的状态切换。此命令会让指定namenode 直接切换状态。但是并不会做任何 fencing的操作,一般不建议使用此命令,而是使用hdfs haadmin -failover

 

4.-failover [--forcefence] [--forceactive] <serviceId> <serviceId>

发起从第一个namenode 到第二个namenodefailover。如果第一个 namenode处于Standby状态,则此命令仅简单地将第二个的状态切换为Active状态。如果第一个nnnamenode)处于Active状态,则会尝试将它转换为Standby 状态。如果失败,则指定文件中的fencing method会被按顺序调用,直到成功。仅在以上过程后,second nn 会被转换成Active状态。如果没有fencing method 成功,则second nn 不会被转换为Active 状态,并返回报错。

 

6. 自动Failover

hdfs haadmin 管理命令中提供了手动failover 的命令。但是在手动模式下,Active NameNode在发生异常后,集群不会自动进行failover。下面介绍自动failover的配置。

自动failover的实现需要在HA HDFS 的部署中增加两个新组件:Zookeeper quorum ZKFailoverController(简称ZKFC进程。

Apache ZooKeeper是一个高可用的服务,维护了集群里部分共享(与协调工作相关)数据,通知客户端这部分数据的变化,并监控客户端的failuresHDFS自动failover 的实现依赖于Zookeeper 的以下功能:

1.       Failure detection:集群中每个Namenode都会在ZooKeeper中有个persistent session。如果机器故障,则它对应于Zookeeper中的session会过期,继而通知其他namenode,表示一个failover需要被触发了

2.       Active NameNode electionZookeeper 提供了一个简单的机制用于选出一个节点作为Active节点。如果当前active namenode 出现故障,另一个节点会在Zookeeper内获取一个特殊的锁,表示它应该成为下一个active

ZKFailoverControllerZKFC)是一个新的组件。它是一个Zookeeper 客户端,也可以监控并管理NameNode 的状态。每个运行namenode的机器也会运行一个ZKFC,它负责以下功能:

1.       Health monitoringZKFC根据health-check 命令定期ping本地Namenode。只要namenode定期返回healthy 状态,ZKFC认为此节点是healthy的。如果节点crash,或是unhealthy,则health monitor会将它标为unhealthy

2.       ZooKeeper session management:当本地NameNodehealthy的,ZKFC会在Zookeeper内保有一个session。如果本地是NameNode active,它同时也会保有一个特别的“lockznode。如果session过期,则lock znode会被自动删除

3.       ZooKeeper-based election:如果本地NameNodehealthy的,并且ZKFC没有其他节点在当前保有lock znode,它会尝试获取lock。如果成功,则它“赢得选举”,并负责执行failover,以让本地NameNode成为ActiveFailover的过程类似于上面提到的手动failover的过程:首先,(若有必要)前一个active被隔离,然后本地local NameNode转换为active 状态。

 

7. 部署ZooKeeper

在常规部署中,ZooKeeper 进程被配置于运行在3 个或5个节点。ZooKeeper是一个轻量级的服务,需要的资源也较少,所以可以将它部署在NameNode和Standby Node。

如果zookeeper 集群down掉,则之后hdfs自动failover不会被触发,但是正常运行的HDFS仍可以继续无影响(不受zookeeper宕机影响)运行。在Zookeeper再次启动后,HDFS会重新连接上zookeeper,且不会有其他影响。

 

8. 配置自动failover

在配置自动failover之前,需要停止集群。暂时并不支持从一个手动failover 模式无缝切换到自动failover模式

配置自动failover需要添加以下配置参数在hdfs-site.xml 中:

<property>

    <name>dfs.ha.automatic-failover.enabled</name>

    <value>true</value>

  </property>

 

Zookeeper 的地址,需要在core-site.xml 下给出:

<property>

    <name>ha.zookeeper.quorum</name>

    <value>ip-xx.xx.xx.xx:2181,ip-yy.yy.yy.yy:2181,ip-zz.zz.zz.zz:2181</value>

  </property>

 

在配置好以上参数后,可以通过以下命令初始化 zookeeper里所必须的状态,在任一NameNode上执行即可:

Hdfs zkfc -formatZK

它会创建一个znode,自动failover系统会存储它的数据在此znode。

在初始化znode数据后,即可以启动hdfs集群,不过这里也同时需要在各个集群节点启动 zkfc 守护进程:

hadoop-daemon.sh --script hdfs start zkfc

在emr中为:sudo start hadoop-hdfs-zkfc

 

9. Failover测试

首先我们看一下当期Active与Standby节点情况:

> hdfs haadmin -getAllServiceState

ip1:8020     active

ip2:8020     standby

 

其中 ip1 对应的 namenode为nn1,ip2对应的namenode为nn2。

在Zookeeper znode中,我们也可以看到对应信息:

> get /hadoop-ha/ha-nn-uri/ActiveBreadCrumb

ha-nn-uri


nn1)ip-1 �>(�>

> get /hadoop-ha/ha-nn-uri/ActiveStandbyElectorLock

ha-nn-uri


nn1)ip-1 �>(�>

 

然在ip-1重启namenode:

> hdfs haadmin -getAllServiceState

ip-1:8020     standby

ip-2:8020     active

 

在Zookeeper 中,查看对应znode信息:

> get /hadoop-ha/ha-nn-uri/ActiveBreadCrumb

ha-nn-uri


nn2)ip-2 �>(�>

> get /hadoop-ha/ha-nn-uri/ActiveStandbyElectorLock

ha-nn-uri


nn2)ip-2 �>(�>

 

References

https://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html

 

 

 

 

posted @ 2019-05-04 15:40  ZacksTang  阅读(1204)  评论(0编辑  收藏  举报