为Hbase建立高可用性多主节点

Hadoop与HBase的设计中的从节点能够进行自动错误恢复。因为集群中存在大量的机器,所以很可能出现单台服务器的硬件错误或者单个从节点的异常。

至于主节点,HBase自身是不存在单点故障(Single Point of Failure – SPOF)的。HBase使用ZooKeerper作为中央控制服务。ZooKeeper用于三台以上的服务器集群之中,只要还有超过半数的服务器在线,ZooKeeper就能够正常提供服务。

HBase将活动主节点、域根节点服务器(root region server)地址以及其它重要的运行数据存放于ZooKeeper。因此,我们就可以在其它机器上开启两个或多个HMaster守护进程,其中第一个启动的HMaster作为HBase机器的活动主节点。

不过,HDFS的NameNode可能引起集群的单点故障。NameNode保存了HDFS全部文件系统的镜像在自己的本地存储之中。一旦NameNode异常,HDFS就无法使用了,导致HBase也无法使用。HDFS当中还有一个Secondary NameNode,它并不是一个备份NameNode,只是提供了一个NameNode的时间点保存镜像(checkpoint)。所以HBase集群的高可用性(highly available),实际上就是保持NameNode的高可用性。

本文中,我们将描述如何搭建两台高可用性的主节点,节点之间使用Heartbeat 互相探测。Heartbeat在Linux集群的HA的方案中被广泛使用,用于通讯与身份识别。Heartbeat 需要与集群资源管理(Cluster Resource Manager – CRM)绑定用于启动和停止集群服务。Heartbeat 推荐使用集群资源管理器Pacemaker来实现。我们将使用Heartbeat 和Pacemaker来搭建一个虚拟IP(Virtual IP – VIP)地址,然后将它与活动的主节点关联起来。因为EC2不支持静态IP,所以我们不能够在EC2上面实现上述方案,但我们将使用弹性IP(Elastic IP – EIP)来达到我们的目的。
我们主要着眼于建立NameNode和HBase,你也可以使用相似的方式来建立JobTracker节点。

准备

你应当已经安装好HDFS与HBase。我们需要使用一台已经准备好的机器建立一个备用的主节点(master2),确认所有的依赖都已经被正确的配置好了,将Hadoop与HBase的根目录从活动的主节点(master1)同步到备份节点上。
我们同样也需要NFS。搭建好NFS服务器,从 master1 和 master2 上挂载相同的NFS目录。确认hadoop用户在NFS目录中有写权限。在NFS中建立目录存储Hadoop的元信息(metadata)。我们假设该目录为/mnt/nfs/hadoop/dfs/name.

我们为两个主节点建立虚拟IP,假设IP地址与DNS映射如下:

  • master1: IP地址为 10.174.14.11
  • master2: IP地址为 10.174.14.12
  • master: IP地址为 10.174.14.10稍后会设置为虚拟IP

如何实施

下面开始介绍如何建立两台高可用性主节点。

安装并配置Heartbeat 与Pacemaker

首先,我们安装Heartbeat 与Pacemaker,并且做一些配置

在 master1 and master2 上执行如下命令以安装Heartbeat 与Pacemaker(在centos 6.0 64x,使用yum可以安装,译者注):
root# apt-get install heartbeat cluster-glue cluster-agents pacemaker
在 master1 与master2 上对Heartbeat 进行如下配置:
root# vi /etc/ha.d/ha.cf

# enable pacemaker, without stonith
# 开启pacemaker,不使用stonith
# (译者注:STONITH这个名词为“Shoot The Other Node In The Head” .
# STONITH的作用便是当宕机发生时,主要服务器可以藉此功能让备份服务器开机,以避免备份服务器启用浮动IP与相关服务,
# 与主要服务器互抢控制权)
crm yes
# log where ?
logfacility local0
# warning of soon be dead
warntime 10
# declare a host (the other node) dead after:
deadtime 20
# dead time on boot (could take some time until net is up)
initdead 120
# time between heartbeats
keepalive 2
# the nodes
node master1
node master2
# heartbeats, over dedicated replication interface!
ucast eth0 master1 # ignored by master1 (owner of ip)
ucast eth0 master2 # ignored by master2 (owner of ip)
# ping the name server to assure we are online
ping ns

建立认证(authkeys )文件,在master1 与master2 上使用root用户执行下述脚本:
root# ( echo -ne “auth 1\n1 sha1 “; \
dd if=/dev/urandom bs=512 count=1 | openssl md5 ) \
> /etc/ha.d/authkeys
root# chmod 0600 /etc/ha.d/authkeys

创建并安装一个NameNode资源代理

Pacemaker 需要依靠资源代理来管理集群。在我们的例子中,虚拟IP和HDFS的NameNode服务就是希望用Pacemaker管理的集群资源。Pacemaker通过IP地址资源代理来管理虚拟IP,因此只需要建立NameNode的资源代理:

1 在 master1 和master2 的root用户下的 .bashrc 文件加入环境变量,别忘记在系统中应用这些变动:

root# vi /root/.bashrc

export JAVA_HOME=/usr/local/jdk1.6
export HADOOP_HOME=/usr/local/hadoop/current
export OCF_ROOT=/usr/lib/ocf

执行如下命令来应用上述修改:
root# source /root/.bashrc
建立一个名为namenode的标准开放集群框架(Open Clustering Framework - OCF)资源代理文件。Namenode资源代理文件文件开头包含如下内容:
root# vi namenode

#!/bin/sh
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
usage() {
echo "Usage: $0 {start|stop|status|monitor|meta-data|validate-all}"
}

添加meta_data() 方法。该方法将资源代理的元数据导出到标准输出。每个资源代理都需要有一组对于自身目标以及支持参数的XML元数据描述:
root# vi namenode

meta_data() {cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="namenode">
<version>0.1</version>
<longdesc lang="en">
This is a resource agent for NameNode. It manages HDFS namenode daemon.
</longdesc>
<shortdesc lang="en">Manage namenode daemon.</shortdesc>
<parameters></parameters>
<actions>
<action name="start" timeout="120" />
<action name="stop" timeout="120" />
<action name="status" depth="0" timeout="120" interval="120" />
<action name="monitor" depth="0" timeout="120" interval="120" />
<action name="meta-data" timeout="10" />
<action name="validate-all" timeout="5" />
</actions>
</resource-agent>
END
}

添加namenode_start() 方法。Pacemaker使用这个方法在服务器上开启NameNode的守护进程。在这个方法中,我们检测NameNode是否已经启动,如果未启动,我们通过hadoop用户执行hadoop-daemon.sh 来启动:
root# vi namenode

namenode_start() {
# if namenode is already started on this server, bail out early
namenode_status
if [ $? -eq 0 ]; then
ocf_log info "namenode is already running on this server, skip"
return $OCF_SUCCESS
fi
# start namenode on this server
ocf_log info "Starting namenode daemon..."
su - hadoop -c "${HADOOP_HOME}/bin/hadoop-daemon.sh start name node"
if [ $? -ne 0 ]; then
ocf_log err "Can not start namenode daemon."
return $OCF_ERR_GENERIC;
fi
sleep 1
return $OCF_SUCCESS
}

添加namenode_stop() 方法。Pacemaker使用这个方法停止服务器上的NameNode守护进程。在这个方法中,我们检查NameNode是否已经停止,如果尚未停止,我们通过hadoop用户执行hadoop-daemon.sh 来停止:
root# vi namenode

namenode_stop () {
# if namenode is not started on this server, bail out early
namenode_status
if [ $? -ne 0 ]; then
ocf_log info "namenode is not running on this server, skip"
return $OCF_SUCCESS
fi
# stop namenode on this server
ocf_log info "Stopping namenode daemon..."
su - hadoop -c "${HADOOP_HOME}/bin/hadoop-daemon.sh stop name node"
if [ $? -ne 0 ]; then
ocf_log err "Can not stop namenode daemon."
return $OCF_ERR_GENERIC;
fi
sleep 1
return $OCF_SUCCESS
}

添加namenode_status() 方法。Pacemaker使用这个方法在服务器上检测NameNode守护进程的状态。在这个方法中,我们jps 命令来打印出 hadoop 用户所有运行的java进程,并使用grep 方法来过滤出NameNode守护进程来观察其是否正常启动:
root# vi namenode

namenode_status () {
ocf_log info "monitor namenode"
su - hadoop -c "${JAVA_HOME}/bin/jps" | egrep -q "NameNode"
rc=$?
# grep will return true if namenode is running on this machine
if [ $rc -eq 0 ]; then
ocf_log info "Namenode is running"
return $OCF_SUCCESS else
ocf_log info "Namenode is not running" return $OCF_NOT_RUNNING
fi
}

添加namenode_validateAll() 方法。确保在我们运行其它方法之前,所有的环境变量都已经被正确的设置了:
root# vi namenode

namenode_validateAll () {
if [ -z "$JAVA_HOME" ]; then
ocf_log err "JAVA_HOME not set."
exit $OCF_ERR_INSTALLED
fi
if [ -z "$HADOOP_HOME" ]; then
ocf_log err "HADOOP_HOME not set."
exit $OCF_ERR_INSTALLED
fi
# Any subject is OK
return $OCF_SUCCESS
}

添加如下主例程。调用上述方法来实现必须的标准开放集群框架( OCF)代理动作:
root# vi namenode

# See how we were called.
if [ $# -ne 1 ]; then
usage
exit $OCF_ERR_GENERIC
fi
namenode_validateAll
case $1 in
meta-data) meta_data
exit $OCF_SUCCESS;;
usage) usage
exit $OCF_SUCCESS;;
*);;
esac
case $1 in
status|monitor) namenode_status;;
start) namenode_start;;
stop) namenode_stop;;
validate-all);;
*)usage
exit $OCF_ERR_UNIMPLEMENTED;;
esac
exit $?

在 master1 和 master2 上修改namenode的文件权限:

root# chmod 0755 namenode
root# ocf-tester -v -n namenode-test /full/path/of/namenode

2 确认在进入下一步之前,上述所有代码测试通过,否则高可用性集群可能会出现异常.

将namenode 资源代理安装到master1 和 master2 的 hac 目录之下:
root# mkdir ${OCF_ROOT}/resource.d/hac
root# cp namenode ${OCF_ROOT}/resource.d/hac
root# chmod 0755 ${OCF_ROOT}/resource.d/hac/namenode

配置高可用性的NameNode

我们已经通过Heartbeat 和 Pacemaker配置了高可用性的NameNode。接下来就是配置一个虚拟IP地址,Hadoop 和HBase使用该虚拟IP地址作为他们的主节点地址。虚拟IP首先指向活动的主节点,一旦主节点异常,Heartbeat 和 Pacemaker能够检测,并且将虚拟IP转换到备用主节点,然后在上面启动NameNode。

启动 master1 and master2 的Heartbeat :
root# /etc/init.d/heartbeat start
修改 crm 默认配置。master1 或 master2 的所有资源相关命令都只执行一次:
root# crm configure property stonith-enabled=false
root# crm configure property default-resource-stickiness=1

3 使用虚拟IP地址加入一个虚拟IP资源:

root# crm configure primitive VIP ocf:heartbeat:IPaddr params ip=”10.174.14.10″ op monitor interval=”10s”
4 修改Hadoop的配置,使用虚拟IP地址。在修改配置以后,同步配置文件到所有主节点、客户端和子节点:
hadoop$ vi $HADOOP_HOME/conf/core-site.xml

<property>
<name>fs.default.name</name>
<value>hdfs://master:8020</value>
</property>

5 配置Hadoop将元信息同时写入本地与NFS,同步配置文件到所有主节点、客户端和子节点:
hadoop$ vi $HADOOP_HOME/conf/hdfs-site.xm

<property>
<name>dfs.name.dir</name>
<value>/usr/local/hadoop/var/dfs/name,/mnt/nfs/hadoop /dfs/name</value>
</property>

6 将步骤5中创建的 namenode 资源代理加入到Pacemaker,我们将NAMENODE作为它的资源名称:
root# crm configure primitive NAMENODE ocf:hac:namenode op monitor interval=”120s” timeout=”120s” op start timeout=”120s” op stop timeout=”120s” meta resource-stickiness=”1″
7 将虚拟IP资源与NAMENODE资源配置为一个资源组:
root# crm configure group VIP-AND-NAMENODE VIP NAMENODE
8 为虚拟IP资源与NAMENODE资源定义托管(colocation):
root# crm configure colocation VIP-WITH-NAMENODE inf: VIP NAMENODE
9 为虚拟IP资源与NAMENODE资源定义资源排序
root# crm configure order IP-BEFORE-NAMENODE inf: VIP NAMENODE
10 使用crm_mon 命令来验证之前的Heartbeat与资源定义。若一切正常,应该看到如下输出:
root@master1 hac$ crm_mon -1r

 ============

Last updated: Tue Nov 22 22:39:11 2011

Stack: Heartbeat

Current DC: master2 (7fd92a93-e071-4fcb-993f-9a84e6c7846f) - partition with quorum

Version: 1.0.9-74392a28b7f31d7ddc86689598bd23114f58978b

2 Nodes configured, 1 expected votes

1 Resources configured.

============

Online: [ master1 master2 ]

Full list of resources:

Resource Group: VIP-AND-NAMENODE

VIP (ocf::heartbeat:IPaddr): Started master1

NAMENODE (ocf::hac:namenode): Started master1

11 确认虚拟IP资源与NAMENODE资源在同一台服务器上.

12 停止master1 的Heartbeat , 虚拟IP与NAMENOD 在数秒后在 master2 上启动.

13 重新启动master1 的Heartbeat,虚拟IP与NAMENOD 应当还是在 master2. 相关资源不应当回到 master1.

启动DataNode, HBase集群并备份HBase主节点(master)

确认了高可用性的配置正常工作之后,我们可以启动HDFS和HBase了。注意NameNode已经被Pacemaker启动了,所以我们只需要启动DataNode:

16 一切正常的话,启动DataNode:

hadoop@master$ for i in 1 2 3
do
ssh slave$i "$HADOOP_HOME/bin/hadoop-daemon.sh start datanode"
sleep 1
done

从虚拟IP指向的活动主节点(master1 )启动HBase集群:
hadoop@master$ $HBASE_HOME/bin/start-hbase.sh
启动备份节点(master2 )的HMaster进程:
hadoop@master2$ $HBASE_HOME/bin/hbase-daemon.sh start master
工作原理

下图描述了上述步骤执行完成后的集群架构:

首先,两台主节点上都安装了 Heartbeat 与 Pacemake,并配置了Heartbeat以支持Pacemaker。

在“创建并安装NameNode资源代理”章节的第2,我们新建了一个实现标准OCF资源代理的namenode 脚本。其中最重要的部分就是namenode_status,它负责检测NameNode守护进程的状态。我们通过jps 命令来打印出 hadoop 用户所有运行的java进程,并使用grep 方法来过滤出NameNode守护进程来观察其是否正常启动。Pacemaker 使用namenode 资源代理来开启/关闭/监控NameNode守护进程。在namenode 的脚本中,namenode_start 和namenode_stop 是通过hadoop-daemon.sh 对单独服务器的Hadoop 守护进程启动和停止。你能够本文中找到相关源码。

当namenode 资源代理测试、安装完成并正常运行之后启动Heartbeat 。我们修改了一些 crm 的默认配置, default-resource-stickiness=1 非常重要,因为它关闭了资源的自动恢复。

在配置高可用性的NameNode一节中的第三到五步,我们在Pacemaker中添加了虚拟IP资源,并配置了Hadoop 和HBase。这样当活动主节点异常之后,可以切换至备用主节点。

同一章节的第六步,我们配置了Hadoop (HDFS NameNode)将元数据同时写入本地与NFS。若活动主节点异常,备用主节点会读取相同的NFS目录,启动NameNode,从而使用最新的元数据信息,在活动主节点异常之前恢复HDFS的状态。

在步骤7到10中,我们通过 namenode 资源代理构建了名为NAMENODE的资源,然后将 虚拟IP与NAMENOD 配置为一个资源组(步骤8),保证在同一服务器(步骤9),以正确顺序启动(步骤10)。这样可以保证不会出现虚拟IP指向master1,而NameNode在 master2 上面启动。

因为Pacemaker 会通过 namenode 资源代理启动NameNode,所以我们需要单独启动DataNode,也就是我们在步骤1中所做的内容:启动DataNode,HBase集群和HBase备份主节点。

HBase正常启动后,启动备份服务器上面的备份HBase主节点 (HMaster)。在备份服务器的日志中,可发现如下内容证明启动的是备份主节点 (HMaster):

2011-11-21 23:38:55,168 INFO org.apache.hadoop.hbase.master.ActiveMasterManager: Another master is the active master, ip-10-174-14-15.us-west-1.compute.internal:60000; waiting to become the next active master

最终NameNode 和HMaster在两台服务器上启动,并且可以随时进行异常切换,避免了集群中的单点异常。

然而,在生产环境实际运用还有很多问题需要处理。我们还要对高可用性集群进行各种特殊情况测试,如单台服务器断电、网线断开、交换机关闭或者能想到的其它任意情况。

另一方面来说,单点故障其实没有想象中那么严重。根据我们的经验,几乎所有的集群宕机都是因为异常操作或者软件升级,所以保持系统的简单性会让系统更稳定。

更多内容(Amzone EC2)

在 Amazon EC2 上建立高可用性的HBase集群更为复杂,因为EC2不支持静态IP地址,我们无法使用虚拟IP。可以考虑使用弹性IP(elastic-ip EIP)。在EC2中,弹性IP类似静态IP,弹性IP只是与你的账户关联,不是其它什么特殊实例。一旦活动主节点异常,我们可以使用Heartbeat自动关联弹性IP启动备份服务器。接着定义Hadoop和HBase使用与一个公共实例DNS关联的弹性IP来访问当前活动的主节点。在namenode 资源代理中,我们不但要启动或停止NameNode,而且还要处理所有的DataNode。因为活动主节点的IP变化了,DataNode必须重启之后才能访问新的主节点。

因为本文的范围有限,所以我们忽略掉一些细节。我们构建了一个弹性IP(elastic-ip EIP)资源代理来实现上述目的,相关源码在本文中可以找到。

英文原文:Safaribooksonline,编译:ImportNew - 陈晨

译文链接:http://www.importnew.com/3020.html

【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】

关于作者: 陈 晨

致力于互联网大型分布式领域,实践各种软件过程与自动化。爱生活,爱JAVA。新浪微博: 一酌散千忧

查看陈 晨的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

3 条评论

  1. 张宗耀 说道:

    你好,你的这个方案的话应该属于热切还是冷切呢??datanote和jobtracker通过虚拟IP联系NameNode,两个NameNode是failover,哦,你的应该是冷切,当Master故障,Slave启动,然后应用NFS中的数据。

    Thumb up 0 Thumb down 0

  2. liuxun 说道:

    是否可以通过Hadoop自身的Quorun Journal Node来保证HDFS的HA,也就保证了HBase的HA?

    Thumb up 0 Thumb down 0

  3. hino dutro 说道:

    Hello, yeah this post is actually fastidious and I have learned lot of things from it concerning blogging.
    thanks.

    Thumb up 0 Thumb down 0

跳到底部
返回顶部