ZooKeeper搭建Hadoop高可用集群
在之前安装的Hadoop3.3.6集群中HDFS NameNode 和 YARN ResourceManager 都是单节点,集群不具有高可用性。
HDFS 高可用架构
HDFS 高可用架构主要组件:
Active NameNode 和 Standby NameNode: 两台 NameNode 形成互备,一台处于 Active 状态,为主 NameNode,另外一台处于 Standby 状态,为备 NameNode,只有主 NameNode 才能对外提供读写服务。
主备切换控制器 ZKFailoverController:ZKFailoverController 作为独立的进程运行,对 NameNode 的主备切换进行总体控制。ZKFailoverController 能及时检测到 NameNode 的健康状况,在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换,当然 NameNode 目前也支持不依赖于 Zookeeper 的手动主备切换。
Zookeeper 集群:为主备切换控制器提供主备选举支持。
共享存储系统:共享存储系统是实现 NameNode 的高可用最为关键的部分,共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。主 NameNode 和 NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候,新的主 NameNode 在确认元数据完全同步之后才能继续对外提供服务。
DataNode 节点:除了通过共享存储系统共享 HDFS 的元数据信息之外,主 NameNode 和备 NameNode 还需要共享 HDFS 的数据块和 DataNode 之间的映射关系。DataNode 会同时向主 NameNode 和备 NameNode 上报数据块的位置信息。
基于 QJM 的共享存储系统的数据同步机制

Active NameNode 首先把 EditLog 提交到 JournalNode 集群,然后 Standby NameNode 再从 JournalNode 集群定时同步 EditLog,当 Active NameNode 宕机后, Standby NameNode 在确认元数据完全同步之后就可以对外提供服务。
需要说明的是向 JournalNode 集群写入 EditLog 是遵循 "过半写入则成功" 的策略,所以你至少要有 3 个 JournalNode 节点,当然你也可以继续增加节点数量,但是应该保证节点总数是奇数。同时如果有 2N+1 台 JournalNode,那么根据过半写的原则,最多可以容忍有 N JournalNode 节点挂掉。
NameNode 主备切换
NameNode 实现主备切换的流程下图所示:
-
HealthMonitor 初始化完成之后会启动内部的线程来定时调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法,对 NameNode 的健康状态进行检测。
-
HealthMonitor 如果检测到 NameNode 的健康状态发生变化,会回调 ZKFailoverController 注册的相应方法进行处理。
-
如果 ZKFailoverController 判断需要进行主备切换,会首先使用 ActiveStandbyElector 来进行自动的主备选举。
-
ActiveStandbyElector 与 Zookeeper 进行交互完成自动的主备选举。
-
ActiveStandbyElector 在主备选举完成后,会回调ZKFailoverController 的相应方法来通知当前的 NameNode 成为主 NameNode 或备 NameNode。
-
ZKFailoverController 调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法将 NameNode 转换为 Active 状态或 Standby 状态。
YARN高可用
YARN ResourceManager 的高可用与 HDFS NameNode 的高可用类似,但是 ResourceManager 不像 NameNode ,没有那么多的元数据信息需要维护,所以它的状态信息可以直接写到 Zookeeper 上,并依赖 Zookeeper 来进行主备选举。
高可用集群搭建
集群准备
按照高可用的设计目标:需要保证至少有两个 NameNode (一主一备) 和 两个 ResourceManager (一主一备) ,同时为满足"过半写入则成功"的原则,需要至少要有 3 个 JournalNode 节点 。这里使用三台
主机进行搭建。
所有服务器都安装有 JDK ,linux JDK安装
搭建好 ZooKeeper 集群(hadoop版本为3.3.6建议zookeeper版本3.6.3) ,ZooKeeper3.6.3 集群搭建
1、关闭网络防火墙
bash
# 查看网络防火墙状态
sudo systemctl status firewalld
# 立即停止 firewalld
sudo systemctl stop firewalld
# 防止重启后再次启动,可以禁用防火墙服务
sudo systemctl disable firewalld
2、配置三台机器的节点映射
设置IP映射,在/etc/hosts添加如下内容(每个节点都需修改,)
bash
sudo vi /etc/hosts
bash
192.168.10.100 hadoop100 hadoop100.centos.com
192.168.10.101 hadoop101 hadoop101.centos.com
192.168.10.102 hadoop102 hadoop102.centos.com
编译生效
bash
source /etc/profile
一、安装java 1.8JDK
(每个节点都需要安装,安装的版本和目录保持一致)
二、配置节点间免密登录
1 生成密匙
在每台主机上使用 ssh-keygen 命令生成公钥私钥对:
bash
ssh-keygen
连按三次
2 免密登录
将 hadoop001 的公钥写到本机和远程机器的 ~/ .ssh/authorized_key 文件中:
bash
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop100 依次输入yes,root用户的密码
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop101
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop102
3 验证免密登录
依次输入,可成功跳转后exit回到hadoop100
bash
ssh hadoop100
exit;
ssh hadoop101
exit;
ssh hadoop102
exit;
4、报错处理
bash
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
修改SSH 配置允许公钥认证
bash
vi /etc/ssh/sshd_config
#将PasswordAuthentication设置成为yes(一般在文件的末尾几行):
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication yes
#vi中使用查找功能快速定位
#进入普通模式(如果您已经在插入模式中),按 Esc 键。
#输入 / 后跟您想要搜索的词或短语。
#按 Enter 键进行搜索。
#n 键:移动到下一个匹配项。
#N 键:移动到上一个匹配项。
#例如,要搜索单词 "PermitRootLogin ",您将输入:/PermitRootLogin
#修改完成后重启ssh服务
systemctl restart sshd.service
# 查看ssh服务状态
sudo systemctl status sshd
三、 Hadoop安装
1、下载hadoop
2、解压缩hadoop-3.3.6.tar.gz 文件
bash
tar -zxvf /opt/soft/hadoop-3.3.6.tar.gz -C /opt/app
-z:使用 gzip 解压缩算法。这是用来解压 .gz 结尾的文件的
-x:提取归档文件的内容
-f:指定要处理的归档文件的名称,后面跟着文件名 hadoop-3.3.6.tar.gz
-C:指定解压后的文件应放置在哪个目录下。在这里,目录是 /opt/app
解压后即可,看到/opt/app/hadoop-3.3.6文件夹
3、配置Hadoop环境变量
vim /etc/profile.d/my_env.sh配置JAVA和Hadoop环境变量(每台机器都需要配置)
bash
vim /etc/profile.d/my_env.sh
bash
export JAVA_HOME=/usr/java/jdk1.8.0_281-amd64
export HADOOP_HOME=/opt/app/hadoop-3.3.6
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
修改后执行【source /etc/profile】命令使其生效
4、配置Hadoop的配置文件
bash
# 进入修改目录
cd /opt/app/hadoop-3.3.6/etc/hadoop/
** (1)、修改 hadoop-env.sh**
bash
export JAVA_HOME=/usr/java/jdk1.8.0_431
** (2)、修改配置文件yarn-env.sh 在文件末尾加上**
bash
export JAVA_HOME=/usr/java/jdk1.8.0_431
(3) 修改配置文件 workers
指定dataNode工作节点。
bash
#删除原有 localhost,添加如下内容
#【不能有空格和空行】
hadoop100
hadoop101
hadoop102
注: 修改下面文件中存在,配置放入其中,且只保留一组的
(4) 修改配置文件core-site.xml
bash
<configuration>
<!-- 设置默认使用的文件系统 Hadoop支持file、HDFS、GFS、ali|Amazon云等文件系统 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop100:8020</value>
</property>
<!-- 指定Hadoop的临时文件的本地存放路径 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/var/log/hadoop/tmp</value>
</property>
<property>
<!-- ZooKeeper 集群的地址 -->
<name>ha.zookeeper.quorum</name>
<value>hadoop101:2181,hadoop101:2182,hadoop101:2183</value>
</property>
<property>
<!-- ZKFC 连接到 ZooKeeper 超时时长 -->
<name>ha.zookeeper.session-timeout.ms</name>
<value>10000</value>
</property>
<!-- 设置HDFS web UI用户身份 -->
<property>
<name>hadoop.http.staticuser.user</name>
<value>root</value>
</property>
<!-- 配置该root(superUser)允许通过代理访问的主机节点 -->
<property>
<name>hadoop.proxyuser.root.hosts</name>
<value>*</value>
</property>
<!-- 配置该root(superUser)允许通过代理用户所属组 -->
<property>
<name>hadoop.proxyuser.root.groups</name>
<value>*</value>
</property>
<!-- 配置该root(superUser)允许通过代理的用户-->
<property>
<name>hadoop.proxyuser.root.groups</name>
<value>*</value>
</property>
</configuration>
(5) 修改配置文件hdfs-site.xml
bash
<configuration>
<!-- 指定HDFS的数据分块的备份数量 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 指定HDFS的主节点的元数据文件的本地存放路径 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>/opt/app/hadoop-3.3.6/name</value>
</property>
<!-- 指定HDFS的数据节点的数据文件的本地存放路径 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>/opt/app/hadoop-3.3.6/data</value>
</property>
<property>
<!-- 集群服务的逻辑名称 -->
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<property>
<!-- NameNode ID 列表-->
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<property>
<!-- nn1 的 RPC 通信地址 -->
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>hadoop100:8020</value>
</property>
<property>
<!-- nn2 的 RPC 通信地址 -->
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>hadoop101:8020</value>
</property>
<property>
<!-- nn1 的 http 通信地址 -->
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>hadoop100:50070</value>
</property>
<property>
<!-- nn2 的 http 通信地址 -->
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>hadoop101:50070</value>
</property>
<property>
<!-- NameNode 元数据在 JournalNode 上的共享存储目录 -->
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://hadoop100:8485;hadoop101:8485;hadoop102:8485/mycluster</value>
</property>
<property>
<!-- Journal Edit Files 的存储目录 -->
<name>dfs.journalnode.edits.dir</name>
<value>/home/hadoop/journalnode/data</value>
</property>
<property>
<!-- 配置隔离机制,确保在任何给定时间只有一个 NameNode 处于活动状态 -->
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<!-- 使用 sshfence 机制时需要 ssh 免密登录 -->
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<property>
<!-- SSH 超时时间 -->
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
<property>
<!-- 访问代理类,用于确定当前处于 Active 状态的 NameNode -->
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<!-- 开启故障自动转移 -->
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
</configuration>
(6) 修改配置文件yarn-site.xml
bash
<configuration>
<property>
<!--配置 NodeManager 上运行的附属服务。需要配置成 mapreduce_shuffle 后才可以在
Yarn 上运行 MapReduce 程序。-->
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<!-- 是否启用日志聚合 (可选) -->
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<!-- 聚合日志的保存时间 (可选) -->
<name>yarn.log-aggregation.retain-seconds</name>
<value>86400</value>
</property>
<property>
<!-- 启用 RM HA -->
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<property>
<!-- RM 集群标识 -->
<name>yarn.resourcemanager.cluster-id</name>
<value>my-yarn-cluster</value>
</property>
<property>
<!-- RM 的逻辑 ID 列表 -->
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<property>
<!-- RM1 的服务地址 -->
<name>yarn.resourcemanager.hostname.rm1</name>
<value>hadoop100</value>
</property>
<property>
<!-- RM2 的服务地址 -->
<name>yarn.resourcemanager.hostname.rm2</name>
<value>hadoop101</value>
</property>
<property>
<!-- RM1 Web 应用程序的地址 -->
<name>yarn.resourcemanager.webapp.address.rm1</name>
<value>hadoop100:8088</value>
</property>
<property>
<!-- RM2 Web 应用程序的地址 -->
<name>yarn.resourcemanager.webapp.address.rm2</name>
<value>hadoop101:8088</value>
</property>
<property>
<!-- ZooKeeper 集群的地址 -->
<name>yarn.resourcemanager.zk-address</name>
<value>hadoop101:2181,hadoop101:2182,hadoop101:2183</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.resourcemanager.zk-state-store.parent-path</name>
<value>/rmstore</value>
</property>
</configuration>
(7) 修改配置文件mapred-site.xml
bash
<configuration>
<!-- 指定MapReduce所使用的外部管理框架,这里使用Hadoop 3.3.6自带的YARN资源管理器 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>
5、修改hadoop启动命令
bash
# 进入到启动命令所在位置hadoop安装路径下的sbin目录下
cd /opt/app/hadoop-3.3.6/sbin
修改start-dfs.sh,stop-dfs.sh,在文件顶部添加
bash
HDFS_ZKFC_USER=root
HDFS_JOURNALNODE_USER=root
HDFS_NAMENODE_USER=root
HDFS_SECONDARYNAMENODE_USER=root
HDFS_DATANODE_USER=root
HDFS_DATANODE_SECURE_USER=root
#HADOOP_SECURE_DN_USER=root
修改 start-yarn.sh,stop-yarn.sh,在文件顶部添加
bash
YARN_RESOURCEMANAGER_USER=root
HADOOP_SECURE_DN_USER=yarn
YARN_NODEMANAGER_USER=root
6 、分发程序
拷贝 Hadoop 安装文件到集群节点
bash
scp -qr /opt/app/hadoop-3.3.6 hadoop101:/opt/app
scp -r /opt/app/hadoop-3.3.6 hadoop102:/opt/app
7、 初始化
1 启动ZooKeeper
分别到服务器上启动 ZooKeeper 服务:
bash
zkServer.sh start

2 启动Journalnode
分别到三台服务器的的 ${HADOOP_HOME}/sbin 目录下,启动 journalnode 进程:
bash
hadoop-daemon.sh start journalnode
3 初始化NameNode
在 hadop100 上执行 NameNode 初始化命令:
bash
hdfs namenode -format
执行初始化命令后,需要将 NameNode 元数据目录的内容,复制到其他未格式化的 NameNode 上。
元数据存储目录就是我们在 hdfs-site.xml 中使用 dfs.namenode.name.dir 属性指定的目录。这里我们需要将其复制到 hadoop101 上:
bash
scp -r /opt/app/hadoop-3.3.6/name hadoop101:/opt/app/hadoop-3.3.6/
4 初始化HA状态
在任意一台 NameNode 上使用以下命令来初始化 ZooKeeper 中的 HA 状态:
bash
hdfs zkfc -formatZK
5 启动HDFS
进入到 hadoop100 的 ${HADOOP_HOME}/sbin 目录下,启动 HDFS。此时 hadoop100 和 hadoop101 上的 NameNode 服务,和三台服务器上的 DataNode 服务都会被启动:
bash
start-dfs.sh
6 启动YARN
进入到 hadoop100 的 ${HADOOP_HOME}/sbin 目录下,启动 YARN。此时 hadoop101 上的 ResourceManager 服务,和两台服务器上的 NodeManager 服务都会被启动:
bash
start-yarn.sh
启动报错
查看日志
进入 /opt/app/hadoop-3.3.6/logs 目录,查看对应组件的启动日志
8、查看集群
可以看到每台机器上启动的应用和我们的配置文件地址是对应的
查看Web UI
webui地址 (http://hadoop100:50070/)
yarn地址 http://hadoop101:8088/
集群的二次启动
集群初次启动涉及到一些必要初始化操作,所以过程略显繁琐。但是集群一旦搭建好后,想要再次启用它是比较方便的,步骤如下(首选需要确保 ZooKeeper 集群已经启动):
先停止服务
bash
stop-dfs.sh
stop-yarn.sh
使用jps查看,确保服务全部关闭
在 hadoop100 启动 HDFS ,此时会启动所有与 HDFS 高可用相关的服务,包括 NameNode,DataNode 和 JournalNode:
bash
start-dfs.sh
然后再启动 YARN
bash
start-yarn.sh