赞
踩
HA: High Availability,高可用集群,指的是集群7*24小时不间断服务。
在HDFS中,有NameNode、DataNode和SecondaryNameNode角色的分布,客户端所有的操作都是要与NameNode交互的,同时整个集群的命名空间信息也都保存在NameNode节点。但是,现在的集群配置中只有一个NameNode,于是就有一个问题: 单点故障
那么,什么是单点故障呢?现在集群中只有一个NameNode,那么假如这个NameNode意外宕机、升级硬件等,导致NameNode不可用了,整个集群是不是也就不可用了?这就是单点故障的问题。
为了解决这样的问题,就需要高可用集群了。
主从模式(冷备)
准备两台服务器, 准备相同的程序。 一台服务器对外提供服务, 称为主节点(Active节点); 另外一台服务器平时不对外提供服务, 主要负责和Active节点之间进行数据的同步, 称为备份节点(Standby节点). 当主节点出现故障, Standby节点可以自动提升为Active节点, 对外提供服务。
ZooKeeper实现的集群高可用, 采用的就是这种模式。
双主互备(热备)(了解)
准备两台服务器, 准备相同的程序. 同时对外提供服务(此时, 这两台服务器彼此为对方的备份), 这样, 当一台节点宕机的时候, 另外一台节点还可以继续提供服务.
集群多备(了解)
基本上等同于双主互备, 区别就在于同时对外提供服务的节点数量更多, 备份数量更多
我们在这里采用的是主从模式的备份方式,也就是准备两个NameNode,一个对外提供服务,称为Active节点;另外一个不对外提供服务,只是实时的同步Active节点的数据,称为Standby的节点。
为了提供快速的故障转移,Standby节点还必须具有集群中块位置的最新信息。为了实现这一点,DataNodes被配置了两个NameNodes的位置,并向两者发送块位置信息和心跳信号。也就是说,DataNode同时向两个NameNode心跳反馈。
JournalNode的功能
Hadoop2.x版本之后, Clouera提出了QJM/QuromJournal Manager, 这是一个基于Paxos算法实现的HA的实现方案
基本的原理就是使用2N+1台JN存储EditLog, 每次写入数据的时候, 有半数以上的JN返回成功的信息, 就表示本次的操作已经同步到了JN
在HA中, SecondaryNameNode这个角色已经不存在了, 保证Standby节点的元数据信息与Active节点的元数据信息一致, 需要通过若干个JN
当有任何的操作发生在Active节点上的时候, JN会记录这些操作到半数以上的节点中. Standby节点检测JN中的log日志文件发生了变化, 会读取JN中的数据到自己的内存中, 维护最新的目录树结构与元数据信息
当发生故障的时候, Active节点挂掉, 此时Standby节点在成为新的Active节点之前, 会将读取到的EditLog文件在自己的内存中进行推演, 得到最新的目录树结构. 此时再升为Active节点, 可以无缝的继续对外提供服务.
防止脑裂的发生
对于HA群集的正确操作至关重要,一次只能有一个NameNode处于Active状态。否则,名称空间状态将在两者之间迅速分散,从而有数据丢失或其他不正确结果的风险。为了确保该属性并防止所谓的“裂脑情况”,JournalNode将一次仅允许单个NameNode成为作者。在故障转移期间,变为活动状态的NameNode将仅承担写入JournalNodes的角色,这将有效地防止另一个NameNode继续处于活动状态,从而使新的Active可以安全地进行故障转移。
- 怎么理解脑裂?
就是Active节点处于网络震荡状态,假死状态,Standby就转为Active。等网络震荡过后,就有两个Active了,这就是脑裂。
JournalNode集群正常工作的条件
- 至少3个Journalnode节点
- 运行个数建议奇数个(3,5,7等)
- 满足(n+1)/2个以上,才能正常服务。即能容忍(n-1)/2个故障。
JournalNode的缺点
在这种模式下,即使活动节点发生故障,系统也不会自动触发从活动NameNode到备用NameNode的故障转移,必须需要人为的操作才行。要是有一个能监视Active节点的服务功能就好了。
这个时候,我们就可以使用zookeeper集群服务,来帮助我们进行自动容灾了。
如果想进行HA的自动故障转移,那么需要为HDFS部署两个新组件:ZooKeeper quorum和ZKFailoverController进程(缩写为ZKFC)
Apache ZooKeeper是一项高可用性服务,用于维护少量的协调数据,将数据中的更改通知客户端并监视客户端的故障。HDFS自动故障转移的实现依赖ZooKeeper进行以下操作:
故障检测
集群中的每个NameNode计算机都在ZooKeeper中维护一个持久性会话。如果计算机崩溃,则ZooKeeper会话将终止,通知另一个NameNode应触发故障转移。活动的NameNode选举(HA的第一次启动)
ZooKeeper提供了一种简单的机制来专门选举一个节点为活动的节点。如果当前活动的NameNode崩溃,则另一个节点可能会在ZooKeeper中采取特殊的排他锁,指示它应成为下一个活动的NameNode。
ZKFailoverController(ZKFC)是一个新组件,它是一个ZooKeeper客户端,它监视和管理namenode的状态。运行namenode的每台机器都会运行一个ZKFC,该ZKFC负责以下内容:
运行状况监视
ZKFC使用运行状况检查命令定期ping其本地NameNode。只要NameNode以健康状态及时响应,ZKFC就会认为该节点是健康的。如果节点崩溃,冻结或以其他方式进入不正常状态,则运行状况监视器将其标记为不正常。ZooKeeper会话管理
当本地NameNode运行状况良好时,ZKFC会在ZooKeeper中保持打开的会话。如果本地NameNode处于活动状态,则它还将持有一个特殊的“锁定” znode。该锁使用ZooKeeper对“临时”节点的支持。如果会话到期,则锁定节点将被自动删除。基于ZooKeeper的选举
如果本地NameNode运行状况良好,并且ZKFC看到当前没有其他节点持有锁znode,则它本身将尝试获取该锁。如果成功,则它“赢得了选举”,并负责运行故障转移以使其本地NameNode处于活动状态。故障转移过程类似于上述的手动故障转移:首先,如有必要,将先前的活动节点隔离,然后将本地NameNode转换为活动状态。
ZKFC(是一个进程,和NN在同一个物理节点上)有两只手,分别拽着NN和Zookeeper。(监控NameNode健康状态,并向Zookeeper注册NameNode);集群一启动,2个NN谁是Active?谁又是Standby呢?
2个ZKFC先判断自己的NN是否健康,如果健康,2个ZKFC会向zoopkeeper集群抢着创建一个节点,结果就是只有1个会最终创建成功,从而决定active地位和standby位置。如果ZKFC1抢到了节点,ZKFC2没有抢到,ZKFC2也会监控watch这个节点。如果ZKFC1的Active NN异常退出,ZKFC1最先知道,就访问ZK,ZK就会把曾经创建的节点删掉。删除节点就是一个事件,谁监控这个节点,就会调用callback回调,ZKFC2就会把自己的地位上升到active,但在此之前要先确认ZKFC1的节点是否真的挂掉?这就引入了第三只手的概念。ZKFC2通过ssh远程连接NN1尝试对方降级,判断对方是否挂了。确认真的不健康,才会真的 上升地位之active。所以ZKFC2的步骤是:
1.创建新节点。
2.第三只手把对方降级。
3.把自己升级
那如果NN都没毛病,ZKFC挂掉了呢?Zoopkeeper有一个客户端session机制,集群启动之后,2个ZKFC除了监控自己的NN,还要和Zoopkeeper建立一个tcp长连接,并各自获取自己的session。只要一方的session失效,Zoopkeeper 就会删除该方创建的节点,同时另一方创建节点,上升地位。
qianfeng01: NameNode、DataNode、JournalNode、QuorumPeerMain、ZKFC
qianfeng02: NameNode、DataNode、JournalNode、QuorumPeerMain、ZKFC
qianfeng03: DataNode、JournalNode、QuorumPeerMain
现在,先停止HDFS的进程,修改如下的配置文件吧!
<!-- 注意: 高可用的集群,没有SecondaryNameNode的存在,因此在这个文件中之前存在的SecondaryNameNode的配置需要删除 --> <configuration> <!-- 配置NameNode的逻辑名称 --> <!-- 注意: 后面的很多参数配置都是需要使用到这个名称的 --> <property> <name>dfs.nameservices</name> <value>supercluster</value> </property> <!-- 配置两个NameNode的唯一标识符 --> <property> <name>dfs.ha.namenodes.supercluster</name> <value>nn1,nn2</value> </property> <!-- 针对每一个NameNode,配置自己的RPC通信地址和端口 --> <property> <name>dfs.namenode.rpc-address.supercluster.nn1</name> <value>qianfeng01:9820</value> </property> <property> <name>dfs.namenode.rpc-address.supercluster.nn2</name> <value>qianfeng02:9820</value> </property> <!-- 针对每一个NameNode,配置WebUI的地址和端口 --> <property> <name>dfs.namenode.http-address.supercluster.nn1</name> <value>qianfeng01:9870</value> </property> <property> <name>dfs.namenode.http-address.supercluster.nn2</name> <value>qianfeng02:9870</value> </property> <!-- 定义journalnode进程的数据存储的父路径, 目录在上面已经定义好了的:journalData --> <property> <name>dfs.journalnode.edits.dir</name> <value>/usr/local/hadoop-3.3.1/tmp</value> </property> <!-- 配置journalnode的服务器地址和存储目录(数目为奇数个) --> <!-- 服务器地址使用分号“;”作为分隔符--> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://qianfeng01:8485;qianfeng02:8485;qianfeng03:8485/journalData</value> </property> <!-- 指定客户端连接Active的namenode节点的java类型 --> <property> <name>dfs.client.failover.proxy.provider.supercluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <!--为了保证系统的正确性,在任何时间只有一个NameNode处于Active状态,需要配置一个防护机制 --> <property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <!--为了使该防护选项起作用,它必须能够在不提供密码的情况下SSH到目标节点。因此,还必须配置以下属性--> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/root/.ssh/id_rsa</value> </property> <!-- 免密登陆超时时间,超过此时间未连接上,则登陆失败,此配置可选--> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property> <!-- 支持自动容灾属性 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- 块的副本数量 --> <property> <name>dfs.replication</name> <value>3</value> </property> </configuration>
<configuration> <!--注意:使用到的是在hdfs-site.xml中配置的逻辑名称 --> <property> <name>fs.defaultFS</name> <value>hdfs://supercluster</value> </property> <!-- hdfs的数据保存的路径,被其他属性所依赖的一个基础路径 --> <property> <name>hadoop.tmp.dir</name> <value>/usr/local/hadoop-3.3.1/tmp</value> </property> <!-- ZooKeeper服务的地址 --> <property> <name>ha.zookeeper.quorum</name> <value>qianfeng01:2181,qianfeng02:2181,qianfeng03:2181</value> </property> </configuration>
# 添加两行
export HDFS_JOURNALNODE_USER=root
export HDFS_ZKFC_USER=root
[root@qianfeng01 ~]# cd $HADOOP_HOME/etc
[root@qianfeng01 hadoop]# scp -r hadoop qianfeng02:$PWD
[root@qianfeng01 hadoop]# scp -r hadoop qianfeng03:$PWD
现在,集群已经搭建成为了高可用的集群了。在启动集群之前,我们需要先明确一件事情: 集群现在的状态有两种:
如果你是第一种情况,请跳转到 启动: 普通集群转HA
如果你是第二种情况,请跳转到 启动: 直接搭建HA
个人建议:最好是直接搭建HA这样可以避免不必要的麻烦
# 1. 启动集群的JournalNode服务。 # 注意事项: 如果之前集群还在运行,需要先将其停止!使用命令 stop-dfs.sh [root@qianfeng01 ~]# hdfs --daemon start journalnode [root@qianfeng02 ~]# hdfs --daemon start journalnode [root@qianfeng03 ~]# hdfs --daemon start journalnode # 2. 启动以前节点上的namenode进程 [root@qianfeng01 ~]# hdfs --daemon start namenode # 3. 在新的namenode节点上拉取镜像文件 [root@qianfeng02 ~]# hdfs namenode -bootstrapStandby # 4. 同步日志到journalnode集群上,再启动集群 # 先关namenode [root@qianfeng01 ~]# hdfs --daemon stop namenode # 再同步日志 [root@qianfeng01 ~]# hdfs namenode -initializeSharedEdits # 5. 格式化zkfc # 5.1. 前提QuorumPeerMain服务必须处于开启状态,客户端zkfc才能格式化成功 [root@qianfeng01 ~]# zkServer.sh start [root@qianfeng02 ~]# zkServer.sh start [root@qianfeng03 ~]# zkServer.sh start # 5.2. 选择其中一个namenode节点进行格式化zkfc [root@qianfeng01 ~]# hdfs zkfc -formatZK # 6. 你就可以快乐的开启HA集群进行测试了 [root@qianfeng01 ~]# start-all.sh # 查看NameNode的状态 [root@qianfeng01 ~]# hdfs haadmin -getServiceState nn1 # 注意: 以后开HA集群时,要先开zookeeper服务,再开HDFS。
# 1. 启动三个节点上的journalnode服务 [root@qianfeng01 ~]# hdfs --daemon start journalnode [root@qianfeng02 ~]# hdfs --daemon start journalnode [root@qianfeng03 ~]# hdfs --daemon start journalnode # 2. 格式化namenode # - 先删除所有节点的${hadoop.tmp.dir}/tmp/的数据(可选,这一步表示弃用fsimage.) # - 选择其中一个namenode进行格式化 [root@qianfeng01 ~]# hdfs namenode -format # - 并启动namenode进程 [root@qianfeng01 ~]# hdfs --daemon start namenode # 3. 在另一台namenode上拉取已格式化的那台机器的镜像文件(数据的一致性) [root@qianfeng02 ~]# hdfs namenode -bootstrapStandby # 4. 然后关闭已经启动的namenode [root@qianfeng01 ~]# hdfs --daemon stop namenode # 5. 格式化zkfc # 5.1. 前提QuorumPeerMain服务必须处于开启状态,客户端zkfc才能格式化成功 [root@qianfeng01 ~]# zkServer.sh start [root@qianfeng02 ~]# zkServer.sh start [root@qianfeng03 ~]# zkServer.sh start # 5.2. 选择其中一个namenode节点进行格式化zkfc [root@qianfeng01 ~]# hdfs zkfc -formatZK # 6. 你就可以快乐的开启HA集群进行测试了 [root@qianfeng01 ~]# start-all.sh # 注意:以后开HA集群时,要先开zookeeper服务,再开HDFS。
由于CentOS7的minimal版本缺少容灾切换ActiveNameNode节点时所需要的组件,因此需要手动安装一下:
yum install -y psmisc
- 1
- 首先查看当前活跃的Active节点是谁
- Kill掉活跃节点上的NameNode进程,模拟宕机
- 观察另外一个节点,是否已经变成Active的状态
对于HA集群来说,如果想要使用JavaAPI进行集群的访问,需要在Configuration中设置好每一个属性,也就是再上方配置集群时进行的各种配置。如果觉得麻烦,最简单的方法就是将core-site.xml、hdfs-site.xml拷贝到项目的Resources目录下。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。