本指南提供一个HDFS HA特性的综述,描述了如何使用QJM配置和管理一个HA HDFS集群。本文档假设读者对于HDFS中的通用组件和节点类型有一个大体的认识。请参考HDFS架构指南获取更多信息。
注意:使用QJM或者传统的共享存储
本文档讨论了如何用QJM配置和使用HDFS HA在Active NameNode和Standby NameNode共享edit日志文件。关于如何使用NFS代替QJM作为共享存储配置HDFS HA的信息,请看High Availability With NFS。
在Hadoop2.0.0之前,NameNode在HDFS集群中是单点故障的。每一个集群都有一个NameNode,如果NameNode所在的机器或者进程变得不可用,在NameNode重新启动或者在另外一台机器上启动之前,整个集群将变得不可用。
这从两个方面影响了HDFS集群的可用性:
HDFS的HA特性解决了上述问题,通过提供一个选择,在一个集群中,配置Active/Passive两个冗余的NameNode,进行热备份。
这允许在机器当即或者优雅的进行管理员发起有计划的维护目的的故障转移的情况下可以快速的故障转移到一个新的NameNode。
在一个典型的HA集群中,两个独立的机器被配置为NameNode。在任何时间点,仅有一个NameNode在Active状态,另一个是Standby状态。Active NameNode负责处理集群中所有客户端的操作,同时,Standby NameNode简单的作为slave,保持足够的状态信息在必要的时候提供快速的故障转移。
为了使Standby NameNode保持它的状态与Active NameNode同步,两个NameNode节点用一组单独的守护进程通信,这组守护进程名叫JournalNodes。当任何的namespace的修改发生在Active NameNode上,它记录一条修改记录到这些JNs中的大多数。Standby NameNode能够从JNs中读取这些edit,不断的观察这组JNs中edit的变化。当Standby NameNode看到这些edit的时候,它应用这些edit到它自己的namespace。在发生故障转移的时候,StandbyNameNode在提升它自己为Active NameNode之前,将确保它读取了所有来自JNs的edit。这将保证故障转移发生前,namespace的状态被完全同步。
为了提供一个快速的故障转移,Standby NameNode有最新的关于集群中Block的位置信息是必要的。为了实现这个目的,DataNode用两个NameNode的位置去配置,然后发送Block位置信息和心跳给两个NameNode。
一个集群在同一时间只有一个NameNode是Active状态对于正确的操作是至关重要的。否则,namespace的状态将会快速的在两个NameNode之间产生分歧,可能会丢失数据或者产生其他不正确的结果。为了保证这个属性和防止所谓的split-brain场景,JNs将永远只允许一个NameNode往其写入。在故障转移期间,将要成为Active 的NameNode将只是简单的接管JNs的写角色,这将有效的防止其他的NameNode继续在Active状态,使新的Active NameNode安全的处理故障转移。
为了部署一个HA 集群,你应该准备下面这些:
注意,在一个HA集群中,Standby NameNode也可以执行namespace状态的Checkpoint,因此,在一个HA的集群中,没有必要再运行一个Secondary NameNode,CheckpointNode或者BackupNode。事实上,如果这么做,这将是个错误。这也允许你重用在non-HA的HDFS集群中用来做为Secondary NameNode的机器将集群配置成HA的。
与联邦配置相似,HA配置是向后兼容的,允许一个存在的单NameNode的集群配置不用改变就可以工作。新的配置被设计成假设集群中所有的节点可能具有相同的配置,没有根据节点的类型部署不同的配置文件到不同的机器。
就像HDFS联邦,HA集群重用nameservice 的ID来标识一个HDFS实例,但事实上这个实例可能包含多个HA的NameNode节点。另外,一个新的概念NameNode ID被HA增加。每一个NameNode都有一个不同的NameNode ID来区分彼此。为了支持所有NameNode一个配置文件,相关的配置用需要增加nameservice ID 和 NameNode ID前缀。
为了配置一个HA NameNode,你必须增加几个配置选项到你的 hdfs-site.xml配置文件中。
这些配置的顺序是不重要的,但是dfs.nameservices 和dfs.ha.namenodes.[nameservice ID] 选择将决定下面的配置的key的名称。因此,你应该在配置剩下的配置先决定这两个配置的值。
为nameservice选择一个逻辑名称,例如,“mycluster”,使用这个逻辑名称作为该配置选项的值。你选则的name是任意的。它将在集群中其他的属性配置和作为HDFS绝对路径的authority 部分被使用。
注意:如果你同时使用HDFS联邦,这个配置的设置应该包括其他的nameservice,不管是不是HA的集群,逗号分隔开。
<property> <name>dfs.nameservices</name> <value>mycluster</value> </property>
用逗号分隔的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。
对于两个先前配置的NameNode的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>
注意:如果你愿意,也可以配置RPCservicerpc-address设置。
4. dfs.namenode.http-address.[nameservice ID].[name node ID]:每个NamNode的监听地址,全限定名的HTTP地址。与上边的rpc-address类似,设置两个NameNode的Http Server的监听地址。例如:
<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的安全特性,你也应该同样地为每个NameNode设置https-address的地址。
这是提供共享edit存储的一组JNs的地址,这些地址,被Active NameNode写入,被Standby NameNode读取,来保持Active NameNode产生的文件系统的所有的修改信息的最新版本。虽然你必须指定多个JournalNode地址,你应该只配置这些URL地址中的一个。URL应该是这种格式"qjournal://host1:port1;host2:port2;host3:port3/journalId"。
这个Journal ID是这个nameservice唯一的标识符,这将允许一组JNs为多个联邦的命名空间系统提供存储。虽然不是必须的,重用nameservice ID作为Journal的标示符是一个好的想法。
例如,如果这个集群的一组JournalNode正运行在"node1.example.com", "node2.example.com", 和 "node3.example.com" 这3台机器上,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>
配置被HDFS客户端用来决定哪一个NameNode当前是Active的Java类,从而决定哪一个NameNode目前服务于客户端请求。目前只有一个实现,ConfiguredFailoverProxyProvider,所以除非你用了自定义的类否则就用这个。例如:
<property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property>
在任意给定的时刻只有一个NameNode处在Active状态对于系统的正确性是积极地。重要的是,当使用QJM时,永远将只有一个NameNode被允许写入到JNs中,所以对于从一个split-brain 场景崩溃文件系统元数据没有可能。然而,当发生一次故障转移时,先前Active 的NameNode可以处理客户端的读请求是可能的,当试图写入JNs时,这个NameNode可能直到关机才失效。因为这个原因,即使在使用QJM的时候,配置 fencing methods也是值得的。然而,为了提高系统在fencing机制失效时的可用性,建议在列表的最后配置一个总是返回success的fencing method 。注意,如果你选择使用不真实的方法,你仍然需要为此配置些东西,例如,shell(/bin/true)。
故障转移期间使用的fencing methods被配置为回车分隔的列表,它们将顺序的被尝试知道一个显示fencing 已经成功。有两个与Hadoop运行的方式:shell和sshfence。关于管多的实现你自定义的 fencing method,查看org.apache.hadoop.ha.NodeFencer类。
1> sshfence:SSH到Active NameNode然后杀掉这个进程
sshfence选择SSH到目标节点,然后用fuser命令杀掉坚挺在服务的TCP端口上的进程。为了使 fencing option 工作,它必须能够免密码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 method 将被认为失败。它可能被配置成这样:
<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>
2> shell:运行任意的shell命令来fence Active NameNode
shell fence的方式会运行一个任意的shell命令,它可能配置成这样:
<property> <name>dfs.ha.fencing.methods</name> <value>shell(/path/to/my/script.sh arg1 arg2 ...)</value> </property>
括号之间字符串被直接传给bash shell命令,可能不包含任何关闭括号。
Shell命令将运行在一个包含当前所有Hadoop配置变量的环境中,在配置的key中用”_“代替”.“。所用的配置已经将任何一个namenode特定的配置改变成通用的形式。例如dfs_namenode_rpc-address将包含目标节点的RPC地址,即使配置可能指定的变量是dfs.namenode.rpc-address.ns1.nn1。
此外,下面的这些涉及到被fence的目标节点的变量,也是可用的:
$target_host | hostname of the node to be fenced |
$target_port | IPC port of the node to be fenced |
$target_address | the above two, combined as host:port |
$target_nameserviceid | the nameservice ID of the NN to be fenced |
$target_namenodeid | the namenode ID of the NN to be fenced |
这些环境变量可能被用来替换shell命令中的变量,例如:
<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,fence被认为是成功了。如果返回任何其他的结束码,fence不是成功的,列表中的下个fence方法将被尝试。注意:fence方法不实现任何的timeout。如果timeout是必要的,它们应该在shell脚本中实现(例如通过fork一个字shell在多少秒后杀死他的父shell)。
可选地,你现在可能为Hadoop客户端配置了默认使用的路径来用新的启用HA的逻辑URI。如果你用”mycluster“作为nameservice ID,这个id将是所有你的HDFS路径的的authority部分。这可能被配置成这样,在你的core-site.xml文件里:
<property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property>
这是一个在JournalNode机器上的绝对路径,此路径中存储了edit和JNs使用的其他的本地状态。你可能只用配置一个单独的路径。这些数据的冗余通过运行多个独立的JournalNode来实现,或者通过配置这个路径到一个RAID组。例如:
<property> <name>dfs.journalnode.edits.dir</name> <value>/path/to/journal/node/local/data</value> </property>
在所有必要的配置被设置之后,你必须启动所有的JournalNode守护进程。这可以通过运行“hdfs-daemon.sh journalnode“命令完成,然后等待守护进程在每一台相关的机器上启动。
一旦JournalNode被启动,你必须先同步两个HA NameNode磁盘上的元数据。
这个时候,你可能启动两个HA NameNode,就像你启动一个NameNode一样。
你可以访问分别每一个NameNode的web主页通过浏览他们配置的HTTP地址。你应该注意到配置的地址的下面将会是NameNode的HA状态(Standby或Active)。不管一个HA NameNode何时启动,它首先会处在Standby状态。
既然你的HA NameNode被配置和启动了,你将可以使用一些命令来管理你的HA HDFS集群。特别的,你应该熟悉“hdfs haadmin “命令的所有子命令。不加任何参数运行此命令将显示下面的帮助信息:
Usage: DFSHAAdmin [-ns <nameserviceId>] [-transitionToActive <serviceId>] [-transitionToStandby <serviceId>] [-failover [--forcefence] [--forceactive] <serviceId> <serviceId>] [-getServiceState <serviceId>] [-checkHealth <serviceId>] [-help <command>]
本指南描述这些子命令的高级用法。对于每一个子命令特殊的用法的信息,你应该运行“hdfs haadmin -help <command> “来获得。
这两个子命令使给定的NameNode各自转到Active或者Standby状态。这两个命令不会尝试运行任何的fence,因此不应该经常使用。你应该更倾向于用“hdfs haadmin -failover “命令。
这个子命令从第一个提供的NameNode到第二个提供的NameNode发起一次故障转移。如果第一个NameNode是Standby状态,这个命令将简单的将第二个NameNode转成Active状态,不会报错。如果第一个NameNode在Active状态,将会尝试优雅的将其转变为Standby状态。如果这个过程失败,fence method(就像dfs.ha.fencing.methods配置的)将会被顺序尝试直到有一个返回success。在这个过程之后,第二个NameNode将会被转换为Active状态。如果没有fence method成功,第二个NameNode将不会被转变成Active状态,将会返回一个错误。
连接到给定的NameNode来判断它目前的状态,打印Standby或者Active到合适的标准输出。这个子命令可以被需要根据NameNode目前的状态做出不同Action的定时任务或者监控脚本使用。
连接到给定的NameNode检查它的健康状况。NameNode可以在对自己进行一些检测,包括检查是否有内部服务正在运行。如果NameNode是健康的,这个命令将返回0,不是则返回非0值。你可以用这个命令用于检测目的。
注意:这还没有实现,现在将总是返回success,除非给定的NameNode完全关闭。
上边的部分描述了如果配置一个手工故障转移。在那种模式下,系统将不会自动触发一个故障转移,将一个NameNode从Active装成Standby,即使Active节点已经失效。这个部分描述了如何配置和部署一个自动故障转移。
自动故障转移增加了两个新的组件到HDFS的部署中:一个Zookeeper仲裁,ZKFailoverController进程(简称ZKFC)。
Apache Zookeeper是一个维护少量数据一致性的高可用的服务,通知客户端那些数据的变化,同时监控客户端的失效状况。HDFS的自动故障转移的实现下面的部分依赖Zookeeper:
ZKFC是一个新的组件,它是一个Zookeeper客户端,同时也用来监视和管理NameNode的状态。每一台运行NameNode的机器都要同时运行一个ZKFC进程,此进程主要负责:
一个参考自动故障转移的设计文档来获取更多的信息,参考Apache HDFS JIRA上的 HDFS-2185 设计文档。
在一个典型的部署中,Zookeeper守护进程配置为在3到5个节点上运行。因为Zookeeper本身有轻量级的资源需求,将Zookeeper守护进程跟HDFS的Active NameNode和Standby NameNode是可以接受的。许多管理员也选择将第三个Zookeeper进程部署到YARN ResourceManager一个节点上。建议将Zookeeper 节点存储它们的数据到一个单独的磁盘驱动,以为了更好的性能和解耦,与存放HDFS元数据的驱动程序分开。
Zookeeper的建立超出了本文档的范围。我们将假设你已经建立起了一个3个或者更多节点的ZooKeeper集群,已经通过用ZK CLI连接到ZKServer验证了其正确性。
在你开始配置自动故障转移之前,你应该关闭你的集群。目前,在集群运行过程中,从手工故障转移到自动故障转移的转变是不可能的。
自动故障转移的配置需要增减两个新的参数到你的配置文件中。在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服务的主机名-端口号信息。
就像本文档中先前描述的参数一样,这些设置可能用每一个nameservice的id做前缀来作为配置时的key。例如,在开启联邦的集群中,你可能需要明确的指定所要开启自动故障转移的nameservice,用 dfs.ha.automatic-failover.enabled.my-nameservice-id指定。
也有其他的配置参数,可能被用来管理自动故障转移的表现;然而,对于大多数的安装来说是不必要的。请参考特定文档获取更多信息。
在增加了配置之后,下一步就是初始化Zookeeper中的必要的状态。你可以通过在任意NameNode所在的主机上运行下面的名来来完成这个操作:
$ hdfs zkfc -formatZK
这将在Zookeeper中创建一个znode,自动故障转移系统存储它的数据这个znode。
因为自动故障转移在配置中已经被开启了,start-dfs.sh脚本将会在任意运行NameNode的机器上自动启动一个ZKFC守护进程。当ZKFC启动,它们将自动选择一个NameNode变成Active。
如果手工管理集群上的服务,你将需要手动在将要运行NameNode的机器上启动zkfc。你可以用下面的命令启动守护进程:
$ hadoop-daemon.sh start zkfc
如果你正在运行一个安全的集群,你将很可能希望确保存储在Zookeeper中的信息也是安全的。这将防止恶意的客户端修改Zookeeper中的元数据或者潜在地触发一个错误的故障转移。
为了确保Zookeeper中信息的安全,首先增加下面的配置到core-site.xml文件:
<property> <name>ha.zookeeper.auth</name> <value>@/path/to/zk-auth.txt</value> </property> <property> <name>ha.zookeeper.acl</name> <value>@/path/to/zk-acl.txt</value> </property>
请注意这两个值中的 “@”字符,这指出配置不是内联的,而是指向磁盘中的一个文件。
第一次配置的文件以跟ZK CLI使用的相同的格式指出了一个Zookeeper认证的列表。例如,你可能像下面这样指定一些东西:
digest:hdfs-zkfcs:mypassword
….. hdfs-zkfcs 是Zookeeper中全局唯一的用户名,mypassword 是一个唯一的字符串,作为密码。
下一步,根据这个认证,生成相应的Zookeeper ACL,使用类似于下面的命令:
$ java -cp $ZK_HOME/lib/*:$ZK_HOME/zookeeper-3.4.2.jar org.apache.zookeeper.server.auth.DigestAuthenticationProvider hdfs-zkfcs:mypasswordoutput: hdfs-zkfcs:mypassword->hdfs-zkfcs:P/OQvnYyU/nF/mGYvB/xurX8dYs=
复制和粘贴output”->“后的部分,写到zk-acls.txt文件中,要加上” digest: “前缀,例如:
digest:hdfs-zkfcs:vlUvLnd8MlacsE80rDuu6ONESbM=:rwcda
为了使ACL生效,你应该重新运行zkfc -formatZK命令。
做了这些之后,你可能需要验证来自ZK CLI的ACL,像下面这样:
[zk: localhost:2181(CONNECTED) 1] getAcl /hadoop-ha'digest,'hdfs-zkfcs:vlUvLnd8MlacsE80rDuu6ONESbM=: cdrwa
一旦自动故障转移被建立,你应该验证它的可用性。为了验证,首先定位Active NameNode。以可以通过浏览NameNode的web接口分辨哪个NameNode是Active的。
一旦定位了Active NameNode,你可以在那个节点上造成一个故障。例如,你可以用 kill -9 <pid of NN>模仿一次JVM崩溃。或者,你可以关闭这台机器或者拔下它的网卡来模仿一次不同类型的中断。在触发了你想测试的中断之后,另一个NameNode应该可以在几秒后自动转变为Active状态。故障检测和触发一次故障转移所需的时间依靠配置ha.zookeeper.session-timeout.ms来实现,默认是5秒。
如果检测不成功,你可能丢掉了配置,检查zkfc和NameNode 进程的日志文件以进行进一步的问题检测。
不重要,在任何给定的节点上,你可以任意顺序启动ZKFC和NameNode进程。
你应该在每一台运行NameNode的机器上增加监控以确保ZKFC保持运行。在某些类型的Zookeeper失效时,例如,ZKFC意料之外的结束,应该被重新启动以确保,系统准备自动故障转移。
除此之外,你应该监控Zookeeper集群中的每一个Server。如果Zookeeper宕机,自动故障转移将不起作用。
如果Zookeeper集群宕机,没有自动故障转移将会被触发。但是,HDFS将继续没有任何影响的运行。当Zookeeper被重新启动,HDFS将重新连接,不会出现问题。
当然不可以。目前,这是不支持的。先启动的NameNode将会先变成Active状态。你可以特定的顺序,先启动你希望成为Active的节点,来完成这个目的。
即使自动故障转移被卑职,你也可以用 hdfs haadmin发起一次手工故障转移。这个命令将执行一次协调的故障转移。
当在HDFS的版本见转换时,有时,新版本的软件可以被简单的安装,集群被重新启动。然而,有时,HDFS版本的升级需要改动磁盘上的数据。在这种情况下,在安装了新版本的软件之后,你必须用HDFS Upgrade/Finalize/Rollback工具。这个过程在HA的环境中更复杂,因为NameNode依赖的磁盘上的元数据是根据定义分布在两个HA的NameNode中的,在用QJM来共享edit存储时,数据在JNs中。文档的本部分描述了在一个HA的集群中用HDFS Upgrade/Finalize/Rollback 工具实现这个过程。
1> 正常关闭所有的NameNode,安装新版本的软件。
2> 启动所有的JNs,注意,在执行升级,回滚和finalization操作时,所有的JNs处于运行状态是至关重要的。如果JNs中的任何一个进程在执行升级,回滚和finalization操作时当掉,操作将失败。
3> 用-upgrade启动NameNode。
4> 启动时,正在操作的NameNode将不会像在HA启动时那样进去Standby状态。这个NameNode将会立即进入Active状态,执行本地存储目录的升级,同时执行共享edit日志的升级。
5> 在这时,另一个NameNode将不会与已经升级的NameNode保持同步。为了将其保持到同步,有一个高可用的集群,你应该用” -bootstrapStandby “命令重新引导这个NameNode。运行第二个NameNode时还用“-upgrade”是错误的。
注意:如果任何时候你想在finalization和回滚之前重新启动NameNode,你应该正常的启动NameNode,不要加任何特殊的启动标识。
HDFS High Availability Using the Quorum Journal Manager,布布扣,bubuko.com
HDFS High Availability Using the Quorum Journal Manager
原文:http://blog.csdn.net/xichenguan/article/details/38516361