HDFS HA
HA即为High Availability,用于解决NameNode单点故障问题,该特性通过热备的方式为主NameNode提供一个备用者,一旦主NameNode出现故障,可以迅速切换至备用的NameNode,从而实现对外提供更稳定的服务
SecondaryNameNode(冷备):只是阶段性的合并edits和fsimage。当NN失效的时候,SNN无法立刻提供服务,甚至无法保证数据完整性:在SNN进行合并时,如果NN数据丢失的话,SNN也无法感知到,从而丢失部分操作
HDFS HA提供两个NameNode,一个处于active状态,对外提供服务;另一个处于standby状态,同步元数据(在内存中)。当active状态的NameNode挂掉时,standby转为active。HA 是通过代理对外提供服务,客户端请求由代理接收然后转发到active状态的NN
为了能够实时同步Active和Standby两个NameNode的元数据信息,我们将元数据状态(fsimage)和对元数据的操作(editlog)存放到 journalnode 集群中,standby状态的NN实时从JN中读取数据并执行重演,同时所有DataNode向两个NameNode都发送心跳,从而保证两个NameNode状态的同步
JNN集群遵从半数以上的势力划分,所以其节点数量总为奇数。active-NN向JNN写入editlog时,当半数以上的JNN节点存储成功则停止写入,其它JNN节点从这些已写入的节点copy editlog。正常情况下集群内各节点间保持通信,standby-NN可以从任意节点上读取到正确的数据,若是因为不可抗力而导致集群分裂(集群内节点通讯中断),此时还可以相互通信的节点组成集群小势力,那么规定势力范围(集群内节点数量)小于半数的集群提供的数据视为无效。例如现在有3个JNN,分裂成了 1+1 和 1 两股势力,那么此时即使 1 中的数据是正确的也会被视为无效,standby得知其节点数小于半数时会转而去 1+1 中读取数据
接下来,如果active状态的NN挂掉了,我们就将另一台切换为active状态。那么问题来了,active状态的NN什么时候会挂掉呢?难道我们要一直盯着服务器看吗?这当然是不可能的。这里我们用到了zookeeper作为一个管理者代替我们进行监视。当然管理员也有管理员的脾气,所以监视这种累人的工作zookeeper就交给了FailoverController。于是FailoverController就开始了兢兢业业的工作,认真的监视着两个NN,并定时向zookeeper汇报NN的状态信息。如果active的NN挂掉了,FailoverController就汇报情况给zookeeper,得知情况后的zookeeper就通知监视另一个NN的FailoverController将standby的NN转为active(如果有多个standby的NN则由zookeeper选举出一个转为active)。
那么这里有一个问题就是如果监视active状态NN的FailoverController不幸挂掉了,那么一定时间内没有得到来自FailoverController心跳的zookeeper无法得知当前active状态的NN究竟是否存活,就会认为其已经挂掉,会去选举启动一个新的active-NN,那么这时可能就会同时存在两台active-NN,这时对外提供服务的代理可能就感觉不太好了,因为它无法确定要将请求转发到哪一台active-NN上。所以当FailoverController启动新的active-NN的同时也会将原来为active状态的NN置为standby而不管其是否已经挂掉,确保同一时间avtive状态的NN只有一台
HDFS HA集群框架图
集群设计
现在以四台虚拟机(centOS6.5)为例,主机名分别为node01,node02,node03,node04.
NN-1–>NameNode( active )
NN-2–>NameNode( standby )
DN–>DataNode
ZK–>zookeeper
ZKFC–>FailoverController
JNN–>journalnode
HDFS HA搭建步骤
-
首先确保各节点都配置好静态IP
-
配置各节点的hosts,修改/etc/hosts文件
[root@node01 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.23.130 node01
192.168.23.131 node02
192.168.23.133 node03
192.168.23.134 node04
-
关闭各节点的防火墙
使用service iptables status
命令查看当前防火墙状态
使用service iptables stop
暂时关闭防火墙(防火墙会在下次开机时启动)
使用chkconfig iptables off
永久关闭防火墙(该命令在下次重启时生效) -
时间同步
首先在各个节点上使用yum install ntp
安装ntp命令
执行ntpdate ntp1.aliyun.com
来同步时间(这里使用阿里云服务器的时间) -
配置SSH免密登录
在所有节点上执行ssh-keygen -t rsa -P ‘’ -f ~/.ssh/id_rsa
如果安装的虚拟机是 minimal 模式,则在各个节点上执行 yum install -y openssh-clients
在node01节点上执行以下命令(将node01的公钥加入到其他节点的白名单中
ssh-copy-id -i ~/.ssh/id_rsa.pub root@node01
ssh-copy-id -i ~/.ssh/id_rsa.pub root@node02
ssh-copy-id -i ~/.ssh/id_rsa.pub root@node03
ssh-copy-id -i ~/.ssh/id_rsa.pub root@node04
在node02节点上执行如下命令(将node02的公钥加入node01,即配置node02免密登录node01,从而实现两个NameNode节点免密登录,方便主NameNode挂掉时 standby转active)
ssh-copy-id -i ~/.ssh/id_rsa.pub root@node01
- 所有节点安装配置JDK
所有节点在 /etc/profile 或 ~/.bashrc 中配置JDK环境变量
export JAVA_HOME=/opt/zgl/jdk1.8.0_151 <!-- 你的JDK的实际位置 -->
export PATH=$PATH:$JAVA_HOME/bin
刷新文件使配置生效 source /etc/profile
-
下载zookeeper安装包并解压,这里以 zookeeper-3.4.12 为例。zookeeper是运行在 node02,node03 和 node04 上的,这里我们先将安装包放在node02上进行配置,配置结束后发送到 node03 和 node04
-
将 zookeeper 安装包下 conf 目录下的 zoo_simple.cfg 修改为 zoo.cfg
[root@node02 conf]# mv zoo_simple.cfg zoo.cfg
- 修改 zoo.cfg 文件,修改 dataDir 的位置(随意修改)并添加服务配置,其中1、2、3是zookeeper的服务编号,后面是对应服务器的主机名
dataDir=/var/zgl/zookeeper
server.1=node02:2888:3888
server.2=node03:2888:3888
server.3=node04:2888:3888
- 在 dataDir 声明的目录下创建一个myid文件,在这个文件中写上当前节点所对应的服务ID号。当前为 node02 节点,由上配置 server.1=node02:2888:3888 确定服务ID号为 1
[root@node02 ~]# cat /var/zgl/zookeeper/myid
1
- 将配置好的zookeeper安装包发送到node03 node04(注意目录对应)
[root@node02 zgl]# scp -r zookeeper-3.4.12 root@node03:/opt/zgl/
[root@node02 zgl]# scp -r zookeeper-3.4.12 root@node04:/opt/zgl/
-
在node03,node04 上分别修改 myid 中的 ID号。node03–>2,node04–>3
-
在 node02 node03 node04 上配置zookeeper的环境变量,这里配置到用户环境中,在 ~/.bashrc 文件中添加如下
export PATH=$PATH:/opt/zgl/zookeeper-3.4.12/bin
- 在 node02 node03 node04 上执行
zkServer.sh start
命令启动 zookeeper。
使用 jps 命令查看,如果 QuorumPeerMain 进程启动则配置成功,成功后一直开着就好
[root@node04 zgl]# jps
22035 QuorumPeerMain
23100 Jps
-
下载 hadoop 安装包并解压,这里以 hadoop-2.6.5为例,先在 node01 上进行配置,配置完成后发送到其他节点
-
修改 hdfs-site.xml 文件
<property>
<name>dfs.nameservices</name> <!-- 配置一个服务(集群名称) -->
<value>mycluster</value>
</property>
<property>
<name>dfs.ha.namenodes.mycluster</name> <!-- 提供服务的节点 -->
<value>nn1,nn2</value>
</property>
<!-- 配置两个NameNode的rpc协议的地址和端口 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>node01:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>node02:8020</value>
</property>
<!-- 配置两个NameNode的http协议的地址和端口 -->
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>node01:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>node02:50070</value>
</property>
<!-- 设置journalnade的位置信息 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node01:8485;node02:8485;node03:8485/mycluster</value>
</property>
<!-- journalnade保存数据用的目录-->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/var/zgl/hadoop/ha/jn</value>
</property>
<!-- 通过代理类来让客户端连接active的NameNode -->
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 通过远程登录,杀掉失联的 NameNode来保证只有一个active的NameNode(前提已经设置了免密登录) -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<!--配置NameNode的自动切换的开关 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
- 修改core-site.xml 文件
<property>
<name>fs.defaultFS</name> <!-- HDFS文件默认地址前缀(简化实际访问时路径书写) -->
<value>hdfs://mycluster</value>
</property>
<!--配置三台zookeeper的位置信息 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>node02:2181,node03:2181,node04:2181</value>
</property>
<property>
<name>hadoop.tmp.dir</name> <!-- HDFS文件存储位置 -->
<value>/var/zgl/hadoop/cluster</value>
</property>
- 修改slaves配置文件。配置DataNode节点,注意每行写一个
node02
node03
node04
- 修改hadoop-env.sh配置文件
# 导入java home
export JAVA_HOME=/opt/zgl/jdk1.8.0_151
# 在hadoop-env.sh 配置一条hadoop配置文件所在目录
export HADOOP_CONF_DIR=/opt/zgl/hadoop-2.6.5/etc/hadoop
刷新文件使配置生效 source hadoop-env.sh
- 将配置好的hadoop安装包分发到其他节点上
使用 scp 命令将hadoop安装包分发到其他节点,注意其他节点提前建好目录且与node01目录(名称,层级)保持一致
eg: node01 /opt/zgl/hadoop-2.6.5 其他节点 /opt/zgl
scp -r hadoop-2.6.5 root@node02:/opt/zgl/
scp -r hadoop-2.6.5 root@node03:/opt/zgl
scp -r hadoop-2.6.5 root@node04:/opt/zgl/
- 在各个节点上配置 hadoop 环境变量
vim ~/.bashrc
export HADOOP_HOME=/opt/zgl/hadoop-2.6.5
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
source ~/.bashrc
- 确认关闭所有节点上的HDFS相关进程,在 node01 上执行
stop-dfs.sh
- 启动所有journalnode,在node01、node02、node03分别执行如下命令
hadoop-daemon.sh start journalnode
- 随机选择一台NameNode,这里在 node01 上执行初始化并启动active-NN
hdfs namenode -format //格式化
hadoop-daemon.sh start namenode //启动 namanode
- 另外一台NameNode(node02)节点执行同步active-NN的元数据
hdfs namenode -bootstrapStandby
- 在 node01 或 node02 上初始化zookeeper
hdfs zkfc -formatZK
- 再次关闭所有节点上的HDFS相关进程
stop-dfs.sh
- 在 node01上启动HDFS集群
start-dfs.sh
- 在各个节点上使用 jps 查看启动进程
[root@node01 zgl]# jps
4804 DFSZKFailoverController
4666 JournalNode
4476 NameNode
[root@node02 ~]# jps
23217 DataNode
23155 NameNode
23301 JournalNode
23385 DFSZKFailoverController
22410 QuorumPeerMain
[root@node03 hadoop]# jps
3697 DataNode
3765 JournalNode
3068 QuorumPeerMain
[root@node04 zgl]# jps
22035 QuorumPeerMain
22454 DataNode
- 通过hadoop提供的 web UI 来进行查看
打开浏览器,输入http://192.168.23.130:50070(因为主机 hosts 文件中并未配置node01,所以这里不能使用 http://node01:50070来访问),然后我们就来到如下页面,可以看到当前 node01 为active状态
再输入http://192.168.23.131:50070查看 node02 为 standby状态
然后我们在 node01 上使用 kill 停止NameNode 进程,刷新页面发现 node02 状态变为active