3.16 HA高可用
单点故障(英语:single point of failure,缩写SPOF)是指系统中某一点一旦失效,就会让整个系统无法运作,换句话说,单点故障即会整体故障。
高可用性(英语:high availability,缩写为 HA),IT术语,指系统无中断地执行其功能的能力,代表系统的可用性程度。是进行系统设计时的准则之一。高可用性系统意味着系统服务可以更长时间运行,通常通过提高系统的容错能力来实现。
高可用性或者高可靠度的系统不会希望有单点故障造成整体故障的情形。一般可以透过冗余的方式增加多个相同机能的部件,只要这些部件没有同时失效,系统(或至少部分系统)仍可运作,这会让可靠度提高。
高可用如何实现?
解决单点故障,实现系统服务高可用的核心并不是让故障永不发生,而是让故障的发生对业务的影响降到最小。因为软硬件故障是难以避免的问题。
当下企业中成熟的做法就是给单点故障的位置设置备份,形成主备架构。通俗描述就是当主挂掉,备份顶上,短暂的中断之后继续提供服务。
常见的是一主一备架构,当然也可以一主多备。备份越多,容错能力越强,与此同时,冗余也越大,浪费资源。
Active:主角色。活跃的角色,代表正在对外提供服务的角色服务。任意时间有且只有一个active对外提供服务。
Standby:备份角色。需要和主角色保持数据、状态同步,并且时刻准备切换成主角色(当主角色挂掉或者出现故障时),对外提供服务,保持服务的可用性。
什么是脑裂问题?
脑裂(split-brain)是指“大脑分裂”,本是医学名词。在 HA 集群中,脑裂指的是当联系主备节点的"心跳线"断开时(即两个节点断开联系时),本来为一个整体、动作协调的 HA 系统,就分裂成为两个独立的节点。由于相互失去了联系,主备节点之间像"裂脑人"一样,使得整个集群处于混乱状态。脑裂的严重后果:
1)集群无主:都认为对方是状态好的,自己是备份角色,后果是无服务;
2)集群多主:都认为对方是故障的,自己是主角色。相互争抢共享资源,结果会导致系统混乱,数据损坏。此外对于客户端访问也是一头雾水,找谁呢?
避免脑裂问题的核心是:保持任意时刻系统有且只有一个主角色提供服务。
HA实现数据同步?
主备切换保证服务持续可用性的前提是主备节点之间的状态、数据是一致的,或者说准一致的。如果说备用的节点和主节点之间的数据差距过大,即使完成了主备切换的动作,那也是没有意义的。
数据同步常见做法是:通过日志重演操作记录。主角色正常提供服务,发生的事务性操作通过日志记录,备用角色读取日志重演操作。
Hdfs 单点故障问题?
在 Hadoop 2.0.0 之前,NameNode 是 HDFS 集群中的单点故障(SPOF)。每个群集只有一个 NameNode,如果该计算机或进程不可用,则整个群集在整个NameNode重新启动或在另一台计算机上启动之前将不可用。
NameNode 的单点故障从两个方面影响了 HDFS 群集的总可用性:
如果发生意外事件(例如机器崩溃),则在重新启动 NameNode 之前,群集将不可用。
计划内的维护事件,例如 NameNode 计算机上的软件或硬件升级,将导致群集停机时间的延长。
HDFS高可用性解决方案
:在同一群集中运行两个(从 3.0.0 起,超过两个)冗余 NameNode。这样可以在机器崩溃的情况下快速故障转移到新的 NameNode,或者出于计划维护的目的由管理员发起的正常故障转移。
3.16.1 QJM
QJM 全称 Quorum Journal Manager,由 Cloudera 公司提出,是 Hadoop 官方推荐的 HDFS HA 解决方案之一。
QJM 中,使用 zookeeper 中 ZKFC 来实现主备切换;使用 Journal Node(JN)集群实现 edits log 的共享以达到数据同步的目的。
ZKFailoverController(zkfc)
Apache ZooKeeper 是一款高可用分布式协调服务软件,用于维护少量的协调数据。 Zookeeper 的下列特性功能参与了 HDFS 的 HA 解决方案中:
临时 znode
如果一个 znode 节点是临时的,那么该 znode 的生命周期将和创建它的客户端的 session 绑定。客户端断开连接 session 结束,znode 将会被自动删除。
Path 路径唯一性
zookeeper 中维持了一份类似目录树的数据结构。每个节点称之为 Znode。Znode 具有唯一性,不会重名。也可以理解为排他性。
监听机制
客户端可以针对 znode 上发生的事件设置监听,当事件发生触发条件,zk 服务会把事件通知给设置监听的客户端。
ZKFailoverController(ZKFC)是一个新组件,它是一个 ZooKeeper 客户端。运行 NameNode 的每台计算机也都运行 ZKFC,ZKFC 的主要职责:
监视和管理 NameNode 健康状态
ZKFC 通过命令定期 ping 本地负责监视的 NameNode 节点。
维持和 ZooKeeper 集群联系
如果本地 NameNode 运行状况良好,并且 ZKFC 看到当前没有其他节点持有锁 znode,它将自己尝试获取该锁。如果成功,则表明它“赢得了选举”,并负责运行故障转移以使其本地 NameNode 处于 Active 状态。如果已经有其他节点持有锁,zkfc 选举失败,则会对该节点注册监听,等待下次继续选举。
Fencing 隔离机制
故障转移过程也就是俗称的主备角色切换的过程,切换过程中最怕的就是脑裂的发送。因此需要 Fencing 机制来避免,将先前的 Active 节点隔离,然后将本地 NameNode 转换为 Active 状态。
Hadoop 公共库中对外提供了两种 fenching 实现,分别是 sshfence 和 shellfence(缺省实现),其中 sshfence 是指通过 ssh 登陆目标节点上,使用命令 fuser 将进程杀死(通过 tcp 端口号定位进程 pid,该方法比 jps 命令更准确),shellfence 是指执行一个用户事先定义的 shell 命令(脚本)完成隔离。
主备数据同步问题解决
Journal Node(JN)集群是轻量级分布式系统,主要用于高速读写数据、存储数据。通常使用 2N+1 台 JournalNode 存储共享 Edits Log(编辑日志)。
任何修改操作在 Active NN上执行时,JournalNode 进程同时也会记录 edits log 到至少半数
以上的 JN 中,这时 Standby NN 监测到 JN 里面的同步 log 发生变化了会读取JN里面的 edits log,然后重演操作记录同步到自己的目录镜像树里面,
当发生故障 Active NN 挂掉后,Standby NN 会在它成为 Active NN 前,读取所有的 JN 里面的修改日志,这样就能高可靠的保证与挂掉的 NN 的目录镜像树一致,然后无缝的接替它的职责,维护来自客户端请求,从而达到一个高可用的目的。
3.16.2 HA 搭建
如果发生意外事件(例如机器崩溃),则在重新启动 NameNode 之前,群集将不可用。
计划内的维护事件,例如 NameNode 计算机上的软件或硬件升级,将导致群集停机时间的延长。
HDFS高可用性解决方案
:在同一群集中运行两个(从 3.0.0 起,超过两个)冗余 NameNode。这样可以在机器崩溃的情况下快速故障转移到新的 NameNode,或者出于计划维护的目的由管理员发起的正常故障转移。临时 znode
如果一个 znode 节点是临时的,那么该 znode 的生命周期将和创建它的客户端的 session 绑定。客户端断开连接 session 结束,znode 将会被自动删除。
Path 路径唯一性
zookeeper 中维持了一份类似目录树的数据结构。每个节点称之为 Znode。Znode 具有唯一性,不会重名。也可以理解为排他性。
监听机制
客户端可以针对 znode 上发生的事件设置监听,当事件发生触发条件,zk 服务会把事件通知给设置监听的客户端。
监视和管理 NameNode 健康状态
ZKFC 通过命令定期 ping 本地负责监视的 NameNode 节点。
维持和 ZooKeeper 集群联系
如果本地 NameNode 运行状况良好,并且 ZKFC 看到当前没有其他节点持有锁 znode,它将自己尝试获取该锁。如果成功,则表明它“赢得了选举”,并负责运行故障转移以使其本地 NameNode 处于 Active 状态。如果已经有其他节点持有锁,zkfc 选举失败,则会对该节点注册监听,等待下次继续选举。
至少半数
以上的 JN 中,这时 Standby NN 监测到 JN 里面的同步 log 发生变化了会读取JN里面的 edits log,然后重演操作记录同步到自己的目录镜像树里面,节点进程 | Master(主节点) | Slave(从节点) | Slave1(从节点) | ... |
---|---|---|---|---|
NameNode | √ | √ | × | ... |
DataNode | √ | √ | √ | ... |
zkfc | √ | √ | × | ... |
QuorumPeerMain(zk) | √ | √ | √ | ... |
journal node | √ | √ | √ | ... |
Resourcemanager | √ | √ | × | ... |
Nodemanager | √ | √ | √ | ... |
1、安装好 zookeeper 集群,启动 zookeeper 集群
zkServer.sh start
2、在当前文档 2.4 的基础之上修改配置文件
core-site.xml内容如下:
<configuration>
<!--Hdfs集群的入口,HA集群名称,该值要和hdfs-site.xml中的配置保持一致-->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<!-- 配置HDFS网页登录使用的静态用户为 -->
<property>
<name>hadoop.http.staticuser.user</name>
<value>briup</value>
</property>
<!-- 配置该briup(superUser)允许通过代理访问的主机节点 -->
<property>
<name>hadoop.proxyuser.briup.hosts</name>
<value>*</value>
</property>
<!-- 配置该briup(superUser)允许通过代理用户所属组 -->
<property>
<name>hadoop.proxyuser.briup.groups</name>
<value>*</value>
</property>
<!-- 配置该briup(superUser)允许通过代理的用户-->
<property>
<name>hadoop.proxyuser.briup.users</name>
<value>*</value>
</property>
<!-- ZooKeeper集群的地址和端口-->
<property>
<name>ha.zookeeper.quorum</name>
<value>master:2181,slave:2181,slave1:2181</value>
</property>
</configuration>
hdfs-site.xml内容如下:
<configuration>
<!--集群的名字-->
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<!-- mycluster下面有两个NameNode,分别是nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<!-- nn1的RPC通信地址 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>master:9000</value>
</property>
<!-- nn1的http通信地址 -->
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>master:9870</value>
</property>
<!-- nn2的RPC通信地址 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>slave:9000</value>
</property>
<!-- nn2的http通信地址 -->
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>slave:9870</value>
</property>
<!-- 指定NameNode的edits元数据在JournalNode上的存放位置 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://master:8485;slave:8485;slave1:8485/mycluster</value>
</property>
<!-- 指定JournalNode在本地磁盘存放数据的位置 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/briup/software/data/hadoop/journaldata</value>
</property>
<!-- 开启NameNode失败自动切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- 指定该集群出故障时,哪个实现类负责执行故障切换 -->
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 配置隔离机制方法-->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<!-- 使用sshfence隔离机制时需要ssh免登陆 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/briup/.ssh/id_rsa</value>
</property>
<!-- 配置sshfence隔离机制超时时间 -->
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
<!--数据块备份数,默认备份是3,备份数不能大于从节点的个数-->
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<!--主节点存储Hdfs文件系统中文件及目录的元数据-->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/home/briup/software/data/hadoop/hdfs/nn</value>
</property>
<!--主节点存储检查相关日志数据-->
<property>
<name>dfs.namenode.checkpoint.dir</name>
<value>file:/home/briup/software/data/hadoop/hdfs/snn</value>
</property>
<property>
<name>dfs.namenode.checkpoint.edits.dir</name>
<value>file:/home/briup/software/data/hadoop/hdfs/snn</value>
</property>
<!--从节点存放hdfs文件系统中的文件的数据-->
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/home/briup/software/data/hadoop/hdfs/dn</value>
</property>
</configuration>
yarn-site.xml内容如下:
<configuration>
<!-- 是否启用日志聚合 (可选) -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<!-- 聚合日志的保存时间 (可选) -->
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>86400</value>
</property>
<!-- 设置日志聚合服务器 -->
<property>
<name>yarn.log.server.url</name>
<value>http://master:19888/jobhistory/logs</value>
</property>
<!-- 启用 RM HA -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- RM 集群标识 -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>Mycluster</value>
</property>
<!-- RM 的逻辑 ID 列表 -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- RM1 的服务地址 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>master</value>
</property>
<!-- RM2 的服务地址 -->
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>slave</value>
</property>
<!-- RM1 Web 应用程序的地址 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm1</name>
<value>master:8088</value>
</property>
<!-- RM2 Web 应用程序的地址 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm2</name>
<value>slave:8088</value>
</property>
<!-- ZooKeeper 集群的地址 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>master:2181,slave:2181,slave1:2181</value>
</property>
<!-- 启用自动恢复 -->
<property>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>true</value>
</property>
<!-- 用于进行持久化存储的类 -->
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
</property>
<!-- 容器虚拟内存与物理内存之间的比率-->
<property>
<name>yarn.nodemanager.vmem-pmem-ratio</name>
<value>3</value>
</property>
<!-- 每个容器请求的最小内存资源(以MB为单位)-->
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>512</value>
</property>
<!-- 每个容器请求的最大内存资源(以MB为单位)-->
<property>
<name>yarn.scheduler.maximum-allocation-mb</name>
<value>4096</value>
</property>
<!--MapReduce程序运行Map容器和Reduce容器数据交换方式-->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!--yarn集群运行用户程序的时候,运行程序暂时存放的目录-->
<property>
<name>yarn.nodemanager.local-dirs</name>
<value>/home/briup/software/data/hadoop/yarn/nm</value>
</property>
<property>
<name>yarn.nodemanager.log-dirs</name>
<value>/home/briup/software/data/hadoop/yarn/logs</value>
</property>
</configuration>
yarn-site.xml内容如下:
<configuration>
<!--Mapreduce程序运行的资源管理平台-->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<!--设置历史服务器地址-->
<property>
<name>mapreduce.jobhistory.address</name>
<value>master:10020</value>
</property>
<!--设置历史服务器web地址-->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>master:19888</value>
</property>
<!--Mapreduce框架程序运行环境目录-->
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<!--Map容器启动的内存-->
<property>
<name>mapreduce.map.memory.mb</name>
<value>4096</value>
</property>
<!--Reduce容器启动的内存-->
<property>
<name>mapreduce.reduce.memory.mb</name>
<value>4096</value>
</property>
</configuration>
一台机器修改,同步文件到其他节点
3、主从节点针对其他计算机配置免密
主节点:
ssh-keygen -t rsa -P '' -m PEM -f ~/.ssh/id_rsa
ssh-copy-id -i master
ssh-copy-id -i slave
ssh-copy-id -i slave1
备份节点:
ssh-keygen -t rsa -P '' -m PEM -f ~/.ssh/id_rsa
ssh-copy-id -i master
ssh-copy-id -i slave
ssh-copy-id -i slave1
4、集群中每台机器创建 JN 数据存储目录
mkdir /home/briup/software/data/hadoop/journaldata
5、集群中每台机器启动 JN
hadoop-daemon.sh start journalnode6、master启动主节点
hadoop-daemon.sh start namenode
7、slave 启动 namenode 元数据同步
hdfs namenode -bootstrapStandby8、格式化 zkfc
在哪台机器上执行,哪台机器就将成为第一次的 Active NN
hdfs zkfc -formatZK
9、启动整个集群
start-dfs.shmaster 监控界面:
slave 监控界面:
10、上传文件
hdfs dfs -mkdir -p /user/briup
hdfs dfs -put /etc/passwd /user/briup
10、kill 主节点,查看备份节点是否生效
kill -9 主机点进程编号
主节点监控界面:
备份节点监控界面:
11、启动 Yarn 集群
start-yarn.sh
节点监控界面:
12、kill 掉生效的 resourcemanager,查看备份 resourcemanager 是否生效
kill -9
监控界面:
13、开启日志服务器
mr-jobhistory-daemon.sh start historyserver
14、手动将 slave 的 namenode 切换为 Active 状态
开启日志服务器可选
hdfs haadmin -transitionToActive slave
15、手动将 slave 的 resourcemanager 切换为 Active 状态
yarn rmadmin -getServiceState slave