本文是在hadoop集群部署(yarn)基础上增加的配置内容,因为那篇缺少HDFS的HA配置,在生产环境不够完整。
hadoop官方提供了两种HDFS的HA配置方案,两种方案殊途同归,但是需要的钱、精力和技术不同。
如果对HDFS架构熟悉的话(如果不熟悉,可以通过HDFS架构了解),就应该知道,NameNode通过FsImage和EditLog两个文件管理DataNode的数据,Secondary NameNode会定期合并EditLog,以减少NameNode启动时的安全检查。EditLog文件存储的是对文件的一条条的操作,也就是说,只要保证有另外一个NameNode的EditLog文件一直与当前正在运行的NameNode的EditLog文件是一样的,那就可以随时使用新的NameNode替换老的NameNode。官方目前给出的两种HA方案也大体是这样:
- QJM:the Quorum Journal Manager,翻译是法定经济管理人,实在没法想象,所以大家都亲切的称之为QJM。这种方案是通过JournalNode共享EditLog的数据,使用的是Paxos算法(没错,zookeeper就是使用的这种算法),保证活跃的NameNode与备份的NameNode之间EditLog日志一致。
- NFS:Network File System 或 Conventional Shared Storage,传统共享存储,其实就是在服务器挂载一个网络存储(比如NAS),活跃NameNode将EditLog的变化写到NFS,备份NameNode检查到修改就读取过来,是两个NameNode数据一致。
客观的说,Secondary NameNode也算是对NameNode的备份,但是使用Secondary NameNode需要手动处理,不如QJM和NFS两种可以自动处理简单,所以没有被列入HA解决方案中。
但是,这两种方案在部署方式上差别比较大。QJM需要启动几个JournalNode即可,NFS需要挂在一个共享存储。因为条件限制,我只能通过QJM的方式实现HDFS的HA,如果想看NFS方案,可以直接看官方文档。
1. hdfs-site.xml
dfs.nameservices:指定nameservice的名称,这个需要自定义,可以是任意的名称。这个值需要用在后面的配置和HDFS集群管理路径中。
<property> <name>dfs.nameservices</name> <value>mycluster</value> </property>
dfs.ha.namenodes.[nameservice ID]:指定集群中两个NameNode的id,目前只能支持最多两个NameNode,所以就需要两个id,以逗号分隔。
<property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property>
dfs.namenode.rpc-address.[nameservice ID].[namenode ID]:指定NameNode的rpc地址,用于数据传输。因为有两个NameNode,所以需要给出两个节点。
<property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>s108:8020</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>s109:8020</value> </property>
dfs.name.http-address.[nameservice ID].[namenode ID]:同3,还需要http地址。
<property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>s108:50070</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>s109:50070</value> </property>
dfs.namenode.shared.edits.dir:需要提供JournalNode的配置地址,用于活跃NameNode向该位置写变化数据,备份NameNode从该位置读取数据应用与自身。如果配置过Kafka就应该可以理解这个。配置地址格式是:qjournal://host1:port1;hots2:port2;host3:port3/journalId,地址端口为一对,每对之间通过分号隔开,最后的journalId是为了区分不同的nameservice的。也就是说,一组JournalNode可以支撑多个NameNode的HA配置。所以,比较好的配置方式是,journalId与nameservice的名称一致。
<property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://s108:8485;s109:8485;s110:8485/mycluster</value> </property>
dfs.client.failover.proxy.provider.[nameservice ID]:HDFS客户端连接活跃NameNode的方式,配置一个Java类。因为NameNode只有一个是活跃的,也就是只有一个提供服务,另一个是备份。所以客户端需要知道哪个是活跃节点。所以需要某种方式找到这个活跃节点。这里提供一个代理类,目前Hadoop只实现了一个代理类
ConfiguredFailoverProxyProvider
,也可以自己定义:<property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property>
dfs.ha.fencing.methods:用于故障转移过程中,在活跃节点执行的一组脚本或Java类。HDFS集群有一条原则是:只能有一个NameNode处于活跃状态。QJM只允许一个NameNode写入JournalNode集群,所以可以避免闹裂的发生。但是故障转移过程中,还可能会有其他的问题,所以需要提供一些防护方法。需要注意的是,如果不想使用具体的防护方法,也必须提供一个脚本,比如
shell(/bin/true)
。sshfence:通过ssh方式连接活跃NameNode,并kill掉进程。所以还需要通过
dfs.ha.fencing.ssh.private-key-files
配置ssh key,还可以通过dfs.ha.fencing.ssh.connect-timeout
配置ssh连接超时时间。<property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/root/.ssh/id_rsa</value> </property> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property>
如果对于不是标准ssh端口或相同用户的,可以在sshfence后添加用户名和端口,格式为
sshfence([[username][:port]])
。shell:运行任意的脚本来进行防护。我是使用sshfence方式配置的,所以下面就列出配置格式,具体信息查看官网。
<property> <name>dfs.ha.fencing.methods</name> <value>shell(/path/to/my/script.sh arg1 arg2 ...)</value> </property>
dfs.journalnode.edits.dir:JournalNode守护进程存储数据的本地路径。这是启动JournalNode需要配置的配置项。当然整个集群配置相同也不会有不好的影响,需要是本地绝对路径。
<property> <name>dfs.journalnode.edits.dir</name> <value>/data/hadoop/journal</value> </property>
dfs.ha.automatic-failover.enabled:自动故障转移,该配置向需要与core-site.xml中的
ha.zookeeper.quorum
配合使用。<property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property>
2. core-site.xml
fs.defaultFS:这个在单点NameNode的时候配置过,这里需要再次配置,需要使用hdfs-site.xml中的nameservice名称。
<property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property>
ha.zookeeper.quorum:这个就是前面提到hdfs-site.xml中配置自动故障转移配合使用的配置项,需要提供zookeeper集群地址
<property> <name>ha.zookeeper.quorum</name> <value>s109:2181,s110:2181,s111:2181</value> </property>
3. 开始启动
3.1 JournalNode
需要首先启动JournalNode,如上面配置的,需要s108/s109/s110三个节点启动JournalNode,默认端口就是8045。启动命令是hadoop-daemon.sh start journalnode
。
3.2 NameNode数据准备
JournalNode启动完成后,因为有两个NameNode节点,就需要先同步两个NameNode节点的数据。
- 如果是全新的HDFS集群,这个时候直接
hdfs namenode -format
格式化即可 - 已经格式化或是从非HA设置为HA的集群,需要把格式化后的NameNode节点的数据拷贝到为格式化节点上。未格式化NameNode节点执行
hdfs namenode -bootstrapStandby
命令。 - 如果是从非HA到HA的配置,需要执行
hdfs namenode -initializeSharedEdits
将原有的NameNode日志写入JournalNode中。
3.3 Zookeeper中的HA状态
因为上面配置了自动故障转移,所以需要在Zookeeper中初始化HA状态。执行命令hdfs zkfc -formatZK
。
3.4 启动
直接使用start-dfs.sh
命令启动NameNode、DataNode,以及ZKFS进程,启动成功之后就可以通过s108:50070和s109:50070访问web页面查看具体哪个NameNode是Active或Standby状态的了。
启动的时候可以注意到,启动过程没有启动Secondary NameNode,这是用为HA不会启动Secondary NameNode。也就是master配置文件配置内容无效了。
4. 管理
可以通过hdfs haadmin
命令进行管理。具体查看官网说明。
参考
个人主页: https://www.howardliu.cn
个人博文: 使用QJM实现HDFS的HA
CSDN主页: http://blog.csdn.net/liuxinghao
CSDN博文: 使用QJM实现HDFS的HA