Hadoop3 HA高可用集群搭建

Hadoop3高可用(HA:High Available)测试集群搭建。

NameNode是整个Hadoop集群的大脑。

HDFS 在非 HA 模式的集群下,NameNode 和 DataNode 是一个主从的架构。在这样的主从架构之下只有一台 NameNode。一台 NameNode 的好处是无需因为元数据的同步而考虑数据的一致性问题。但这种模式下一旦NameNode挂掉,整个集群将不可用。

为了解决单台 NameNode 挂掉不可用的问题,HDFS 在 2.x 版本的时候引入了 HDFS 集群的 HA 模式,也就是有了 NameNode 的主备架构。在 2.x 的版本中,HDFS 支持一主一备的架构,在 3.x 的版本中最多支持 5 个,官方推荐使用 3 个。

Hadoop高可用架构

实际上保证高可用只需要考虑两点。

  1. 可用性
  2. 一致性

如果Active节点和StandBy节点使用同步阻塞的方式同步数据,那么StandBy节点的故障会影响到Active节点正常工作(同步数据需要返回确认信息),延迟、故障等因素影响了集群可用性;

如果Active节点和StandBy节点使用异步非阻塞模式同步数据,StandBy节点同步的过程中可能什么也没做,也可能发生故障了,Active节点不知道情况,继续正常工作,影响了一致性;

这样好像很难同时保证高可用和一致性,原因在于NameNode之间耦合太重了,所以Hadoop3又引入了JournalNode节点专门同步NameNode数据。

Hadoop3 HA架构

Hadoop-ha

  1. journalNode的作用是存放EditLog的。在MR1中editlog是和fsimage存放在一起的然后SecondNamenode做定期合并,Yarn在这上面就不用SecondNamanode了.

  2. Active Namenode与StandBy Namenode之间的就是JournalNode,作用相当于NFS共享文件系统。Active Namenode往里写editlog数据,StandBy再从里面读取数据进行同步。

  3. 两个NameNode为了数据同步,会通过一组称作JournalNodes的独立进程进行相互通信。当active状态的NameNode的命名空间有任何修改时,会告知大部分的JournalNodes进程。

    standby状态的NameNode有能力读取JNs中的变更信息,并且一直监控edit log的变化,把变化应用于自己的命名空间。standby可以确保在集群出错时,命名空间状态已经完全同步了。

NameNode服务器:运行NameNode的服务器应该有相同的硬件配置。

JournalNode服务器:运行的JournalNode进程非常轻量,可以部署在其他的服务器上。注意:必须允许至少3个节点。当然可以运行更多,但是必须是奇数个,如3、5、7、9个等等。

当运行N个节点时,系统可以容忍至少(N-1)/2(N至少为3)个节点失败而不影响正常运行。

在HA集群中,standby状态的NameNode可以完成checkpoint操作,因此没必要配置Secondary NameNode、CheckpointNode、BackupNode。如果真的配置了,还会报错。

前置步骤

  1. 集群互信
  2. 安装配置zookeeper

集群规划

以下节点加入集群

192.168.8.3 centos1
192.168.8.4 centos2
192.168.8.5 centos3
centos1 centos2 centos3
NameNode NameNode NameNode
ResourceManager ResourceManager RecourceManager
JournalNode JournalNode JournalNode
DataNode DataNode DataNode
JobHistoryServer

安装Hadoop并配置环境变量

  1. 解压hadoop并分发

    cd /soft
    tar -xzvf hadoop-3.2.0.tar.gz
    mv hadoop-3.2.0 hadoop3.2
    xsync.sh hadoop3.2
    
  2. 配置环境变量

    vim /etc/profile.d/bigdata.sh
    # 添加两行
    export HADOOP_HOME=/soft/hadoop3.2
    export LD_LIBRARY_PATH=$LIBRARY_PATH:$HADOOP_HOME/lib/native
    # 分发
    xsync.sh /etc/profile.d/bigdata.sh
    # 激活测试
    source /etc/profile
    echo $HADOOP_HOME
    
  3. 如果以root用户搭建集群环境,还需要添加以下环境变量(在hadoop-env.sh、yarn-env.sh等中也可以)

    export HDFS_ZKFC_USER=root
    export HDFS_NAMENODE_USER=root
    export HDFS_DATANODE_USER=root
    export HDFS_JOURNALNODE_USER=root
    export YARN_NODEMANAGER_USER=root
    export YARN_RESOURCEMANAGER_USER=root
    

配置文件

core-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <!-- 指定 NameNode 的地址 单节点
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://centos1:8020</value>
    </property>
    -->
    <!-- Namenode高可用配置-自定义集群名称,且不用指定端口号 -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://mycluster</value>
    </property>
    <!-- 指定 hadoop 数据的存储目录 -->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/hadoop3.2/data</value>
    </property>
    <!-- 配置 HDFS 网页登录使用的静态用户为 bigdata -->
    <property>
        <name>hadoop.http.staticuser.user</name>
        <value>bigdata</value>
    </property>
    <!--置超级代理-->
    <property>
        <name>hadoop.proxyuser.bigdata.hosts</name>
        <value>*</value>
    </property>
    <property>
        <name>hadoop.proxyuser.bigdata.groups</name>
        <value>*</value>
    </property>
    <!-- 配置ZKFC进程连接zookeeper的地址 -->
    <property>
        <name>ha.zookeeper.quorum</name>
        <value>centos1:2181,centos2:2181,centos3:2181</value>
    </property>
</configuration>

hdfs-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <!--

nn web 端访问地址
<property>
<name>dfs.namenode.http-address</name>
<value>centos1:9870</value>
</property>
2nn web 端访问地址
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>centos3:9868</value>
</property>

-->
  <!-- namenode服务逻辑id -->
  <property>
    <name>dfs.nameservices</name>
    <value>mycluster</value>
  </property>
  <!-- namenode服务mycluster下3个节点 -->
  <property>
    <name>dfs.ha.namenodes.mycluster</name>
    <value>nn1,nn2,nn3</value>
  </property>
  <!-- 节点通讯地址 -->
  <property>
    <name>dfs.namenode.rpc-address.mycluster.nn1</name>
    <value>centos1:8020</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.mycluster.nn2</name>
    <value>centos2:8020</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.mycluster.nn3</name>
    <value>centos3:8020</value>
  </property>
  <!-- web ui地址 -->
  <property>
    <name>dfs.namenode.http-address.mycluster.nn1</name>
    <value>centos1:9870</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.mycluster.nn2</name>
    <value>centos2:9870</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.mycluster.nn3</name>
    <value>centos3:9870</value>
  </property>
  <!-- journalnode edits读取写入地址 -->
  <property>
    <name>dfs.namenode.shared.edits.dir</name>
    <value>qjournal://centos1:8485;centos2:8485;centos3:8485/mycluster</value>
  </property>
  <!--  the Java class that HDFS clients use to contact the Active NameNode -->
  <property>
    <name>dfs.client.failover.proxy.provider.mycluster</name>
    <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
  </property>
  <!-- a list of scripts or Java classes which will be used to fence the Active NameNode during a
  failover -->
  <property>
    <name>dfs.ha.fencing.methods</name>
    <value>sshfence</value>
    <value>shell(/bin/true)</value>
  </property>
  <property>
    <name>dfs.ha.fencing.ssh.private-key-files</name>
    <value>/home/bigdata/.ssh/id_rsa</value>
  </property>
  <property>
    <name>dfs.ha.nn.not-become-active-in-safemode</name>
    <value>true</value>
  </property>
  <!-- 故障情况自动切换 -->
  <property>
    <name>dfs.ha.automatic-failover.enabled</name>
    <value>true</value>
  </property>
  <!-- Namenode 数据存储目录-->
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>${hadoop.tmp.dir}/name</value>
  </property>

  <!-- Datanode 数据存储目录-->
  <property>
    <name>dfs.namenode.data.dir</name>
    <value>${hadoop.tmp.dir}/data</value>
  </property>

  <!-- journalnode 数据存储目录-->
  <property>
    <name>dfs.journalnode.edits.dir</name>
    <value>${hadoop.tmp.dir}/jn</value>
  </property>
</configuration>

相关配置可查看官网

yarn-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <!-- 指定 MR 走 shuffle -->
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <!-- 指定 ResourceManager 的地址 单节点 -->
    <!--
<property>
<name>yarn.resourcemanager.hostname</name>
<value>centos2</value>
</property>
-->
    <!-- 环境变量的继承 -->
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>
            JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
    </property>
    <!-- hadoop classpath输出以下路径 -->
    <property>
        <name>yarn.application.classpath</name>
        <value>
            /opt/hadoop3.2/etc/hadoop:/opt/hadoop3.2/share/hadoop/common/lib/*:/opt/hadoop3.2/share/hadoop/common/*:/opt/hadoop3.2/share/hadoop/hdfs:/opt/hadoop3.2/share/hadoop/hdfs/lib/*:/opt/hadoop3.2/share/hadoop/hdfs/*:/opt/hadoop3.2/share/hadoop/mapreduce/lib/*:/opt/hadoop3.2/share/hadoop/mapreduce/*:/opt/hadoop3.2/share/hadoop/yarn:/opt/hadoop3.2/share/hadoop/yarn/lib/*:/opt/hadoop3.2/share/hadoop/yarn/*</value>
    </property>
    <!-- 开启resourcemanager HA-->
    <property>
        <name>yarn.resourcemanager.ha.enabled</name>
        <value>true</value>
    </property>
    <!-- 自定义一个resourcemanager的逻辑集群id-->
    <property>
        <name>yarn.resourcemanager.cluster-id</name>
        <value>yarn-cluster</value>
    </property>
    <!-- 指定resourcemanager集群的逻辑节点名称列表-->
    <property>
        <name>yarn.resourcemanager.ha.rm-ids</name>
        <value>rm1,rm2,rm3</value>
    </property>
    <!-- rm1的节点信息-->
    <property>
        <name>yarn.resourcemanager.hostname.rm1</name>
        <value>centos1</value>
    </property>
    <!-- yarn web页面地址  -->
    <property>
        <name>yarn.resourcemanager.webapp.address.rm1</name>
        <value>centos1:8088</value>
    </property>
    <!-- rm1 对客户端暴露的地址,客户端通过该地址向RM提交任务等 -->
    <property>
        <name>yarn.resourcemanager.address.rm1</name>
        <value>centos1:8032</value>
    </property>
    <!-- rm1 与 applicationMaster的通信地址  -->
    <property>
        <name>yarn.resourcemanager.scheduler.address.rm1</name>
        <value>centos1:8030</value>
    </property>
    <!-- rm1 与 nm的通信地址  -->
    <property>
        <name>yarn.resourcemanager.resource-tracker.address.rm1</name>
        <value>centos1:8031</value>
    </property>

    <!-- rm2的节点信息-->
    <property>
        <name>yarn.resourcemanager.hostname.rm2</name>
        <value>centos2</value>
    </property>
    <!-- yarn web页面地址  -->
    <property>
        <name>yarn.resourcemanager.webapp.address.rm2</name>
        <value>centos2:8088</value>
    </property>
    <!-- rm2 对客户端暴露的地址,客户端通过该地址向RM提交任务等 -->
    <property>
        <name>yarn.resourcemanager.address.rm2</name>
        <value>centos2:8032</value>
    </property>
    <!-- rm2 与 applicationMaster的通信地址  -->
    <property>
        <name>yarn.resourcemanager.scheduler.address.rm2</name>
        <value>centos2:8030</value>
    </property>
    <!-- rm2 与 nm的通信地址  -->
    <property>
        <name>yarn.resourcemanager.resource-tracker.address.rm2</name>
        <value>centos2:8031</value>
    </property>

    <!-- rm3的节点信息-->
    <property>
        <name>yarn.resourcemanager.hostname.rm3</name>
        <value>centos3</value>
    </property>
    <!-- yarn web页面地址  -->
    <property>
        <name>yarn.resourcemanager.webapp.address.rm3</name>
        <value>centos3:8088</value>
    </property>
    <property>
        <name>yarn.resourcemanager.address.rm3</name>
        <value>centos3:8032</value>
    </property>
    <!-- rm3 与 applicationMaster的通信地址  -->
    <property>
        <name>yarn.resourcemanager.scheduler.address.rm3</name>
        <value>centos3:8030</value>
    </property>
    <!-- rm3 与 nm的通信地址  -->
    <property>
        <name>yarn.resourcemanager.resource-tracker.address.rm3</name>
        <value>centos3:8031</value>
    </property>

    <!-- 配置zookeeper信息  -->
    <property>
        <name>yarn.resourcemanager.zk-address</name>
        <value>centos1:2181,centos2:2181,centos3:2181</value>
    </property>

    <!-- 启动自动恢复 -->
    <property>
        <name>yarn.resourcemanager.recovery.enabled</name>
        <value>true</value>
    </property>

    <!-- 配置将recourcemanager的状态信息存储在zookeeper中 -->
    <property>
        <name>yarn.resourcemanager.store.class</name>
        <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
    </property>
    <!-- 开启日志聚集功能 -->
    <property>
        <name>yarn.log-aggregation-enable</name>
        <value>true</value>
    </property>
    <!-- 设置日志聚集服务器地址 -->
    <property>
        <name>yarn.log.server.url</name>
        <value>http://centos1:19888/jobhistory/logs</value>
    </property>
    <!-- 设置日志保留时间为 7 天 -->
    <property>
        <name>yarn.log-aggregation.retain-seconds</name>
        <value>604800</value>
    </property>
</configuration>

mapred-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <!-- 指定 MapReduce 程序运行在 Yarn 上 -->
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <!-- 历史服务器端地址 -->
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>centos1:10020</value>
    </property>
    <!-- 历史服务器 web 端地址 -->
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>centos1:19888</value>
    </property>
</configuration>

workers(DataNodes)

centos1
centos2
centos3

初始化集群

  1. 启动zookeeper集群

  2. 初始化ZKFC

    ZKFC用于监控active namenode节点是否挂掉,通知其它节点上的ZKFC强行杀死自己ZKFC节点上的namenode(防止其假死状态产生集群namenode脑裂的发生),然后选举出其他namenode为active节点。在节点1执行

    ${HADOOP_HOME}/bin/hdfs zkfc -formatZK
    
  3. 在每个节点上启动journalnode进程

    cluster_all.sh ${HADOOP_HOME}/bin/hdfs --daemon start journalnode
    
  4. 在节点1上格式化namenode

    ${HADOOP_HOME}/bin/hdfs namenode -format
    
  5. 启动节点1的namenode

    ${HADOOP_HOME}/bin/hdfs --daemon start namenode
    
  6. 其他StandBy节点同步

    # 同步
    ${HADOOP_HOME}/bin/hdfs namenode -bootstrapStandby
    

HA集群初始化工作完成,后续可使用启动脚本快速启动、停止集群。

集群测试

  1. 启动整个集群

    myhadoop.sh start
    
    # 
    [bigdata@centos1 root]$ myhadoop.sh start
    start hadoop cluster
    start hdfs on centos1
    Starting namenodes on [centos1 centos2 centos3]
    Starting datanodes
    Starting journal nodes [centos2 centos3 centos1]
    Starting ZK Failover Controllers on NN hosts [centos1 centos2 centos3]
    start yarn on centos2
    Starting resourcemanagers on [ centos1 centos2 centos3]
    Starting nodemanagers
    start historyserver
    
  2. 查看active节点

    # 连接zookeeper
    get -s /hadoop-ha/hacluster/ActiveStandbyElectorLock
    # 或者
    ${HADOOP_HOME}/bin/hdfs haadmin -getAllServiceState
    

    可打开web页面查看节点是否为active

  3. 查看所有进程

    centos1 centos2 centos3
    QuorumPeerMain QuorumPeerMain QuorumPeerMain
    JournalNode JournalNode JournalNode
    NameNode NameNode NameNode
    DataNode DataNode DataNode
    DFSZKFailoverController DFSZKFailoverController DFSZKFailoverController
    ResourceManager ResourceManager ResourceManager
    NodeManager NodeManager NodeManager
    JobHistoryServer
  4. 测试HDFS高可用

    kill -9 active namenode进程,查看页面状态,可发现另外某个namenode自动切换成active状态。

  5. 验证YARN高可用

    访问任意resourcemanager节点的8088都会跳转到固定的一个resourcemanager节点上,说明高可用配置成功。

    Kill掉固定节点上的resourcemanager进程,继续访问8088端口,发现地址已转变到其它resourcemanager节点上,说明高可用验证成功。

    也可通过命令查看所有节点状态:

    yarn rmadmin -getAllServiceState
    
  6. 集群是否可用

    hdfs dfs -put ./wordcount.txt hdfs://mycluster/tmp/wordcount.txt
    hdfs dfs -ls hdfs://mycluster/wordcount
    hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.2.jar wordcount /tmp/wordcount.txt /tmp/output
    hdfs dfs -get hdfs://mycluster/tmp/output ./output
    

集群脚本

#!/bin/bash

# /opt/cluster_bin/myhadoop.sh

if [ ! $# -eq 1 ]
then
        echo "arg error!"
        exit 1
fi

if [ ! -e "$HADOOP_HOME" ]
then
        echo "HADOOP_HOME not set!"
        exit 1
fi

namenode="centos1"
resourcenode="centos2"

case $1 in
"start")
        echo "start hadoop cluster"

        echo "start hdfs on $namenode"
        ssh $namenode "${HADOOP_HOME}/sbin/start-dfs.sh"
        echo "start yarn on $resourcenode"
        ssh $resourcenode "${HADOOP_HOME}/sbin/start-yarn.sh"
        echo "start historyserver"
        ssh $namenode "${HADOOP_HOME}/bin/mapred --daemon start historyserver"
        ;;
"stop")

        echo "stop hadoop cluster"

        echo "stop hdfs on $namenode"
        ssh $namenode "${HADOOP_HOME}/sbin/stop-dfs.sh"
        echo "stop yarn on $resourcenode"
        ssh $resourcenode "${HADOOP_HOME}/sbin/stop-yarn.sh"
        echo "stop historyserver"
        ssh $namenode "${HADOOP_HOME}/bin/mapred --daemon stop historyserver"
        ;;
*)
        echo "arg error start or stop"
        ;;
esac

参考文章

hadoop中的JournalNode

HDFS 在 HA 模式集群下 JournalNode 节点的作用

Hadoop3.x入门-搭建3节点Hadoop HA集群

Q.E.D.


一切很好,不缺烦恼。