本节书摘来异步社区《HBase管理指南》一书中的第1章,第1.9节,作者: 蒋燚峰 译者: 苏南,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.9 安装多个高可用性(HA)的主节点
Hadoop和HBase的设计目标是能够在各从节点之间自动进行故障转移。 因为在大型集群中可能有很多节点,服务器硬件故障或从节点宕机被视为是集群中的正常现象。
对于主节点来说,HBase本身并没有SPOF。HBase使用ZooKeeper作为它的中央协调服务。ZooKeeper仲裁团通常是一个由3台以上服务器所组成的集群。只要集群中有一半以上的服务器还处于在线状态,ZooKeeper就可以正常提供服务。
HBase将其活跃主节点、根域服务器的位置以及其他一些重要的运行数据都保存在ZooKeeper中。因此,我们只需在两台不同服务器上启动两个(或两个以上的)HMaster守护进程,其中先启动的那个就会成为HBase集群的活跃主服务器。
但是,HDFS的NameNode却是集群的单点故障。NameNode将整个HDFS文件系统的映像文件保存在它的本地存储中。如果NameNode宕机,HDFS就无法正常工作,因此HBase也就宕机了。你可能已经注意到了,HDFS还有一个二级NameNode(Secondary NameNode)。需要注意的是,二级NameNode并不是一个后备NameNode,它只是为NameNode提供了一种检查点的功能。因此,高可用集群的难点就在于实现NameNode的高可用性。
本节将介绍含两个高可用主节点的集群的安装方式,这两个节点将通过Heartbeat软件来监控对方的状态。Heartbeat是一种应用广泛的高可用性解决方案,可以为Linux集群提供通信和成员关系的设定。在启动/停止集群的服务时,需要将Heartbeat与集群资源管理器(CRM,Cluster Resource Manager)结合在一起使用。Pacemaker是最适合Heartbeat的一种集群资源管理器。我们将使用Heartbeat和Pacemaker来建立一个虚拟IP(VIP,Virtual IP)地址,然后将该地址与活跃主节点相关联。因为EC2不支持静态IP地址,所以我们不能在EC2上演示这一过程,但我们可以讨论另一种替代方案:使用弹性IP(EIP,Elastic IP)来达到同样的目的。
本节的重点是如何安装NameNode和HBase,使用类似的方法也可以设置好两个JobTracker节点。
1.9.1 准备
你应该已经安装好了HDFS和HBase。我们将安装一个后备主节点(master2),因此你需要准备好另外一台服务器。请确保所有需要的类库都已正确配置。将你的Hadoop及HBase的根目录从活跃主节点(master1)同步到后备主节点中。
本节也需要使用NFS。请安装好NFS服务器,并在master1和master2上装载好相同的NFS目录。确保hadoop用户具有该NFS目录的写权限。在NFS上创建一个目录来存储Hadoop的元数据。我们假设该目录就是/mnt/nfs/hadoop/dfs/name。
我们将为两个主节点设置VIP,假设其IP地址与DNS的映射关系如下。
master1:它的IP地址为10.174.14.11。
master2:它的IP地址为10.174.14.12。
master:它的IP地址为10.174.14.10。这就是我们后面要进行设置的VIP。
1.9.2 操作步骤
按照如下步骤设置两个高可用主节点。
一、安装配置Heartbeat和Pacemaker
首先,我们要安装Heartbeat和Pacemaker,并进行一些基本的配置。
1.在master1和master2上安装Heartbeat和Pacemaker。
root# apt-get install heartbeat cluster-glue cluster-agents pacemaker
2.在配置Heartbeat时,在master1和master2上都要进行如下修改。
root# vi /etc/ha.d/ha.cf
# enable pacemaker, without stonith
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
3.创建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要依靠一个资源代理来管理集群。资源代理就是一个负责管理集群资源的可执行程序。在本例中,VIP地址和HDFS NameNode的服务都是我们要(使用Pacemaker来)管理的集群资源。Pacemaker带有一个可对VIP进行管理的名为IPaddr的资源代理,因此我们需要创建的只是一个名为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
2.创建一个标准开放集群框架(OCF,Open Clustering Framework)的资源代理文件,将其命名为namenode并在文件中加入如下的内容。
该namenode资源代理启动后,会包含显示元数据以及对NameNode节点进行启动、关闭、状态查询和环境变量验证等标准的OCF功能。下面我们就来逐一添加这些功能。
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|validateall}"
}
3.添加meta_data()函数,代码如下所示。该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
}
4.添加namenode_start()函数。Pacemaker要使用该函数来启动NameNode守护进程。在namenode_start()函数中,首先要检查服务器上是否已经启动了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
}
5.添加namenode_stop()函数。Pacemaker要使用该函数来关闭NameNode守护进程。在namenode_stop()函数中,首先要检查服务器上的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
}
6.添加namenode_status()函数。Pacemaker要使用该函数来监控服务器上NameNode守护进程的状态。在namenode_status()函数中,我们使用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
}
7.添加一个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
}
8.添加主例程。该主例程所做的就是调用前面定义的几个函数来实现一些必备的标准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 $?
9.在master1和master2上修改namenode文件的权限并进行测试。
root# chmod 0755 namenode
root# ocf-tester -v -n namenode-test /full/path/of/namenode
10.请确保所有测试都已通过,然后再进入下一步;否则HA集群就有可能会工作不正常。
11.在master1和master2上,将namenode资源代理程序安装到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了。我们要设置一个VIP地址,然后对Hadoop和HBase进行配置,让它们使用这个VIP地址来作为自己的主节点。NameNode将在那个分配到VIP的活跃主节点上启动。在活跃主节点发生宕机时,Heartbeat和Pacemaker能够检测到宕机并将VIP地址分配给后备主节点,然后在该节点上启动NameNode。
1.在master1和master2上启动Heartbeat。
root# /etc/init.d/heartbeat start
2.修改默认的crm配置。在master1或master2上,所有资源相关的命令只需执行一次。
root# crm configure property stonith-enabled=false
root# crm configure property default-resource-stickiness=1
3.添加一个使用了VIP地址的VIP资源。
root# crm configure primitive VIP ocf:heartbeat:IPaddr params ip="10.174. 14.10" op monitor interval="10s"
4.按如下方法修改Hadoop的配置,让Hadoop使用VIP地址。在完成修改之后,将该配置文件同步到所有的主节点、客户端和从节点上。
hadoop$ vi $HADOOP_HOME/conf/core-site.xml
<property>
<name>fs.default.name</name>
<value>hdfs://master:8020</value>
</property>
5.按如下方法修改HBase的配置,让HBase使用VIP地址。在完成修改之后,将该配置文件同步到所有的主节点、客户端和从节点上。
hadoop$ vi $HBASE_HOME/conf/hbase-site.xml
<property>
<name>hbase.rootdir</name>
<value>hdfs://master:8020/hbase</value>
</property>
6.修改Hadoop的配置,让Hadoop将其元数据写到本地磁盘和NFS上,然后将配置文件同步到所有主节点、客户端和从节点上。
hadoop$ vi $HADOOP_HOME/conf/hdfs-site.xml
<property>
<name>dfs.name.dir</name>
<value>/usr/local/hadoop/var/dfs/name,/mnt/nfs/hadoop/dfs/name </value>
</property>
7.将之前创建的那个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"
8.将VIP资源和NAMENODE资源配置为一个资源组。
root# crm configure group VIP-AND-NAMENODE VIP NAMENODE
9.配置VIP资源和NAMENODE资源的colocation属性。
root# crm configure colocation VIP-WITH-NAMENODE inf: VIP NAMENODE
10.配置VIP资源和NAMENODE资源的资源顺序。
root# crm configure order IP-BEFORE-NAMENODE inf: VIP NAMENODE
11.使用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
12.确认同一台服务器上的VIP资源和NAMENODE资源是否已启动。
13.现在,关闭maste1上的Heartbeat;几秒钟之后,master2上的VIP-AND-NAMENODE就会启动。
14.在master1上重新启动Heartbeat;而master2上的VIP-AND-NAMENODE仍然在运行。资源不会切还给master1。
四、启动DataNode、HBase集群和后备HBase主节点
我们已经确认HA配置能正常工作了,所以现在可以启动HDFS和HBase了。请注意, Pacemaker已经把NameNode启动起来了,所以我们现在只需要启动DataNode。
1.如果一切顺利,我们现在就可以启动DataNode了。
hadoop@master$ for i in 1 2 3
do
ssh slave$i "$HADOOP_HOME/bin/hadoop-daemon.sh start datanode"
sleep 1
done
2.在master上启动HBase集群,master就是VIP地址现在所关联的那台活跃主节点服务器。
hadoop@master$ $HBASE_HOME/bin/start-hbase.sh
3.在后备主节点服务器(本例中即为master2)上启动后备HMaster。
hadoop@master2$ $HBASE_HOME/bin/hbase-daemon.sh start master
1.9.3 运行原理
经过前面这些步骤的操作之后,我们会得到一个如图1-7所示的集群结构。
首先,我们在两个主节点上安装了Heartbeat和Pacemaker,并且对Heartbeat进行了配置,使之能够与Pacemaker联合使用。
在“创建并安装NameNode资源代理”的第2步中,我们创建了namenode脚本,该脚本实现了一个标准的OCF资源代理程序。在namenode脚本中,最重要的是namenode_status函数,它负责对NameNode守护进程的状态进行监控。该函数会使用jps来命令显示hadoop用户下所有正在运行的Java进程,然后使用grep命令来从中查找NameNode守护进程的名字,以此来确定它是否已启动。 Pacemaker要使用namenode资源代理程序来启动、关闭和监控NameNode守护进程。从namenode脚本可以看出,namenode_start和namenode_stop方法实际上是调用了hadoop-daemon.sh脚本来启动/关闭NameNode,之前我们在单服务器上启动/关闭Hadoop守护进程时就使用过这一脚本。在本书附带的源代码中,可以找到该脚本的完整代码。
在测试和安装完namenode资源代理程序之后,我们启动了Heartbeat;然后,我们对一些默认的crm配置进行了修改。“default-resourcestickiness=1”这一设置非常重要,因为它会关闭了资源的自动故障恢复功能。
在“配置高可用NameNode”一节的第3至5步中,我们给Pacemaker增加了一个VIP资源,然后使用该资源对Hadoop和HBase进行了配置。在配置中使用了VIP之后,如果活跃主节点发生宕机,Hadoop和HBase就会切换到与后备主节点进行通信的工作方式。
该节第6步是对Hadoop(HDFS NameNode)进行配置,让它将元数据同时写入到本地磁盘和NFS上。如果活跃主节点宕机了,后备主节点上的NameNode就会启动。因为它们装载的是同一个NFS目录,所以在后备主节点上启动的NameNode也可以从NFS上访问到最新的元数据,然后将HDFS恢复到原活跃主节点宕机之前的状态。
在第7~10步中,我们首先增加了一个NAMENODE资源,它使用了我们在“创建并安装一个NameNode资源代理”一节第2步中创建的namenode资源代理程序(第7步);然后,我们将VIP资源和NameNode的资源设置为了一个资源组(第8步),并确保它们一直运行在同一台服务器上(第9步)按照正确的顺序先后启动(第10步)。我们这样做是因为:我们不希望出现“VIP运行在master1上,而NameNode运行在master2上”这样的一种情况。
因为Pacemaker会通过namenode资源代理程序来启动NameNode,所以我们只需要单独启动DataNode。这就是“启动DataNode、HBase集群和后备HBase主节点”一节的第1步的内容。
在HBase正常启动之后,我们在后备主服务器上启动了后备HBase主节点(HMaster)。如果查看HBase主节点的日志,你会看到类似下面这样的输出,这表明该节点现在是后备HMaster。
2011-11-21 23:38:55,168 INFO org.apache.hadoop.hbase.master.ActiveMaster- Manager: 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运行在了按“主机-后备”方式进行配置的两台服务器上。这样就可以避免集群的单点故障了。
但是,如果是在生产环境中,我们则还有很多工作要做。你需要测试HA集群在各种罕见情况下的反应,比如服务器掉电、网线被拔掉、网络交换机宕机或其他任何你能想到的情况。
另一方面,集群的单点故障可能也不像你想象的那么重要。根据我们的经验,几乎所有集群停机时间都是由于运维人员的失误或需要进行软件升级而引起的。最好还是让你的集群保持简单一些吧。
1.9.4 补充说明
在Amazon EC2上建立一个高可用HBase集群更为复杂,因为EC2不支持静态IP地址,所以我们不能在EC2上使用VIP。替代的方法是使用Elastic IP(弹性IP)地址。弹性IP地址相当于是EC2上的静态IP地址,不过它关联的是你的账户,而不是某个特定的实例。如果活跃主节点宕机了,我们可以使用Heartbeat自动将EIP关联到后备主节点上;然后,我们再对Hadoop和HBase进行配置,让它们使用一个与EIP关联的实例公共DNS,以此来寻找活跃主节点。另外,在namenode资源代理程序中,我们不仅要启动/关闭NameNode,还要启动/关闭所有的DataNode。这是因为活跃主节点的IP地址已经发生了变化,如果不重新启动DataNode,它也无法找到新的活跃主节点。
本节没有对此进行详述,因为它不属于本书所要讲述的范畴。我们创建了一个elastic-ip资源代理程序来实现这一目的。你可在本书附带的源码中找到该资源代理程序。