本文是在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节点的数据。

  1. 如果是全新的HDFS集群,这个时候直接hdfs namenode -format格式化即可
  2. 已经格式化或是从非HA设置为HA的集群,需要把格式化后的NameNode节点的数据拷贝到为格式化节点上。未格式化NameNode节点执行hdfs namenode -bootstrapStandby命令。
  3. 如果是从非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命令进行管理。具体查看官网说明。


参考

  1. HDFS High Availability Using the Quorum Journal Manager
  2. HDFS High Availability

个人主页: http://www.howardliu.cn

个人博文: 使用QJM实现HDFS的HA

CSDN主页: http://blog.csdn.net/liuxinghao

CSDN博文: 使用QJM实现HDFS的HA