利用QJM实现HDFS的HA策略部署与验证工作记录分享

来源:互联网 发布:mac mysql怎么用 编辑:程序博客网 时间:2024/06/03 13:06

http://www.tuicool.com/articles/AbQBz2


1、概述  

Hadoop2.X 中的 HDFS(Vsersion2.0) 相比于 Hadoop1.X 增加了两个重要功能, HA 和 Federation 。 HA 解决了 Hadoop1.X  Namenode 中一直存在的单点故障问题, HA 策略通过热备的方式为主 NameNode 提供一个备用者,并且这个备用者的状态一直和主 Namenode 的元数据保持一致,一旦主 NameNode 挂了,备用 NameNode 可以立马转换变换为主 NameNode ,从而提供不间断的服务。另外, Federation 特性,主要是允许一个 HDFS 集群中存在多个 NameNode 同时对外提供服务,这些 NameNode 分管一部分目录(水平切分),彼此之 间相互隔离,但共享底层的 DataNode 存储资源。本文档主要是总结我自己个人利用为 QJM ( Quorum Journal Manager )为公司测试集群(hadoop2.2.0)部署 HA 策略的过程以及自己在部署过程中遇到的一些问题。

2、HDFSHA基本架构

     先来看个手动挡切换的 HA 架构图:

wKiom1PLxCWRWGoyAAFSXeD1ZAw943.jpg

    在一个典型的 HDFS  HA 场景中,通常由两个 NameNode 组成,一个处于 Active 状态,另一个处于 Standby 状态。 Active NameNode 对外提供服务,比如处理来自客户端的 RPC 请求,而 Standby NameNode 则不对外提供服务,仅同步 Active NameNode 的状态,以便能够在它失败时快速进行切换。

为了能够实时同步 Active 和 Standby 两个 NameNode 的元数据信息(实际上 editlog ) ,需提供一个共享存储系统,可以是 NFSQJ ( Quorum Journal Manager )或者 Bookeeper , Active  NameNode 将数据写入共享存储系统,我们可以在 Active  NameNode 的 50070 端口上看到相应的 NameNode Journal  Status 信息:wKiom1PLxL_TKDIFAAIHocrMywM300.jpg

同时 Standby NameNode 监听该系统( QJM 管理下的 Journalnode 进程对应的存储路径),一旦发现有新数据写入,则读取这些数据,并加载到自己内存中,以保证自己内存状态与 Active NameNode 保持基本一致,那么在紧急情况下 standby NameNode 便可快速切为 Active NameNode 。另外,在 Hadoop1.X 中的 Secondary NameNode 或者自己通过 nfs 热备的 NameNode 信息在 Hadoop2.X 中已经不再需要了,他们被 Standby NameNode 取代了。  在 Yarn 的官网中,我还看到一段关于 JournalNode 错误兼容性信息:wKiom1PLxWHRVtBxAAQOByhmflQ995.jpg

大概意思是主备 NameNode 之间通过一组 JournalNode 同步元数据信息(我的通俗理解就是 QJM 类似一个数据池,池里边装着多个 JournalNode 进程存储 editlog , Active NameNode 往池里边的 JournalNode 进程写 editlog , StandBy NameNode 向池里边的 JournalNode 取数据同步),一条数据只要成功写入多数 JournalNode 即认为写入成功。启动的 JournalNode 的个数必须为奇数个。如果你的 HA 策略中启动了 N 个 JournalNode 进程那么整个 QJM 最多允许 (N-1)/2 个进程死掉,这样才能保证 editLog 成功完整地被写入。比如 3 个 JournalNode 时,最多允许 1 个 JournalNode 挂掉, 5 个 JournalNode 时,最多允许 2 个 JournalNode 挂掉。

3、  HDFS HA部署

 3.1、部署和测试环境

HDFS HA 的部署和验证是在公司的测试集群中完成,其中测试集群中数据节点一共有 4 个主机名分别为 hadoop-slave1 、 hadoop-slave02 、 hadoop-slave03 、 hadoop-slave04 , master 节点的主机名为 hadoop-master 。因为 JournalNode 和 Zookeeper 进程是非常轻量级的,可以其他服务共用节点。现在的部署情况是:

hadoop-master :作为 Active NameNode 

haoop-slave01: 作为 StandBy NameNode 

hadoop-slave02: 作为 DataNode ,并且启动一个 JournalNode 、启动一个 Zookeeper 

hadoop-slave03: 作为 DataNode ,并且启动一个 JournalNode 、启动一个 Zookeeper 

hadoop-slave04: 作为 DataNode ,并且启动一个 JournalNode 、启动一个 Zookeeper 

其他软件:

Apache Hadoop 2.2.0 、 JDK1.6 

3.2、修改配置文件

主要配置 ${HADOOP_HOME}/etc/hadoop/ 下的 hdfs-site.xml 。下面是一些配置参数以及说明:

(1)        dfs.nameservices 

HDFS 的命名服务逻辑名称,可以自己定义。在已经配置 HA 策略的 HDFS 会用到这个逻辑名称,同时该名称也会被基于 HDFS 的系统用,例如 HBASE 等。另外,如果需要启动 HDFS Federation 的话,可以通过该参数指定多个服务逻辑名称,用“,”作为分隔符。

我的配置如下:

<property>

   <name>dfs.nameservices</name>

    <value>mycluster</value>

    <description>Logical name forthis new nameservice</description>

</property>

(2)       dfs.ha.namenodes.[$nameserviceID] 

命名服务下面包含的 NameNode 列表,可为每个 NameNode 指定一个自定义的 ID 名称,比如命名服务 testCluster 下有两个 NameNode ,分别命名为 nn1 和 nn2 (到目前为止一个命名服务下最多包含 2 个 NameNode ),我的配置如下:

<property>    <name>dfs.ha.namenodes.testCluster</name>    <value>nn1,nn2</value>    <description>Unique identifiers for each NameNode in the nameservice </description></property>

(3)        dfs.namenode.rpc-address.[$nameserviceID].[$name node ID] 

这个参数很容易理解,主要是为每个 NameNode 设置 RPC 地址,我的配置如下:

<property>

   <name>dfs.namenode.rpc-address.testCluster.nn1</name>

    <value>hadoop-master:8020</value>

</property>

<property>

   <name>dfs.namenode.rpc-address.testCluster.nn2</name>

   <value>hadoop-slave01:8020</value>

</property>

(4)        dfs.namenode.http-address.[$nameserviceID].[$name node ID] 

这个参数主要是为 NameNode 设置对外的 HTTP 地址, 通过此配置的指定你可以执行在浏览器中管理 HDFS 界面等操作。我的配置如下:

<property>

    <name>dfs.namenode.http-address.testCluster.nn1</name>

    <value>hadoop-master:50070</value>

</property>

<property>

    <name>dfs.namenode.http-address.testCluster.nn2</name>

    <value>hadoop-slave01:50070</value> 

</property>

(5)        dfs.namenode.shared.edits.dir 

设置一组 JournalNode 的 URL 地址, ActiveNameNode 会将 Edit Log 写入这些 JournalNode 所配置的本地目录(可以用 nfs 等共享文件系统,由参数 dfs.journalnode.edits.dir 控制)中,而 StandByNameNode 通过 DataNode 的心跳通知去读取这些 Edit Log ,并且作用在内存中的目录树中,其配置格式为: qjournal://host1:port1;host2:port2;host3:port3/journalId ,我的配置如下:

<property>

       <name>dfs.namenode.shared.edits.dir</name>

       <value>qjournal://hadoop-slave02:8485;hadoop-slave03:8485;hadoop-slave04:8485/testcluster</value>

        <description>journalNodeList</description>

</property>

(6)        dfs.journalnode.edits.dir 

这个就是刚刚提到的 JournalNode 所在节点上的一个目录,用于存放 editlog 和其他状态信息,该参数只能设置一个目录,你可以对磁盘做 RIAD 提高数据可靠性。

<property>

       <name>dfs.journalnode.edits.dir</name>

        <value>/home/hadoop/hadoop-2.2.0/journal/node/local/data</value>

</property>

(7)        dfs.client.failover.proxy.provider.[$nameserviceID] 

该参数设置 HDFS 客户端与 ActiveName 进行交互的 JAVA 实现类, HDFS 客户端通过该参数来寻找到集群中的 Active NameNode ,此类默认实现 ConfiguredFailoverProxyProvider ,我的配置如下:

<property>

        <name>dfs.client.failover.proxy.provider.testcluster</name>

       <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>

</property>

(8)        dfs.ha.fencing.methods 

这个参数比较重要,主要用于在主备节点切换时实现隔离机制的,在官方网站中做了相当详细的配置说明,其大概意思为:主备架构解决单点故障问题时,必须要认真解决的是脑裂问题,即出现两个 master 同时对外提供服务,导致系统处于不一致状态,可能导致数据丢失等潜在问题。在 HDFS HA 中, JournalNode 只允许一个 NameNode 写数据,不会出现两个 Active NameNode 的问题,但是,当主备切换时,之前的 Active NameNode 可能仍在处理客户端的 RPC 请求,为此,需要增加隔离机制( fencing )将之前的 Active NameNode 杀死。 HDFS 允许用户配置多个隔离机制,当发生主备切换时,将顺次执行这些隔离机制,直到一个返回成功。 Hadoop 2.0 内部打包了两种类型的隔离机制,分别是 shell  和 sshfence 。

1 ) sshfence 方式

sshfence 通过 ssh 登录到前一个 ActiveNameNode 并将其杀死。为了让该机制成功执行,需配置免密码 ssh 登陆(注意:这个为主备节点配置双向的 RSA 免密码登陆),这可通过参数 dfs.ha.fencing.ssh.private-key-files 指定一个私钥文件。我的配置如下:

<property>

        <name>dfs.ha.fencing.methods</name>

       <value>sshfence</value>

</property>

<property>

       <name>dfs.ha.fencing.ssh.private-key-files</name>

       <value>/home/hadoop/.ssh/id_rsa</value>

</property>

另外,在设置一个超时时间,一旦 ssh 超过该时间,则认为执行失败。我的配置如下:

<property>

   <name>dfs.ha.fencing.ssh.connect-timeout</name>

    <value>30000</value>

</property>

2 )   shell 方式(我没有采用这种方式)

执行自定义的 Shell 脚本命令隔离旧的 ActiveNameNode 。相比于 sshfence 方式,个人认为这种方式有个好处就是,你在 shell 脚本里边可以将之前的 Active NameNode 直接 kill 掉,然后立马启动 NameNode ,此时刚刚启动的 NameNode 就是立马处于一个 StandBy 状态,立马就可以进入 HA 状态,如果采用 sshfence 方式还要手动自己重启刚刚被 kill 掉的 NameNode 从而才能进入 HA (这些的前提都是,采用手动 HA 方式,之前的 Acitve NameNode 不是宕机而仅仅是 NameNode 进程挂掉)。配置可以为:

<property>

   <name>dfs.ha.fencing.methods</name>

   <value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>

</property>

注意, Hadoop 中所有参数将以环境变量的形似提供给该 shell ,但所有的“ . ”被替换成了“ _ ”,比如“ dfs.namenode.rpc-address.ns1.nn1 ”变为“ dfs_namenode_rpc-address ”。

3.3、 启动各种服务

HDFS 集群进程启动的大概顺序为:启动所有的 JournalNode à 启动 nn1 和 nn2 à 启动所有 DataNode 。具体详细步骤如下:

(1)            启动所有 JournalNode 

在所有的配置有 JournalNode 的服务节点上,以我的配置就是在 hadoop-slave02 、 hadoop-slave03 和 hadoop-slave04 上分别执行 : 

$HADOOP_HOME/sbin/hdfs-daemon.sh startjournalnode

(2)            初始化 JournalNode 

此步骤要注意的是,如果你是将非 HA HDFS 的集群转化成为 HA HDFS 那么这一步骤就需要,如果都是 HA HDFS 就不需要执行此步骤。该步骤的主要作用是将非 HA HDFS 中 NameNode 的 edit log 去初始化 JourNalnodes 。具体操作在 nn1 上执行 : 

$HADOOP_HOME/bin/hdfs namenode -initializeSharedEdits [-force | -nonInteractive] 。

此命令默认是交互式的,需要用户输入各种 YOR N ,如果嫌麻烦就直接执行:

$HADOOP_HOME/bin/hdfs namenode�initializeSharedEdits �force

(3)            启动 nn1 和 nn2 

子步骤 1 :

进入 nn1 ,如果是新集群则 format (注意,如果不是新集群千万不要 format ):

$HADOOP_HOME/bin/hadoop namenode -format

子步骤 2 :进入 nn1 ,接着启动 nn1: 

hadoop-daemon.sh start namenode

子步骤 3 :进入 nn2 ,执行下面命令让 nn2 从 nn1 上将最新的 FSimage 信息拉回来:

注意:如果是 nn2 的 NameNode 已经是被 format 掉了或者是将非 HA HDFS 的集群转化成为 HA HDFS 则不需要执行这一个步骤。

$HADOOP_HOME/bin/hdfs namenode -bootstrapStandby -force

子步骤 4 : 进入 nn2 ,然后启动 nn2: 

hadoop-daemon.sh start namenode

子步骤 5 : 启动所有的 DataNode 

在各个 DataNode 节点执行:

hadoop-daemon.sh start datanode

或者直接在 nn1 节点直接执行:

hadoop-daemons.sh start namenode

各个服务到现在为止已经启动完毕,主备节点都还处于 StandBy 状态。我们可以看到主备节点的信息:

wKioL1PLyVTyFREgAAFx2OhoUj8896.jpg

wKiom1PLyDrj0IDwAAF2lMzHTf8341.jpg

    在这里说说一个遇到的“错误”问题,我在分别启动 nn1 和 nn2 之后,还没有将其中一个切换为 Acive NameNode 时,在 nn1 和 nn2 的日志上都报了以下这个 “错误”:

wKioL1PLyarhnl5SAAxOUiZjzUY964.jpg

其实这个错误信息完全可以不用管,出现这个问题原因上面信息已经很明显了,只要接下来将其中一个切换成 Acive NameNode 就 ok 了。

3.4、手动切换Active NameNode

nn1 和 nn2 启动后都处于 StandBy 状态,此时都不能够对外提供服务,现在需要将 nn1 切换为Active NameNode ,进入 nn1 节点输入:

$HADOOP_HOME/bin/hdfs haadmin-transitionToActive nn1

切换后我们再看看 50070 页面, nn1 已经被切换为 Active 了:

wKioL1PLykjC_HGjAAHDEdwSPbc419.jpg

wKiom1PLyS7iK41rAAF9b0gK81Q739.jpg

在来看看之前还没有切换 Acive NameNode 的“错误”信息已经消失了,下面分别是 nn1 和 nn2 的日志信息,非常正常:

wKioL1PLyrfyTWhbAAas2ZqM84I807.jpg

wKiom1PLyZ3R7b8UAAiUOLt1PdY291.jpg

另外,如果你现在想将 nn2 转化为 Acive NameNode ,则在进入 nn2 所在节点,输入命令:

$HADOOP_HOME/bin/hdfs haadmin-failover --forcefence --forceactive nn1 nn2

看看 nn2 上的日志:

wKiom1PLyhPTS5P4AA5mkf9FZZk906.jpg

在这里说说我在切换时遇到过的几个小问题:

  • 在住备节点上一定要配置双向的 RSA 免密码登陆,不然再切换时会出错, sshfence 方式不能找到旧的 Active NameNode ,直接被 reject 掉。

  • 第二,在切换的过程中我遇到了这个错误:

wKiom1PLymCDvQ-tAAnc5Wu1wVg264.jpg

这个是权限问题,解决方法是直接在 core-site.xml 文件添加下面权限控制选项:

<property>

   <name>hadoop.http.filter.initializers</name>

    <value>org.apache.hadoop.security.AuthenticationFilterInitializer</value>

</property>

<property>

   <name>hadoop.http.authentication.type</name>

    <value>simple</value>

</property>

<property>

   <name>hadoop.http.authentication.token.validity</name>

    <value>36000</value>

</property>

<property>

   <name>hadoop.http.authentication.signature.secret.file</name>

    <value>/home/hadoop/hadoop-http-auth-signature-secret</value>

</property>

<property>

   <name>hadoop.http.authentication.cookie.domain</name>

    <value></value>

</property>

<property>

   <name>hadoop.http.authentication.simple.anonymous.allowed</name>

    <value>true</value>

</property>

然后建立 /home/hadoop/hadoop-http-auth-signature-secret 文件,并且在文件写入访问用户,我写入的是hadoop,然后将这个文件scp到各个节点,问题解决。

  • 如果你将 Active NameNode 从 nn1 转到 nn2 后,在各个 DataNode 日志出现一个 “错误”信息:

wKioL1PLy9KB-KjfAAUasSkPxVg154.jpg

其实这个是我意料之中的“错误”信息,其实是没有任何问题的。因为,当你的 Acive NameNode 从 nn1 切换至 nn2 时, nn1 就会被 kill (即 hadoop-msater 中的 NameNode 进程会被 kill 掉),在上面切换日志我标注红方框的地方已经很清楚了。此时,各个 DataNode 还是会同时向 Active NameNode 和 StandBy NameNode 同时发送心跳信息的,因为 nn1 已经被 kill 掉了,所有会报这个信息,对系统没有任何影响,切换后正常使用,如果你重启 nn1 则不会再报信息了,新启动的 nn1 是处于 StandBy 模式的。

我们知道, StandByNameNode 是不处理 DataNode 的 RPC 请求的,那么各个 DataNode 为什么还会同时向 Active NameNode 和 StandBy NameNode 同时发送心跳呢?这是因为这 2 个心跳的用途是不同的,各个 DataNode 向 Active NameNode 发送心跳主要是汇报数据块的状态信息,而向 StandBy NameNode 发心跳的主要目的是通知 StandBy NameNode 告诉它 Active NameNode 元数据发生了改变,要求 StandBy NameNode 去 QJM 区下载更改过的 Edit Log 信息。

3.5、配置自动切换模式

自动切换模式的实现需要下面两个组建的额支持:

(1)        Zookeeper 实例

需要质数个 Zookeeper 实例,在本集群我一个启用了 3 个 Zookeeper 实例,分别部署在 hadoop-slave02 、 hadoop-slave03 、 hadoop-slave04 中。

(2)        ZKFailoverController (简称“ ZKFC” )

ZKFC 是一个 Zookeeper 客户端,负责监控和管理 NameNode 的状态,每台运行 NameNode 的机器上也会运行一个 ZKFC 进程。

  • 健康状况监控:

ZKFC 周期性地与本地的 NameNode 交互,执行一些健康状况监测命令。

  • Zookeeper session 管理:

如果本地 NameNode 是健康的,则会持有 Zookeeper 上一个 znode ,如果它是 Active 的,会持有 zookeeper 的仅有的一个特殊 znode ,该 znode 类型为 ephemeral ,一旦 namenode 挂掉后,会自动消失。

  • 基于 zookeeper 的选举:

如果本地 NameNode 是活的,而没有其他 Namenode 持有特殊的 znode , ZKFC 将尝试获取这个 znode ,一旦获取成功后,则认为它“赢得了选举”,进而隔离之前的 Active namenode ,自己转换为新的 Active namenode 。其大概结构如下图:

wKiom1PLyy3gOWxrAAKQ0Uj7IRQ128.jpg

具体配置步骤:

步骤 1 :关闭集群修改 hdfs-site.xml 配置文件,增加自动切换选项:

<property>

<name>dfs.ha.automatic-failover.enabled</name>

<value>true</value>

</property>

步骤 2 :编辑 core-site.xml 文件,添加 Zookeeper 实例:

<property>

<name>ha.zookeeper.quorum</name>

<value>hadoop-slave02:2181,hadoop-slave03:2181,hadoop-slave04:2181</value>

</property>

步骤 3 :启动节点上的 zookeeper 实例:

分别进入 hadoop-slave02 、 hadoop-slave03 、 hadoop-slave04 节点执行:

$ZOOKEEPER_HOME/bin/zkServer.sh start

Zookeeper 实例对应的进程名为:

wKiom1PLy5DSX-H5AAB-yleplXQ550.jpg

步骤 4 :初始化 zookeeper 。

注意:这个步骤是针对第一次启动 zookeeper 实例用的,如果你的 zookeeper 实例不是第一次启动则不需要执行此命令。

$HADOOP_HOME/bin/hdfs zkfc -formatZK

步骤 5 :启动 JournalNode 、 NameNode 和 DataNode 。

步骤 6 :启动 ZKFC 。

分别进入 hadoop-master 和 hadoop-slave1 即在各个 NameNode 节点上执行:

$HADOOP_HOME/sbin/hadoop-daemon.sh startzkfc

ZKFC 对应的进程名为:

wKioL1PLzKvzOi9OAACkDfchk2U559.jpg

要注意的一点是:我们最先启动的 NameNode 为 Active NameNode 。现在为止配置完毕,验证请看下面一小节。

4、 HDFS HA机制的可用性验证

4.1手动切换模式验证

这里我使用的验证方法主要是模拟 ActiveNameNode 进程死掉的情况,另外 Active NameNode 所在节点发生宕机的情况也是一样的。现在集群中 nn1 为 Active NameNode , nn2 为 StandBy NameNode ,具体步骤:

步骤 1 :进入 nn1 所在节点即 hadoop-master ,运行 kill -9  $NameNodePID 将 nn1 杀死(此时集群中只有一个 StandByNameNode )。

步骤 2 :往集群上传文件 , 或者执行 hadoop fs 相关命令提示连接不到(此时,集群中没有 Active NameNode 来处理客户端的 RPC 请求)。看错误信息:

wKioL1PLzTqhS06nAAd7j72C53I301.jpg

步骤 3 :恢复集群,将 StandBy NameNode 转换为 Active NameNode 。进入 nn2 所在节点即hadoop-slave01 执行:

$HADOOP_HOME/bin/hdfshaadmin -transitionToActive nn2

此时, nn2 已经变成为 Active NameNode ,看 50070 :

wKioL1PLzYny5_EIAAGPec3rbko752.jpg

步骤 4 :再次执行 hadoop fs 相关命令或者上传文件,一切正常。

步骤 5 :另外,不要忘记集群虽然是恢复了,但是此时已经没有了 StandBy NameNode 了,这是直接进入 nn1 所在节点启动 NameNode 进程,此时 nn1 为 Standby NameNode 。

wKiom1PLzG_DvTUBAAGmyp--GSc442.jpg

当目前为止,一起验证以及恢复已经完成。各个服务的日志也恢复了正常。

4.2、自动切换模式验证

自动切换模式的验证和手动切换基本一样,还是手动 kill 掉 Active NameNode 进程,观察集群是否会自动恢复,将备用节点转换为 Active NameNode 。经过测试,当手动 kill 掉 Active NameNode 后, Standby NameNode 成功地自动转换为 Active NameNode 继续服务于个个 DataNode 。

参考文献:

[1] http://hadoop.apache.org/docs/r2.2.0/hadoop-yarn/hadoop-yarn-site/HDFSHighAvailabilityWithQJM.html

[2] http://www.sizeofvoid.net/hadoop-2-0-namenode-ha-federation-practice-zh/

[3] http://yanbohappy.sinaapp.com/?p=205

本文出自 “一只风骚的蚂蚁” 博客,谢绝转载!

0 0
原创粉丝点击