鲁春利的工作笔记,谁说程序员不能有文艺范?
1、目的
本文简略介绍了HDFS高可用性(HDFS High Availability简称HA)的特点,以及如何基于QJM(Quorum Journal Manager)来配置和管理HA HDFS集群。
本文假定阅读者已经大概了解了HDFS集群中的一般组件和节点类别,如需了解详细信息请参阅HDFS Architecture指南。
2、提示:QJM或Conventional Shared Storage
本文论述了通过QJM在Active NameNode和Standby NameNode间共享edit los的方式来配置和使用HDFS HA。若希望了解通过NFS共享存储的方式来配置和管理HDFS HA的话,请参考HA With NFS。
3、背景
Hadoop2.0.0之前,NameNode在HDFS集群中存在单点故障(SPOF : single point of failure)。每个集群只有一个NameNode节点,如果主机或进程不可用,则整个集群将陷入瘫痪,直到NameNode被重启或者其他NameNode接入。
如下两种情况将严重影响整个HDFS集群的可用性:
突发事件,如宕机,集群将不可用,直到维护人员NameNode;
系统维护,如NameNode节点的软件或硬件升级,需要停止NameNode节点,集群也将暂时失效。
HDFS HA的特性就是为了解决上述问题的。它在集群中同时运行2个(redundant)NameNode,并支持Active/Passive之间热备(hot standby)。这种HA机制保证了当机器宕机时可以快速转移故障恢复到一个新的NameNode,或者在系统维护时由管理员发起友好的failover。
4、架构
在典型的HA集群中,两个独立的机器配置NameNode节点。在任何时刻,只有一个NameNode处于Active状态,另一个处于Standby状态(passive)。Active NameNode处理集群中所有client请求,Standby NameNode作为slave用来保存好足够多的状态,以提供快速的故障恢复能力。
为了保证Active NN与Standby NN节点状态同步,即元数据保持一致。除了DataNode需要向两个NN发送block位置信息外,还构建了一组独立的守护进程”JournalNodes”, 用来同步FsEdits信息。当Active NN执行任何有关命名空间的修改,它需要持久化到一半以上的JournalNodes上。而Standby NN负责观察JNs的变化,读取从Active NN发送过来的FsEdits信息,并更新其内部的命名空间。一旦ActiveNN遇到错误,Standby NN需要保证从JNs中读出了全部的FsEdits,然后切换成Active状态。
HA需要保证在任何一个时间点,最多只有一个NameNode处于Active状态,否则两个Namenode将会分别有两种不同的数据状态,可能会导致数据丢失,或者其它不可预见的错误,这种情况通常称为”Brain Split”(脑裂,即集群中不同的Datanodes却看到了两个Active Namenodes)。对于JNS(Journal Nodes)而言,任何时间点只允许一个Namenode作为writer向其写FsEdits信息;在failover期间,原来的Standby Node将会接管Active的所有职能,并负责向JNS写入日志记录,这就阻止了其他Namenode基于处于Active状态的问题。
5、硬件资源
为了构建HA集群架构,你需要准备如下资源:
NameNode 机器:两台同等硬件配置的机器,分别运行Active and Standby NameNodes,
JournalNode 机器:运行JournalNodes的主机。JouralNode守护进程相当的轻量级,它们可以和hadoop的其他进程部署在一起,比如Namenodes、jobTracker、ResourceManager等。注意:至少要有3个JournalNode守护进程,因为edits操作必须在多数派上写入成功,此时只能容忍单台机器故障。你可以运行多于3个JournalNode进程,但为了真正能够增加应用中系统能够容忍的故障机器数,建议运行奇数个JNs(如3、5、7个等)。当运行N个JournalNode时,那么系统允许(N-1)/2个JNS进程失效而不影响工作。
提示:在HA集群中,Standby NameNode同样可以执行namespace状态的checkpoint,因此,就不需要在HA集群中运行SecondaryNamenode、CheckpointNode或者BackupNode。事实上,HA架构中运行上述节点,将会出错(不允许)。 (不明白)This also allows one who is reconfiguring a non-HA-enabled HDFS cluster to be HA-enabled to reuse the hardware which they had previously dedicated to the Secondary NameNode.
6、部署--配置
配置概述
和HDFS Federation类似,HA配置向后兼容,支持单NameNode运行而无需做任何修改。新的配置中,集群中所有的Nodes都有相同的配置文件,而无需根据不同的Node设定不同的配置文件。类似于HDFS Federation,HA集群重用了“nameservice ID”来标识一个HDFS 实例(事实上它可能包含多个HA Namenods)。此外,“NameNode ID”概念被添加到HA中,集群中的每一个NameNode都有一个NameNode ID来相互区分。为了实现单个配置文件支持所有的Namenodes,相关的配置参数以Nameservice ID和NameNode ID结尾。
配置细节
为了完成HA NameNodes配置,你需要在hdfs-site.xml文件中增加一些配置选项:
配置文件中参数的顺序不重要,但是你设定的参数dfs.nameservices和dfs.ha.namenodes.[nameservice ID]的value值决定了后续参数的key值。因此,在设定后续的配置参数之前你应当已经确定了上述配置参数的值。
dfs.nameservices - nameservice的逻辑名称
为nameservice选定一个逻辑名称,该名字可以是任意的,比如“mycluster”,然后在配置选中中使用该逻辑名称。它将应用于配置中以及作为集群中HDFS绝对路径的权限组件。
提示:如果在HDFS Federation中使用,配置设置还应包括其他nameservices列表,HA或其他,以逗号(,)分割。
<property> <name>dfs.nameservices</name> <value>mycluster</value> </property>
dfs.ha.namenodes.[nameservice ID] - nameservie中每个NameNode的唯一标识符
配置以逗号分割的NameNode ID列表,用来让DataNode识别集群中的NameNode。比如之前已经使用“mycluster”作为nameservice ID,然后想要使用“nn1”和“nn2”分别作为NameNode ID,那么配置如下:
<property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property>
注意:目前每个nameservice最多只能配置两个NameNode。
dfs.namenode.rpc-address.[nameservice ID].[name node ID] - NameNode RPC地址
对于之前配置的NameNode ID,设定NameNode进程监听的主机名及通信端口,每个NameNode ID对应一个配置选项,比如:
<property> <!-- 处理客户端请求的RPC地址,对于HA/Federation这种存在多个NameNode的情况, 集群的nameservice ID需要添加上,如dfs.namenode.rpc-address.EXAMPLENAMESERVICE --> <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>
注意:如果你愿意,也可以配置dfs.namenode.servicerpc-address参数。
dfs.namenode.servicerpc-address: 该参数配置了HDFS服务通信的RPC地址。 若配置了该地址,则BackupNode, Datanodes和所有其他HDFS服务将与该地址通信; 若未单独配置该参数,则默认采用dfs.namenode.rpc-address.mycluster.nn1配置的值。 对于HA/Federation这种存在多个NameNode的情况,需要添加上集群的nameservice ID, 如dfs.namenode.rpc-address.EXAMPLENAMESERVICE。
dfs.namenode.http-address.[nameservice ID].[name node ID] - NameNode HTTP地址
和rpc-address类似,配置了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's security特性,则需要为每一个NameNode配置https-address地址。
dfs.namenode.shared.edits.dir - 标识一组JNs的URI地址,NameNode写入或读取edits
<!-- 在我搭建的自己的集群中,内容为: --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://nnode:8485;dnode1:8485;dnode2:8485/cluster</value> </property>
配置JNS组的url地址,这是一个共享存储区,Active Namenode写入,Standby Node读取以便保持Active Namenode对于文件系统所做的最新更改。尽管可以配置多个JournalNode 地址,但是你只能配置一个URL,URL的格式为:"qjournal://host1:port1;host2:port2;host3:port3/journalId"。其中Journal ID在nameservice中是unique identifier,尽管没有强制要求,但一般是将nameservice ID作为journal identifier。
举例来说, 如果当前集群中JournalNode运行在主机 "node1.example.com", "node2.example.com", 和"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的Java类
DFS Client通过此类来判断哪个NameNode处于Active,并与它保持通信。目前hadoop中唯一的实现类为"ConfiguaredFailoverProxyProvider"。
<property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property>
dfs.ha.fencing.methods - 在failover期间用来隔离(fence) Active NameNode的脚本或Java类列表
系统的正确性要求任意时刻都只能有一个NameNode是Active状态。虽然QJM可以确保集群中只有一个Active Node写入edits,这对保护edits一致性很重要,可以保证不会出现由于脑列现象(split-brain scenario)导致的文件系统元数据不一致。但是在failover期间,有可能Acitive Node仍然存活,Client可能还与其保持连接提供旧的数据服务,我们可以通过此配置,指定shell脚本或者java程序,SSH到Active NameNode然后Kill Namenode进程。它有两种可选值:
sshfence - SSH到Active NameNode然后kill掉NameNode进程
为了该fencing选项可以工作,需要能够通过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>
也可以配置非标准的用户名或者密码来实现SSH。或者可以配置SSH的超时时间,超过该时间则fencing方法被认为失败。
<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 - 运行shell脚本来隔离Active NameNode
<property> <name>dfs.ha.fencing.methods</name> <value>shell(/path/to/my/script.sh arg1 arg2 ...)</value> </property>
“()”之间为shell脚本的路径,以及参数列表。
另外,在shell脚本中可以引用hadoop配置的变量,只是需要把配置的“-”替换为“.”。
<property> <name>dfs.ha.fencing.methods</name> <value>shell(/path/to/my/script.sh --nameservice=$target_nameserviceid $target_host:$target_port)</value> </property>
shell脚本返回0则认为fencing方法成功,否则认为失败。在这里无法配置fencing的timeout,若确实必要,在shell脚本中实现。
fs.defaultFS - 当访问HDFS而未指定绝对路径时,Hadoop FS client使用的默认路径前缀
现在可以配置HA的逻辑URI作为Hadoop客户端使用的默认路径,比如之前使用“mycluster”作为nameservice ID,那么它就可以作为所有HDFS路径的公有部分,在core-site.xml中配置如下:
<property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property
dfs.journalnode.edits.dir - JournalNode进程存储存储本身状态的路径
JournalNode机器上的绝对路径,用来存储edits和节点被JNs使用的自身状态。对于该配置可以只使用一个单独的路径,但这部分数据的冗余存储是通过运行多个独立的JournalNodes来实现的,或者将这些目录指向本地挂载的RAID(locally-attached RAID array)。比如:
<property> <name>dfs.journalnode.edits.dir</name> <value>/path/to/journal/node/local/data</value> </property>
7、部署-部署细节(Deployment details)
上述配置设置完毕后,就可以在需要运作JournalNode进程的机器上依次执行“hadoop-daemon.sh start journalnode”脚本来启动。一旦JournalNodes启动成功,它们将会从Namenode上同步metadata。
1、如果你的HDFS集群是新建的,那么需要在每个Namenode上执行"hdfs namenode -format"指令。
2、如果你的namenodes已经format了,或者是将non-ha转换成ha架构,你应该在将其中一个namenode上的metadata复制到另一台上(dfs.namenode.name.dir目录下的数据)。复制方式为在那个没有format的新加入的namenode上执行"hdfs namenode -bootstrapStandby"。运行这个指令需要确保JournalNodes(dfs.namenode.shared.edits.dir)中持有足够多的edits。
3、如果你将一个non-ha的Namenode(比如backup,其已经formated)切换成HA,你需要首先运行"hdfs -initializeSharedEdits",这个指令将本地Namenode中的edits初始化Journalnodes。
此后,你就可以启动HA Namenodes。可以通过配置指定的HTTP地址(dfs.namenode.https-address)来查看各个Namenode的状态,Active or Standby。
8、管理员命令
HA集群启动后,我们可以通过一些指令来管理HDFS集群。“bin/hdfs haadmin -DFSHAAdmin”指令,其可选参数:
[hadoop@nnode ~]$ hdfs haadmin -DFSHAAdmin Usage: DFSHAAdmin [-ns <nameserviceId>] [-transitionToActive <serviceId> [--forceactive]] [-transitionToStandby <serviceId>] [-failover [--forcefence] [--forceactive] <serviceId> <serviceId>] [-getServiceState <serviceId>] [-checkHealth <serviceId>] [-help <command>]
1、-transitionToActive <namenodeid>与-transitionToStandbyl <namenode id>:将指定的namenode ID切换为Active或者standby。这个指令并不会触发“fencing method”,所以不常用,我们通常使用"hdfs haadmin -failover"来切换Namenode状态。
2、-failover[--forcefence] [--foreactive] <serviceId-fist> <serviceId-second>:在两个Namenode之间failover。这个指令会触发将first节点failover到second节点。如果first处于standby,那么只是简单的将second提升为Active。如果first为Active,那么将会友好的 将其切换为standby,如果失败,那么fencing methods将会触发直到成功,此后second将会提升为Active。如果fencing method失败,那么second将不会被提升为Active。
例如:"hdfshaadmin -DFSHAAdmin -failover nn1 nn2"
3、-getServiceState<serviceId>:获取serviceId的状态,Active还是Standby。链接到指定的namenode上,并获取其当前的 状态,打印出“standby”或者“active”。我可以在crontab中使用此命令,用来监测各个Namenode的状况。
4、-checkHealth<serviceId>:检测指定的namenode的健康状况。
9、Automatic Failover
上述介绍了如何配置手动failover,在这种模式下,系统不会自动触发failover,即不会将Standby提升为Active,即使Active已经失效。接下来介绍如何实现自动failover。
组件
Automatic Failover中,增加了2个新的组件:zookeeper集群,ZKFailoverController进程(简称为ZKFC)。
Zookeeper是一个高可用的调度服务,可以保存一系列调度数据,当这些数据变更(notify)时可以通知Client,以及监控(montitor)Clients失效,自动failover的实现将依赖于Zookeeper的几个特性:
1、Failure delection:失效检测,每个Namenode将会和zookeeper建立一个持久session,如果Namenode失效,那么次 session将会过期失效,此后Zookeeper将会通知另一个Namenode,然后触发Failover。
2、Active Namenode election:zookeeper提供了简单的机制来实现Acitve Node选举,如果当前Active失效,Standby将会获取一个特定的排他锁(lock),那么获取(持有)锁的Node接下来将会成为 Active。
ZKFailoverControllor(ZKFC)是一个zookeeper客户端,它主要用来监测和管理Namenodes的状态,每个Namenode机器上都会运行一个ZKFC程序,它的职责为:
1、Health monitoring:ZKFC间歇性的使用health-check指令ping本地的Namenode,Namenode也会及时的反馈自己的 health status。如果Namenode失效,或者unhealthy,或者无响应,那么ZKFC将会标记其为“unhealthy”。
2、Zookeeper session manangement:当本地Nanenode运行良好时,ZKFC将会持有一个zookeeper session。如果本地Namenode为Active,它同时也持有一个“排他锁”(znode);这个lock在zookeeper中为 “ephemeral” znode(临时节点),如果session过期,那么次lock所对应的znode也将被删除。(参见zookeeper特性)。
3、Zookeeper-based election:如果本地Namenode运行良好,并且ZKFC没有发现其他的的Namenode持有lock(比如Active失效后,释放了lock),它将尝试获取锁。如果获取成功,即“赢得了选举”,那么此后将会触发failover把本地Namenode标记为Active。该failover进程类似于手动触发failover,首 先,调用fencing method,然后提升本地Namenode 为Active。
具体Failover过程和详细内容,请参见HDFS-2185。
部署ZooKeeper
通常ZooKeeper进程配置到3或5个节点上,由于ZooKeeper本身只需要少量的资源,因此可以和NameNode或者Standby Node部署在同一个机器上。大部分的使用人员选择将第三方的ZooKeeper和YARN ResourceManager部署在同一个节点上。为了获取较好的性能和实现隔离性,建议ZooKeeper节点在单独的磁盘上存储来自于HDFS元数据的信息。
本文档中不介绍ZooKeeper的安装,我们假定你已经在3个或更多个节点上安装并运行了ZooKeeper集群,并通过ZK CLI操作验证了其运行的正确性。
Automatic Failover的配置中,需要在配置文件hdfs-site.xml中增加两个新的配置参数:
<!―声明该集群启用了automatic failover --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property>
此外还需要在core-site.xml中,增加如下配置:
<!―运行ZooKeeper服务的主机-端口列表 --> <property> <name>ha.zookeeper.quorum</name> <value>zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181</value> </property>
上述zookeeper集群为即备,尽可能选择相对平稳的zk集群。
其中"dfs.ha.automatic-failover.enabled"可以为每个nameservice ID分别配置:dfs.ha.automatic-failover.enabled.[nameservice ID]。此外在core-site.xml中还可以配置Zookeeper Client的相关参数,比如sessionTimeout,这些配置项以"ha.zookeeper"开头,其中"dfs.ha."开头的部分配置项可 以用来设定fencing method的相关控制。
上述准备工作结束后,我们还需要在zookeeper中初始化HA的状态,通过执行“hdfs zkfc -formatZK”,此命令将会在zookeeker中创建一个znode,用来保存HA或failover的数据。
hdfs zkfc -formatZK
启动集群
以使用"start-dfs.sh"这个便捷的指令,它启动了hdfs所需要的所有守护进程,当然包括ZKFC。也可以使用"hadoop-daemon.sh start zkfc"手动启动ZKFC客户端。
验证Failover
一旦Automatic Failover集群启动之后,我们需要检测Failover是否符合预期。首先,我们需要通过命令(getServiceState)或者在 Namenode的Web UI上查看各个Namenode的状态,确认两个Namenode是否分别处于Active和Standby;此后,你可以手动关闭Active Namenode,比如使用kill -9 <pid num>,在确定Acitve Node失效后,再次检测原来的Standby是否已经提升为Active;不过因为zookeeper session过期判定需要达到sessionTimeout(可配置,ha.zookeeper.session-timeout),这个 failover过程可能需要滞后数秒,默认为5秒。
如果没有按照预期failover,那么你需要检测配置文件是否正确,zk服务是否正确。此外,我们还可以使用上述DFSHAAadmin指令多次尝试。
10、FAQ
1、ZKFC和Namenodes守护进程的启动顺序是否重要?
No,对于指定的Namenode,你可以在其之前或者之后启动ZKFC均可以,ZKFC只是调度Namenode的存活状态,如果不启动ZKFC,此Namenode将无法参与自动failover过程。
2、是否需要额外的monitoring?
你需要在Namenode机器上,添加额外的monitor用来监控ZKFC是否运行。在某些情况下,zookeeper集群的故障可能导致ZKFC意外中断,你需要适时的重启ZKFC。此外,还需要监控Zookeeper集群的运行状况,如果Zookeeper集群失效,那么HA集群将无法 failover。
3、如果Zookeeper失效,将会怎么样?
如果zookeeper集群故障,那么Automatic Failover将不会触发,即使Namenode失效,这也意味着ZKFC无法正常运行。不过,如果Namenodes正常(即使有一个失效),那么 HDFS系统将不会受到影响。因为HDFS Client并没有基于zookeeper做任何事情,但zookeeper集群仍需要尽快的恢复以避免当前Active失效而造成的“split- brain”等问题。
4、是否可以在Namenodes之间指定优先级?
NO,这是不能支持的。首先启动的Namenode将作为Active,我们只能认为控制Namenode启动的顺序来做到“优先级”。
5、在Automatic Failover中,手动Failover怎么做?
和普通的Failover一样,我们总是可以通过"hdfs haadmin -DFSHAAdmin -failover"来实现手动Failover。