3

一. High Availability背景知识

1. 单点故障、高可用

单点故障(英语:single point of failure,缩写SPOF)是指系统中某一点一旦失效,就会让整个系统无法运作,换句话说,单点故障即会整体故障

高可用性(英语:high availability,缩写为 HA),IT术语,指系统无中断地执行其功能的能力,代表系统的可用性程度。是进行系统设计时的准则之一。高可用性系统意味着系统服务可以更长时间运行,通常通过提高系统的容错能力来实现

高可用性或者高可靠度的系统不会希望有单点故障造成整体故障的情形。一般可以透过冗余的方式增加多个相同机能的部件,只要这些部件没有同时失效,系统(或至少部分系统)仍可运作,这会让可靠度提高。

2. 高可用的实现

  1. 主备集群
    解决单点故障,实现系统服务高可用的核心并不是让故障永不发生,而是让故障的发生对业务的影响降到最小。因为软硬件故障是难以避免的问题。
    当下企业中成熟的做法就是给单点故障的位置设置备份,形成主备架构。通俗描述就是当主挂掉,备份顶上,短暂的中断之后继续提供服务。
    常见的是一主一备架构,当然也可以一主多备。备份越多,容错能力越强,与此同时,冗余也越大,浪费资源。
  2. Active、Standby
    Active:主角色。活跃的角色,代表正在对外提供服务的角色服务。任意时间有且只有一个active对外提供服务。
    Standby:备份角色。需要和主角色保持数据、状态同步,并且时刻准备切换成主角色(当主角色挂掉或者出现故障时),对外提供服务,保持服务的可用性。

3. 可用性评判标准—x个9

在系统的高可用性里有个衡量其可靠性的标准——X个9,这个X是代表数字3-5。X个9表示在系统1年时间的使用过程中,系统可以正常使用时间与总时间(1年)之比

  • 3个9:(1-99.9%)36524=8.76小时,表示该系统在连续运行1年时间里最多可能的业务中断时间是8.76小时
  • 4个9:(1-99.99%)36524=0.876小时=52.6分钟,表示该系统在连续运行1年时间里最多可能的业务中断时间是52.6分钟
  • 5个9:(1-99.999%)36524*60=5.26分钟,表示该系统在连续运行1年时间里最多可能的业务中断时间是5.26分钟。

可以看出,9越多,系统的可靠性越强,能够容忍的业务中断时间越少,但是要付出的成本更高

4. HA 系统设计核心问题

  1. 脑裂问题

脑裂(split-brain)是指“大脑分裂”,本是医学名词。在HA集群中,脑裂指的是当联系主备节点的"心跳线"断开时(即两个节点断开联系时),本来为一个整体、动作协调的HA系统,就分裂成为两个独立的节点。由于相互失去了联系,主备节点之间像"裂脑人"一样,使得整个集群处于混乱状态。脑裂的严重后果:

  • 集群无主:都认为对方是状态好的,自己是备份角色,后果是无服务;
  • 集群多主:都认为对方是故障的,自己是主角色。相互争抢共享资源,结果会导致系统混乱,数据损坏。此外对于客户端访问也是一头雾水,找谁呢?

避免脑裂问题的核心是:保持任意时刻系统有且只有一个主角色提供服务

  1. 数据同步问题
    主备切换保证服务持续可用性的前提是主备节点之间的状态、数据是一致的,或者说准一致的。如果说备用的节点和主节点之间的数据差距过大,即使完成了主备切换的动作,那也是没有意义的。
    数据同步常见做法是:通过日志重演操作记录。主角色正常提供服务,发生的事务性操作通过日志记录,备用角色读取日志重演操作。

    二. HDFS NAMENODE单点故障问题

    在Hadoop 2.0.0之前,NameNode是HDFS集群中的单点故障(SPOF)。每个群集只有一个NameNode,如果该计算机或进程不可用,则整个群集在整个NameNode重新启动或在另一台计算机上启动之前将不可用。
    NameNode的单点故障从两个方面影响了HDFS群集的总可用性

    • 如果发生意外事件(例如机器崩溃),则在重新启动NameNode之前,群集将不可用。
    • 计划内的维护事件,例如NameNode计算机上的软件或硬件升级,将导致群集停机时间的延长。

    HDFS高可用性解决方案:在同一群集中运行两个(从3.0.0起,超过两个)冗余NameNode。这样可以在机器崩溃的情况下快速故障转移到新的NameNode,或者出于计划维护的目的由管理员发起的正常故障转移。
    单点部署:
    image.png
    高可用部署
    image.png

三. HDFS HA解决方案—QJM

QJM全称Quorum Journal Manager,由cloudera公司提出,是Hadoop官方推荐的HDFS HA解决方案之一
QJM中,使用zookeeper中ZKFC来实现主备切换;使用Journal Node(JN)集群实现edits log的共享以达到数据同步的目的。
image.png

1. QJM—主备切换、脑裂问题解决

  1. ZKFailoverController(zkfc)
    Apache ZooKeeper是一款高可用分布式协调服务软件,用于维护少量的协调数据。 Zookeeper的下列特性功能参与了HDFS的HA解决方案中

    • 临时znode
      如果一个znode节点是临时的,那么该znode的生命周期将和创建它的客户端的session绑定。客户端断开连接session结束,znode将会被自动删除
    • Path路径唯一性
      zookeeper中维持了一份类似目录树的数据结构。每个节点称之为Znode。Znode具有唯一性,不会重名。也可以理解为排他性。
    • 监听机制
      客户端可以针对znode上发生的事件设置监听,当事件发生触发条件,zk服务会把事件通知给设置监听的客户端。

    ZKFailoverController(ZKFC)是一个新组件,它是一个ZooKeeper客户端。运行NameNode的每台计算机也都运行ZKFC,ZKFC的主要职责

    • 监视和管理NameNode健康状态
      ZKFC通过命令定期ping本地负责监视的NameNode节点。
    • 维持和ZooKeeper集群联系
      如果本地NameNode运行状况良好,并且ZKFC看到当前没有其他节点持有锁znode,它将自己尝试获取该锁。如果成功,则表明它“赢得了选举”,并负责运行故障转移以使其本地NameNode处于Active状态。如果已经有其他节点持有锁,zkfc选举失败,则会对该节点注册监听,等待下次继续选举。
  2. Fencing 隔离机智
    故障转移过程也就是俗称的主备角色切换的过程,切换过程中最怕的就是脑裂的发送。因此需要Fencing机制来避免,将先前的Active节点隔离,然后将本地NameNode转换为Active状态
    Hadoop公共库中对外提供了两种fenching实现,分别是sshfence和shellfence(缺省实现),其中sshfence是指通过ssh登陆目标节点上,使用命令fuser将进程杀死(通过tcp端口号定位进程pid,该方法比jps命令更准确),shellfence是指执行一个用户事先定义的shell命令(脚本)完成隔离。

    2. QJM—主备数据同步问题解决

    Journal Node(JN)集群是轻量级分布式系统,主要用于高速读写数据、存储数据。通常使用2N+1台JournalNode存储共享Edits Log(编辑日志)
    任何修改操作在 Active NN上执行时,JournalNode进程同时也会记录edits log到至少半数以上的JN中,这时 Standby NN 监测到JN 里面的同步log发生变化了会读取JN里面的edits log,然后重演操作记录同步到自己的目录镜像树里面,
    当发生故障Active NN挂掉后,Standby NN 会在它成为Active NN 前,读取所有的JN里面的修改日志,这样就能高可靠的保证与挂掉的NN的目录镜像树一致,然后无缝的接替它的职责,维护来自客户端请求,从而达到一个高可用的目的。

4. HA环境搭建

HA集群搭建的难度主要在于配置文件的编写,心细,心细,心细!

1. 集群基础环境准备

  1. 修改Linux 主机名 /etc/hostname
  2. 修改IP /etc/sysconfig/network-scripts/ifcfg-ens33
  3. 修改主机名和Ip映射关系
  4. 关闭防火墙
  5. ssh免密登陆
  6. 安装JDK,配置环境变量等 /etc/profile
  7. 集群时间同步
  8. 配置主备NN之间的相互免密登录

2. HA集群规划

node1  namenode  zkfc   datanode   zookeeper   journal node
node2  namenode  zkfc   datanode   zookeeper   journal node
node3  datanode zookeeper journal node9

3. 安装配置zookeepe

上传zookeeper 至linux

  1. 解压

    tar -zxvf zookeeper-3.4.5.tar.gz -C /export/server
  2. 修改配置文件

             cd /export/server/zookeeper-3.4.5/conf/
             cp zoo_sample.cfg zoo.cfg
             vim zoo.cfg
             修改:dataDir=/export/data/zkdata
             在最后添加:
             server.1=node1:2888:3888
             server.2=node2:2888:3888
             server.3=node3:2888:3888
             保存退出
             然后创建一个tmp文件夹
             mkdir /export/data/zkdata
             echo 1 > /export/data/zkdata/myid
  3. 将配置好的zookeeper拷贝到其他节点

             scp -r /export/server/zookeeper-3.4.5 node2:/export/server
             scp -r /export/server/zookeeper-3.4.5 node3:/export/server
             
             编辑node2、node3对应/export/data/zkdata/myid内容
             node2:
                 mkdir /export/data/zkdata
                 echo 2 > /export/data/zkdata/myid
             node3:
                 mkdir /export/data/zkdata
                 echo 3 > /export/data/zkdata/myid

4. 上传解压Hadoop安装包

hadoop-3.1.4-bin-snappy-CentOS7.tar.gz
tar zxvf hadoop-3.1.4-bin-snappy-CentOS7.tar.gz -C /export/server/

5.配置Hadoop 环境变量

vim /etc/profile

export HADOOP_HOME=/export/server/hadoop-3.1.4
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
source /etc/profile

6. 修改Hadoop 配置文件

  1. hadoop-env.sh

    cd /export/server/hadoop-3.1.4/etc/Hadoop
    vim hadoop-env.sh
    export JAVA_HOME=/export/server/jdk1.8.0_65
    export HDFS_NAMENODE_USER=root
    export HDFS_DATANODE_USER=root
    export HDFS_JOURNALNODE_USER=root
    export HDFS_ZKFC_USER=root
    
  2. core-site.xml

    vim core-site.xml
    <configuration>
     <!-- HA集群名称,该值要和hdfs-site.xml中的配置保持一致 -->
     <property>
         <name>fs.defaultFS</name>
         <value>hdfs://mycluster</value>
     </property>
    
     <!-- hadoop本地磁盘存放数据的公共目录 -->
     <property>
         <name>hadoop.tmp.dir</name>
         <value>/export/data/ha-hadoop</value>
     </property>
    <!-- ZooKeeper集群的地址和端口-->
     <property>
         <name>ha.zookeeper.quorum</name>
    <value>node1.uuicon.com:2181,node2.uuicon.com:2181,node3.uuicon.com:2181</value>
     </property>
    </configuration>
    
  3. hdfs-site.xml
<configuration>
    <!--指定hdfs的nameservice为mycluster,需要和core-site.xml中的保持一致 -->
    <property>
        <name>dfs.nameservices</name>
        <value>mycluster</value>
    </property>
    
    <!-- mycluster下面有两个NameNode,分别是nn1,nn2 -->
    <property>
        <name>dfs.ha.namenodes.mycluster</name>
        <value>nn1,nn2</value>
    </property>

    <!-- nn1的RPC通信地址 -->
    <property>
        <name>dfs.namenode.rpc-address.mycluster.nn1</name>
        <value>node1.uuicon.com:8020</value>
    </property>

    <!-- nn1的http通信地址 -->
    <property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
        <value>node1.uuicon.com:9870</value>
    </property>

    <!-- nn2的RPC通信地址 -->
    <property>
        <name>dfs.namenode.rpc-address.mycluster.nn2</name>
        <value>node2.uuicon.com:8020</value>
    </property>
    
    <!-- nn2的http通信地址 -->
    <property>
        <name>dfs.namenode.http-address.mycluster.nn2</name>
        <value>node2.uuicon.com:9870</value>
    </property>

    <!-- 指定NameNode的edits元数据在JournalNode上的存放位置 -->
    <property>
        <name>dfs.namenode.shared.edits.dir</name>
        <value>qjournal://node1.uuicon.com:8485;node2.uuicon.com:8485;node3.uuicon.com:8485/mycluster</value>
    </property>
    
    <!-- 指定JournalNode在本地磁盘存放数据的位置 -->
    <property>
        <name>dfs.journalnode.edits.dir</name>
        <value>/export/data/journaldata</value>
    </property>
    <!-- 开启NameNode失败自动切换 -->
    <property>
        <name>dfs.ha.automatic-failover.enabled</name>
        <value>true</value>
    </property>

    <!-- 指定该集群出故障时,哪个实现类负责执行故障切换 -->
    <property>
      <name>dfs.client.failover.proxy.provider.mycluster</name>
      <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
    </property>

    <!-- 配置隔离机制方法-->
    <property>
      <name>dfs.ha.fencing.methods</name>
      <value>sshfence</value>
    </property>

    <!-- 使用sshfence隔离机制时需要ssh免登陆 -->
    <property>
      <name>dfs.ha.fencing.ssh.private-key-files</name>
      <value>/root/.ssh/id_rsa</value>
    </property>    
    
    <!-- 配置sshfence隔离机制超时时间 -->
    <property>
        <name>dfs.ha.fencing.ssh.connect-timeout</name>
        <value>30000</value>
    </property>
</configuration>
  1. workers

    # 每台服务器的主机名:
    node1.uuicon.cn
    node2.uuicon.cn
    node3.uuicon.cn
  2. mapred-site.xml

    <configuration>
    <!-- 指定mr框架为yarn方式 -->
    <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
    </property>
    </configuration>    
  3. yarn-site.xml

      <!-- 开启RM高可用 -->
     <property>
         <name>yarn.resourcemanager.ha.enabled</name>
         <value>true</value>
     </property>
     <!-- 指定RM的cluster id -->
     <property>
         <name>yarn.resourcemanager.cluster-id</name>
         <value>yrc</value>
     </property>
     <!-- 指定RM的名字 -->
     <property>
         <name>yarn.resourcemanager.ha.rm-ids</name>
         <value>rm1,rm2</value>
     </property>
     <!-- 分别指定RM的地址 -->
     <property>
         <name>yarn.resourcemanager.hostname.rm1</name>
         <value>node1</value>
     </property>
     <property>
         <name>yarn.resourcemanager.hostname.rm2</name>
         <value>node2</value>
     </property>
     <!-- 指定zk集群地址 -->
     <property>
         <name>yarn.resourcemanager.zk-address</name>
         <value>node1:2181,node2:2181,node3:2181</value>
     </property>
     <property>
         <name>yarn.nodemanager.aux-services</name>
         <value>mapreduce_shuffle</value>
     </property>
     <property>
         <name>yarn.resourcemanager.address.rm1</name>
         <value>node1:8032</value>
     </property>
     <property>
         <name>yarn.resourcemanager.scheduler.address.rm1</name>
         <value>node1:8030</value>
     </property>
     <property>
         <name>yarn.resourcemanager.webapp.address.rm1</name>
         <value>node1:8088</value>
     </property>
     <property>
         <name>yarn.resourcemanager.resource-tracker.address.rm1</name>
         <value>node1:8031</value>
     </property>
     <property>
         <name>yarn.resourcemanager.admin.address.rm1</name>
         <value>noode1:8033</value>
     </property>
     <property>
         <name>yarn.resourcemanager.ha.admin.address.rm1</name>
         <value>node1:23142</value>
     </property>
    
     <property>
         <name>yarn.resourcemanager.address.rm2</name>
         <value>node2:8032</value>
     </property>
     <property>
         <name>yarn.resourcemanager.scheduler.address.rm2</name>
         <value>node2:8030</value>
     </property>
     <property>
         <name>yarn.resourcemanager.webapp.address.rm2</name>
         <value>node2:8088</value>
     </property>
     <property>
         <name>yarn.resourcemanager.resource-tracker.address.rm2</name>
         <value>node2:8031</value>
     </property>
     <property>
         <name>yarn.resourcemanager.admin.address.rm2</name>
         <value>node2:8033</value>
     </property>
     <property>
         <name>yarn.resourcemanager.ha.admin.address.rm2</name>
         <value>node2:23142</value>
     </property>
    
    

    7.配置免密码登陆

    配置参考教程

    1. 首先要配置node1到node1、node2、node3的免密码登陆
      2. 两个namenode之间要配置ssh免密码登陆 ssh远程补刀时候需要

    8. 集群同步安装包

    cd /export/server
    scp -r hadoop-3.1.4 root@node2:$PWD
    scp -r hadoop-3.1.4 root@node3:$PWD

    5. 启动HA-Hadoop 集群

    1. 启动zookeeper集群 (分别在node1、node2、node3上启动zk)

    bin/zkServer.sh start
    #查看状态:一个leader,两个follower
    bin/zkServer.sh status

    2. 手动启动journalnode(分别在在node1、node2、node3上执行)

    hdfs --daemon start journalnode
    #运行jps命令检验,多了JournalNode进程

    3. 格式化namenode

    #在node1上执行命令:
    hdfs namenode -format
    #格式化后会在根据core-site.xml中的hadoop.tmp.dir配置的目录下生成个hdfs初始化文件,
    #在node1启动namenode进程
    hdfs --daemon start namenode
    ##在node2上进行元数据同步
    hdfs namenode -bootstrapStandby

    4. 格式化ZKFC(在active上执行即可)

    hdfs zkfc -formatZK

    5. 启动HDFS(在node1上执行)

    start-dfs.sh

    6. 启动YARN

    start-yarn.sh
    还需要手动在standby上手动启动备份的  resourcemanager
    yarn-daemon.sh start resourcemanager

6. HDFS HA 效果演示

Node1
image.png
node2
image.png

  1. 正常操作
    image.png
  2. 模拟故障出现
    在node1,手动kill杀死namenode进程。

    kill -9 20129(node1 上的 namenode 进程号)

    image.png
    关闭node1 上的namenode 后发现namenode 没有自动切换,查看日志:
    /export/server/hadoop-3.1.4/logs/hadoop-root-zkfc-node2.uuicon.com.log
    日志显示:

     WARN org.apache.hadoop.ha.SshFenceByTcpPort: Unable to create SSH session
    com.jcraft.jsch.JSchException: invalid privatekey: [B@65d13812
         at com.jcraft.jsch.KeyPair.load(KeyPair.java:664)
         at com.jcraft.jsch.KeyPair.load(KeyPair.java:561)
         at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:40)
         at com.jcraft.jsch.JSch.addIdentity(JSch.java:406)
         at com.jcraft.jsch.JSch.addIdentity(JSch.java:366)
         at org.apache.hadoop.ha.SshFenceByTcpPort.createSession(SshFenceByTcpPort.java:121)
         at org.apache.hadoop.ha.SshFenceByTcpPort.tryFence(SshFenceByTcpPort.java:90)
         at org.apache.hadoop.ha.NodeFencer.fence(NodeFencer.java:98)
         at org.apache.hadoop.ha.ZKFailoverController.doFence(ZKFailoverController.java:538)
         at org.apache.hadoop.ha.ZKFailoverController.fenceOldActive(ZKFailoverController.java:511)
         at org.apache.hadoop.ha.ZKFailoverController.access$1100(ZKFailoverController.java:60)
         at org.apache.hadoop.ha.ZKFailoverController$ElectorCallbacks.fenceOldActive(ZKFailoverController.java:934)
         at org.apache.hadoop.ha.ActiveStandbyElector.fenceOldActive(ActiveStandbyElector.java:992)
         at org.apache.hadoop.ha.ActiveStandbyElector.becomeActive(ActiveStandbyElector.java:891)
         at org.apache.hadoop.ha.ActiveStandbyElector.processResult(ActiveStandbyElector.java:476)
         at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:610)
         at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:508)
    2021-10-19 11:12:56,537 WARN org.apache.hadoop.ha.NodeFencer: Fencing method org.apache.hadoop.ha.SshFenceByTcpPort(null) was unsuccessful.
    2021-10-19 11:12:56,537 ERROR org.apache.hadoop.ha.NodeFencer: Unable to fence service by any configured method.
    2021-10-19 11:12:56,537 WARN org.apache.hadoop.ha.ActiveStandbyElector: Exception handling the winning of election
    java.lang.RuntimeException: Unable to fence NameNode at node1.uuicon.com/192.168.26.101:8020
         at org.apache.hadoop.ha.ZKFailoverController.doFence(ZKFailoverController.java:539)
         at org.apache.hadoop.ha.ZKFailoverController.fenceOldActive(ZKFailoverController.java:511)
         at org.apache.hadoop.ha.ZKFailoverController.access$1100(ZKFailoverController.java:60)
         at org.apache.hadoop.ha.ZKFailoverController$ElectorCallbacks.fenceOldActive(ZKFailoverController.java:934)
         at org.apache.hadoop.ha.ActiveStandbyElector.fenceOldActive(ActiveStandbyElector.java:992)
         at org.apache.hadoop.ha.ActiveStandbyElector.becomeActive(ActiveStandbyElector.java:891)
         at org.apache.hadoop.ha.ActiveStandbyElector.processResult(ActiveStandbyElector.java:476)
         at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:610)
         at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:508)
    2021-10-19 11:12:56,537 INFO org.apache.hadoop.ha.ActiveStandbyElector: Trying to re-establish ZK session
    2021-10-19 11:12:56,541 INFO org.apache.zookeeper.ZooKeeper: Session: 0x10000f7ca30001b closed

    解决方式:
    https://www.cnblogs.com/simpl...

再次测试:

  1. 开启已关闭的namenode

    hdfs --daemon start naemnode 
  2. 查看两个namenode状态
    image.png

image.png

  1. 关闭node1 中的namenode
    image.png
  2. 查看node2状态
    image.png

namenode已自动切换完成


捕风
47 声望16 粉丝