背景
Hadoop2.0.0之前,NameNode存在单点故障问题,每个集群中只有一个NameNode,一旦机器损坏或者进程出了问题,在问题被修复(重启进程或者机器)之前整个集群都处于不可用的状态。
HDFS集群的可用性主要是指在遇到以下两种情况的时候仍然可以对外不间断的提供服务:
1. 不可预测的硬件故障!
2. 有计划的软件升级或维护!
HDFS的HA特性通过在同一个集群中同时运行一个Active和一个Standby状态的NameNode来解决上面的问题。当Active状态的NameNode节点意外宕机之后Standby NameNode迅速切换为Active NameNode,或者在软件升级的时候通过管理员命令平滑的切换来解决NameNode的可用性。
架构
在典型的HA集群中,两台独立的物理节点都被配置为NameNode,在任意时刻都只会有一个Active状态的NameNode,而另一个则是Stadnby状态的。Active NameNode负责为所有客户端提供服务,Standby NameNode同步集群状态,在集群故障时快速的进行Failover。
Standby NameNode通过JournalNodes来同步Active NameNode的元数据信息。当Active NameNode的namespace被修改之后,日志记录信息就会被持久化到大部分的JournalNode节点上,Standby NameNode 会一直监控JournalNode节点上的编辑日志,当发现编辑日志有所改变后会读取这些编辑日志并合并到自己的namespace中。当故障切换发生时,Standby NameNode在成为Active状态之前会确保已经读取JournalNode上的所有edit log,避免元数据的不完整。
为了尽量减少故障切换所消耗的时间,Standby NameNode存储集群中block的位置信息也是必要的。为了实现这个功能,集群中所有的DataNode节点会配置好所有NameNode节点的信息,然后为所有NameNode节点发送块的位置信息和心跳。
在HA集群中确保只有一个Active NameNode是非常重要的,否则namespace的状态就会出现紊乱,数据就会有所丢失。为了确保这一点,JournalNodes在同一时刻只会允许一个NameNode往里面写数据。当故障切换发生时,只有即将成为Active状态的那个NameNode才有往JournalNode写的权限,这也有效避免了其他NameNode成为Active状态。
硬件资源
NameNode machines - 用来部署Active NameNode和Standby NameNode节点的机器配置应该一致(与在非Ha状态下机器的配置一样即可)。
JournalNode machines -JournalNode服务是轻量级服务,所以没有必要部署到单独的节点上,可以运行在NameNode节点、ResourceManager节点上。注:JournalNode至少需要部署3台机器节点上,或者更多奇数台节点上,只要不超过(N-1)/2台节点挂掉的话JournalNode仍然可以正常工作。
在HA集群中,Standby NameNode同时也完成了namespace的checkpoint工作,所以不需要在运行Secondary NameNode, CheckpointNode, or BackupNode。实际上如果你运行这些服务的话会出错。
部署
和Federation配置相同,Ha配置时向后兼容的和允许现存的单节点NameNode配置保持不变。这个配置对所有的节点中都使用相同的配置。
向HDFS Federation一样,Ha集群重新使用nameservice ID 来区分不同的HDFS实例,另外,另一个被称为 NameNode ID (不同的NameNode由不同的NameNode ID 进行区分)的元素被添加到Ha中。为了使单一的配置文件满足所有的NameNode的需求,相关的配置参数后缀使用nameservice ID和NameNode ID。
配置详情
在hdfs-site.xml配置文件中添加:
- dfs.nameservices - nameservice的逻辑名称
选择此命名服务的逻辑名称,例如“mycluster”,在其他配置选项的中使用这个逻辑名。可以选择任意的名称。它将用于配置和HDFS集群中的绝对路径的权限组件。
注:如果使用了HDFS Federation,则需要添加nameserivces的列表,使用“,”隔开。
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
- dfs.ha.namenodes.[nameservice ID] - 在nameservices中的namenodes唯一的标示符
配置nameservice ID使用“,”隔开,这也使DataNodes发现集群中所有的名字节点。例如之前使用“mycluster”作为nameservices,如果想使用 “nn1” 和 “nn2” 作位NameNode的ID;
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
注:每一个nameservices最多只能配置两个名字节点
- 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>
注:如果需要的话你也可以配置”servicerpc-address”。
- dfs.namenode.http-address.[nameservice ID].[name node ID] - 每个NameNode监听的Http地址
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>machine1.example.com:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>machine2.example.com:50070</value>
</property>
注:如果启用了Hadoop的security特性,则需要为每个NameNdoe设置https-address。
- dfs.namenode.shared.edits.dir - 这个URL用来鉴别JNs中用来读或写edits的组, NameNode从JournalNode中读写的命名空间
这是一个配置的journalnodes提供共享编辑存储的地址,被Active NameNode写和StandBy NameNode读。你必须指定几个journalnode地址,但只需要配置一个URI。URI的格式应为:“qjournal://host1:port1;host2:port2;host3:port3/journalId”,journalId是命名服务的唯一表示符,一般使用nameservice ID来作为journalId。
例如:如果这个集群的JournalNodes运行在”node1.example.com”, “node2.example.com”, and “node3.example.com”这三台机器上和nameservice ID为mycluster。(默认为journalnode端口是8485)
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster</value>
</property>
- dfs.client.failover.proxy.provider.[nameservice ID] - HDFS 客户端通过该类找到当前Active NameNode
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
- dfs.ha.fencing.methods - 在NameNode进行故障切换的时候进行fence的脚本或者Class
集群在特定时间只有一个NameNode是系统正确性的保障。。当使用QJM,只有一个NameNode将允许写入JournalNodes,因此不存在脑裂现象。然而,当发生故障转移,以前的Active NameNode还会出现可以响应客户请求,这可能是过期的,可能直到NameNode关闭,一直试图写入journalnodes。因此,即使使用Quorum Journal Manager,还需要配置fence方法,即使fence机制也可能是失败的,但是它确实提高了系统的高可用性,注:如果没有实际的fence方法,仍然必须配置此设置的东西,例如“shell(/ bin / TRUE)”。
在failover过程中,在arriage-return-separated列表中的fence方法将被使用,直到一个fence方法成功。如果需要自己实现fence,需要参考org.apache.hadoop.ha.NodeFencer类。有两种配置方案:ssh和sshfence
-
- sshfence - SSH to the Active NameNode and kill the process
sshfence选项使用ssh登录到目标节点和使用fuser杀死监听服务的TCP端口的进程。为了是这个脚本可以使用,必须能够ssh无密码登录到目标机器中,因此必须配置dfs.ha.fencing.ssh.private-key-files
- sshfence - SSH to the Active NameNode and kill the process
<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>
可选配置
# 可以配置非标准用户名或端口来执行SSH
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence([[username][:port]])</value>
</property>
# 超过特定时间,就可以说明失败了
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
-
- shell - run an arbitrary shell command to fence the Active NameNode
<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>
</property>
()里面为参数以及是可选的。
该命令将运行环境设置为包含所有当前Hadoop的配置变量,用“_“字符替换”.”。(具体细节参考原文)
注:Active和Standby NameNode节点之间需要做好免SSH验证登录。
- dfs.journalnode.edits.dir - 本地存储JournalNode状态的目录
这是JournalNode机器存储edits和其他状态的地址。
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/path/to/journal/node/local/data</value>
</property>
部署
配置完成之后可以通过
hadoop-daemon.sh start journalnode
启动JournalNode进程。
当JournalNode进程启动之后需要同步两台NameNode节点上的元数据信息。
- 如果是新配置的HDFS集群则在其中一个NameNode节点上运行format命令。
- 如果已经formated过NameNode节点或者是将一个非HA集群配置为HA集群,则需要拷贝NameNode的元数据目录到新部署的NameNode节点上,通过在新的NameNode节点上运行
hdfs namenode -bootstrapStandby
命令。这个命令也会确保JournalNodes包含足够的edits来启动所有的NameNodes。 。
- 如果是将一个非HA集群转换为一个HA集群,你还需要运行
hdfs -initializeSharedEdits
命令,将NameNode本地的edits目录初始化到JournalNode中。
这时候再去启动每个NameNode节点。
可以通过查看每个NameNode的Web界面来验证是否配置成功,一个active,一个standby。
故障自动切换
上面的章节部分介绍的是如何手动配置故障切换,但是这个切换并不是自动的,当Active NameNode发生故障时Standby NameNode无法自动切换为Active状态,下面介绍如何配置和部署NameNode的自动故障切换。
组件
自动故障切换需要增加两个新的组件:Zookeeper集群,ZKFailoverController进程(ZKFC)。
Zookeeper在NameNode的自动故障切换中起到以下两点作用:
- Failure detection:每个NameNode在Zookeeper中维护了一个持久性的session会话,如果机器宕机了,保存在Zookeeper中的session就会过期失效,这个时候NameNode的故障切换行为就会被触发。
- Active NameNode election:Zookeeper提供了一个机制用来选择一个节点作为Active NameNode。如果Active节点宕机了,另一个节点会在Zookeeper中持有一把锁来指明该节点即将成为Active NameNode
ZKFC 是一个Zookeeper客户端,用来监控和管理NameNode状态,每个运行了NameNode服务的节点也同样需要运行ZKFC服务,主要作用如下:
- Health monitoring - ZKFC 周期性的ping 本地的NameNode来进行health-check。如果NameNode及时的响应一个健康的状态,那么ZKFC就会认为该NameNode当前为健康。如果节点处故障了,则会被标记为不健康状态。
- ZooKeeper session management - 当本地的NameNode为健康状态时,ZKFC会在Zookeeper中打开一个session会话。当本地的NameNode为Active NameNode时,ZKFC也会维持一个特殊的”lock”znode。这个znode是瞬态的,如果session过期,这个”lock”znode就会被自动删除。
- 如果本地NameNode为健康状态,ZKFC发现当前没有其他节点持有lock znode,就会自己持有这个znode。如果成功了,则运行failover来确保当前NameNode成为Active NameNode。这个理的failover进程类似手动进行failover:首先之前的Active NameNode进行fence,然后当前节点的NameNode进入Active状态。
部署zookeeper
Zookeeper集群至少部署3台机器节点上,Zookeeper服务是轻量级服务,不会占用太多资源,可以部署到其他服务节点上。为了获得更好的性能和数据隔离,资建议将Zookeeper的数据存放在和HDFS元数据不同的磁盘上。
安装Zookeeper集群已经超过本文档内容的范围,我们假定你已经可以正确安装部署Zookeeper集群。
开始之前
在配置自动故障切换之前先将集群关闭,目前不支持在运行的集群上配置自动故障切换。
配置自动failover
在hdfs-site.xml中添加如下配置:
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
在core-site.xml中添加如下配置:
<property>
<name>ha.zookeeper.quorum</name>
<value>zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181</value>
</property>
列表中是运行zookeeper service服务的集群的主机名和端口号。
如果使用了Federation机制,则需要:
dfs.ha.automatic-failover.enabled.my-nameservice-id
在zookeeper中初始化Ha状态
在其中一个NameNode中运行
$ hdfs zkfc -formatZK
这将创建一个Znode节点在zookeeper上,用于存储状态数据。
使用start-dfs打开集群
start-dfs.sh
自动故障切换启用后,start-dfs.sh脚本会自动启动每个NameNode节点上的ZKFC服务。当ZKFC服务启动后会自动选择一个NameNode作为Active NameNode的。
手动开集群
在每个运行NameNode机器上,手动打开zkfc守护线程
$ hadoop-daemon.sh start zkfc
关于安全机制的配置参考原文!