title: Hadoop HDFS之HA集群部署 #标题tags: hadoop #标签
date: 2021-01-01
categories: Hadoop # 分类
本文用于记录下HDFS、YARN高可用性(HA)功能以及如何使用Quorum Journal Manager(QJM)功能配置和管理HA HDFS群集。
所谓HA就是保证7*24小时不中断服务,实现高可用的最关键的策略就是消除单点故障,HA严格来说应该分成各个组件的HA机制:HDFS的HA和YARN的HA。
在Hadoop 2.0.0之前,NameNode是HDFS集群中的单点故障(SPOF)。每个群集只有一个NameNode,如果该服务不可用,则整个群集在整个NameNode重新启动或在另一台计算机上启动之前将不可用。
在典型的HA群集中,将两个单独的节点配置为NameNode。在任何时间点,其中一个NameNode处于Active状态,而另一个处于Standby状态。Active NameNode负责群集中的所有客户端操作,而Standby只是充当从属,并保持足够的状态以在必要时提供快速故障转移。
为了使Standby节点保持其状态与Active节点同步,两个节点都与称为“ JournalNodes”(JN)的一组单独的守护程序进行通信。当Active节点执行任何名称空间修改时,它会持久地将修改记录记录到这些JN的中。Standby节点能够从JN读取编辑内容,并不断监视它们以查看编辑日志的更改。当“备用节点”看到编辑内容时,会将其应用到自己的名称空间。发生故障转移时,Standby服务器将确保在将自身升级为活动状态之前,已从JournalNode读取所有编辑内容。这样可确保在发生故障转移之前,名称空间状态已完全同步。
为了提供快速故障转移,Standby节点还必须具有有关集群中块位置的最新信息。为了实现这一点,DataNode被配置了两个NameNode的位置,并向两者发送块位置信息和心跳。
对于HA群集的正确操作至关重要,一次只能有一个NameNode处于活动状态。否则会产生脑裂现象,从而有数据丢失或其他不正确结果的风险。为了确保此属性并防止所谓的“裂脑情况”,JournalNode将仅一次允许一个NameNode成为“作者”。在故障转移期间,将变为Active状态的NameNode将仅承担写入JournalNodes的角色,这将有效地防止另一个NameNode继续处于活动状态,从而使新的Active节点可以安全地进行故障转移。
参考:
环境准备
为了部署高可用性群集,应该满足以下条件:
- NameNode节点:运行Active NameName和Standby NameNode的服务器应具有彼此等效的硬件。
- JournalNode节点:JournalNode守护程序相对较轻,因此可以合理地将JournalNode节点与其他Hadoop守护程序(例如NameNode,JobTracker或YARN ResourceManager)进行复用。注意:必须至少有3个JournalNode守护程序,因为必须将编辑日志修改写入大多数JN。这样可以避免JournalNode节点的单点故障,你也可以允许三个以上的JournalNode节点,但节点数必须为奇数,如3、5、7… ,当与N个JournalNode一起运行时,系统最多可以容忍(N-1)/ 2个故障,并继续正常运行。
在高可用性群集中,Standby节点还执行SecondaryNameNode的工作(编辑日志合并),因此不必在高可用性群集中运行SecondaryNameNode节点。
| OS | hostname | IP | roles |
|---|---|---|---|
| Centos 7.5 | hadoop01 | 192.168.20.2 | Zookeeper、DataNode、NodeManager、NameNode、JournalNode |
| Centos 7.5 | hadoop02 | 192.168.20.3 | Zookeeper、 DataNode、NodeManager、NameNode、JournalNode |
| Centos 7.5 | hadoop03 | 192.168.20.4 | Zookeeper、DataNode、NodeManager、ResourceManager、JournalNode |
| Centos 7.5 | hadoop04 | 192.168.20.5 | DataNode、NodeManager、ResourceManager、historyserver |
接下的所有操作,只要没特别说明,则只需在 hadoop01 这一个机器上执行即可。
环境配置
在正式进行集群配置前,需要先对系统进行初步配置。
配置ssh免密登录
切记,先配置主机解析记录,再配置免密登录。
# 配置主机名解析记录$ cat >> /etc/hosts <<EOF192.168.20.2 hadoop01192.168.20.3 hadoop02192.168.20.4 hadoop03192.168.20.5 hadoop04EOF# 修改后的hosts文件发送至其他节点$ for i in $(seq 1 4);do rsync -az /etc/hosts hadoop0${i}:/etc/;done# 下面是配置免密登录,需要在resourcemanager和NameNode两个角色的机器上执行# 如果可以,最好在集群中所有节点都配置免密登录到集群中的其他节点# 为了方便后续群起集群,我们必须在resourcemanager和NameNode两个角色的机器上配置免密登录到其他节点$ ssh-keygen -t rsa # 执行后一路回车$ for i in $(seq 1 4);do ssh-copy-id hadoop0${i} ;done # 执行后根据提示,该输入yes输入yes,该输入密码就输入密码
一定要确保所有NameNode和resourcemanager节点可以免密登录到其他节点,否则后续自动故障转移会出问题。
修改系统限制
$ mv /etc/security/limits.conf{,.bak}cat > /etc/security/limits.conf << EOF* - nofile 655360* - memlock unlimited* - stack 655360* - nproc unlimitedEOFcat > /etc/sysctl.conf << EOFkernel.sysrq = 0kernel.core_uses_pid = 1fs.file-max=655360kernel.msgmnb = 65536kernel.msgmax = 65536kernel.shmmax = 68719476736kernel.shmall = 4294967296kernel.pid_max = 655360net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 0net.ipv4.tcp_max_tw_buckets = 10000net.ipv4.tcp_fin_timeout = 30net.ipv4.tcp_timestamps = 0net.ipv4.tcp_sack = 1net.ipv4.tcp_window_scaling = 1net.ipv4.tcp_ecn = 0net.ipv4.tcp_keepalive_time = 600net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 3net.ipv4.tcp_max_orphans = 655360net.ipv4.tcp_max_syn_backlog = 262144net.ipv4.tcp_mem = 65536 131072 262144net.ipv4.udp_mem = 65536 131072 262144net.ipv4.tcp_rmem = 4096 87380 16777216net.ipv4.tcp_wmem = 4096 16384 16777216net.ipv4.ip_local_port_range = 1024 65535net.ipv4.route.gc_timeout = 100# 禁止icmp重定向报文net.ipv4.conf.all.accept_redirects = 0# 禁止icmp源路由net.ipv4.conf.all.accept_source_route = 0net.core.somaxconn = 65535net.core.rmem_default = 8388608net.core.wmem_default = 8388608net.core.rmem_max = 16777216net.core.wmem_max = 16777216net.core.netdev_max_backlog = 262144vm.swappiness = 10vm.overcommit_memory = 1vm.max_map_count = 262144EOFsysctl -p#将修改后的文件发送至其他节点$ for i in $(seq 1 4);do rsync -az /etc/security/limits.conf hadoop0${i}:/etc/security/;done$ for i in $(seq 1 4);do rsync -az /etc/sysctl.conf hadoop0${i}:/etc/;done# 其余节点需执行下面命令刷新内核参数$ sysctl -p
配置jdk
自行去oracle官网下载java包 jdk-8u261-linux-x64.tar.gz ,然后上传至服务器。
配置jdk环境的操作,是需要在所有节点进行的。
$ mkdir /apps/usr -p && systemctl stop firewalld && systemctl disable firewalld && setenforce 0$ tar zxf jdk-8u261-linux-x64.tar.gz -C /apps/usr/$ ln -sf /apps/usr/jdk1.8.0_261 /apps/usr/jdk$ cat >> /etc/profile << EOFexport JAVA_HOME=/apps/usr/jdkexport CLASSPATH=\$JAVA_HOME/libexport PATH=\$JAVA_HOME/bin:\$PATHEOF$ source /etc/profile$ java -version # 查看版本信息java version "1.8.0_261"Java(TM) SE Runtime Environment (build 1.8.0_261-b12)Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)
配置ntp时间同步
在hadoop01配置时间服务器,其他客户端制定定时任务即可。
$ yum -y install ntp$ vim /etc/ntp.conf # 修改配置文件# 找到(17行左右):#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap# 改为(网段是你自己的网段,表示允许哪个网段的客户端来同步时间):restrict 192.168.20.0 mask 255.255.255.0 nomodify notrap# 找到server 0.centos.pool.ntp.org iburstserver 1.centos.pool.ntp.org iburstserver 2.centos.pool.ntp.org iburstserver 3.centos.pool.ntp.org iburst# 将其注释掉,表示不使用互联网上的时间同步服务器#server 0.centos.pool.ntp.org iburst#server 1.centos.pool.ntp.org iburst#server 2.centos.pool.ntp.org iburst#server 3.centos.pool.ntp.org iburst# 末尾追加server 127.127.1.0fudge 127.127.1.0 stratum 5# 在上级时钟源失效时,NTP会使用127.127.1.0的本地时钟,将local时间作为ntp服务器时间提供给ntp客户端。# NTP把本地主机的时钟也看作外部时钟源来处理,分配的地址是127.127.1.0# 让硬件时间和系统时间一起同步$ echo 'SYNC_HWCLOCK=yes' >> /etc/sysconfig/ntpd# 重启ntp服务器生效$ systemctl restart ntpd && systemctl enable ntpd# 配置其他客户端定时同步时间(哪些机器要同步上面时间服务器的时间,就进行以下配置)$ yum -y install ntp$ crontab -e # 写入以下定时任务*/5 * * * * /usr/sbin/ntpdate 192.168.20.2 &> /dev/null
部署hadoop
没有特别说明的,以下操作在hadoop01上进行即可。
$ wget https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/current2/hadoop-2.10.1.tar.gz$ tar zxf hadoop-2.10.1.tar.gz -C /apps/usr/# 配置hadoop环境变量$ cat >> /etc/profile << EOFexport HADOOP_HOME=/apps/usr/hadoop-2.10.1export PATH=\${HADOOP_HOME}/bin:\${HADOOP_HOME}/sbin:\${PATH}EOF$ source /etc/profile$ cd /apps/usr/hadoop-2.10.1/# 修改hadoop的jdk环境变量$ sed -i 's/export JAVA_HOME=.*/export JAVA_HOME=\/apps\/usr\/jdk/g' etc/hadoop/hadoop-env.sh$ vim etc/hadoop/hdfs-site.xml # 修改此文件<configuration> # 找到此行,写入以下配置<!--默认副本数为3--><property><name>dfs.replication</name><value>3</value></property><!--自定义hdfs集群名称--><property><name>dfs.nameservices</name><value>mycluster</value></property><!--自定义参加集群的NameNode名称,mycluster名称需和上面定义的一致--><property><name>dfs.ha.namenodes.mycluster</name><value>nn1,nn2</value></property><!--为上面定义的nn1、nn2指定监听的RPC端口--><property><name>dfs.namenode.rpc-address.mycluster.nn1</name><value>hadoop01:9000</value></property><property><name>dfs.namenode.rpc-address.mycluster.nn2</name><value>hadoop02:9000</value></property><!--设置两个namenode的HTTP服务器要监听的地址--><property><name>dfs.namenode.http-address.mycluster.nn1</name><value>hadoop01:50070</value></property><property><name>dfs.namenode.http-address.mycluster.nn2</name><value>hadoop02:50070</value></property><!--指定Jn的监听地址,也就是指定namenode共享数据的存放到哪里--><property><name>dfs.namenode.shared.edits.dir</name><value>qjournal://hadoop01:8485;hadoop02:8485;hadoop03:8485/mycluster</value></property><!--指定HDFS客户端用来联系Active NameNode的Java类--><property><name>dfs.client.failover.proxy.provider.mycluster</name><value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value></property><!--指定namenode隔离机制,在故障转移期间用来保护激活的NameNode--><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><!--关闭权限检查,如果上面指定的用户私钥就是hdfs的启动用户,那么此项不建议配置--><property><name>dfs.permissions.enable</name><value>false</value></property></configuration>$ vim etc/hadoop/core-site.xml # 修改此文件<configuration> # 找到此行,写入以下配置<!--指定HDFS中NameNode的地址为hdfs-site中定义的集群名称--><property><name>fs.defaultFS</name><value>hdfs://mycluster</value></property><!--指定Jn的edits相关数据的存放路径--><property><name>dfs.journalnode.edits.dir</name><value>/apps/usr/hadoop-2.10.1/data/tmp/jn/</value></property><!--指定Hadoop运行时产生文件的存储目录--><property><name>hadoop.tmp.dir</name><value>/apps/usr/hadoop-2.10.1/data/tmp</value></property></configuration># 修改slavaes文件,以便日后可以群起集群,而不是一个个节点去启动# 下面slaves写的是主机名,每行主机名后不可以有空格,并且必须保证hosts文件里写了正确的解析记录$ cat > etc/hadoop/slaves << EOFhadoop01hadoop02hadoop03hadoop04EOF# 发送修改好的hadoop目录到其他节点$ for i in 2 3 4;do rsync -az /apps/usr/hadoop-2.10.1 hadoop0${i}:/apps/usr/;done# 其他节点执行以下命令,以便配置hadoop的环境变量$ cat >> /etc/profile << EOFexport HADOOP_HOME=/apps/usr/hadoop-2.10.1export PATH=\${HADOOP_HOME}/bin:\${HADOOP_HOME}/sbin:\${PATH}EOF$ source /etc/profile
启动hdfs集群
注:新集群启动并格式化NameNode后,会产生一个空的fsimage和edits文件,但由于是高可用集群,JournalNode的作用就是来保存及同步两个NameNode的edits文件的,所以在启动及格式化namenode之前,需要先将JournalNode启动,以免出现问题。
启动JournalNode
# 所有JournalNode节点上执行此指令,启动Journalnode$ hadoop-daemon.sh start journalnode$ ss -lnptu | grep 8485 # 确定所有JournalNode节点已启动tcp LISTEN 0 128 *:8485 *:* users:(("java",pid=22753,fd=256))
格式化nn1并启动
在格式化之前,请确保防火墙已关闭或者已放行相关IP的流量。
# 以下操作在nn1执行$ hdfs namenode -format # 格式化namenode(仅在集群第一次构建完成后需要格式化),输出如下信息表示格式化成功# .......省略部分输出21/01/01 23:46:57 INFO util.GSet: 0.029999999329447746% max memory 889 MB = 273.1 KB21/01/01 23:46:57 INFO util.GSet: capacity = 2^15 = 32768 entries21/01/01 23:46:57 INFO namenode.FSImage: Allocated new BlockPoolId: BP-1160453677-192.168.20.2-160951601775521/01/01 23:46:57 INFO common.Storage: Storage directory /apps/usr/hadoop-2.10.1/data/tmp/dfs/name has been successfully formatted.21/01/01 23:46:57 INFO namenode.FSImageFormatProtobuf: Saving image file /apps/usr/hadoop-2.10.1/data/tmp/dfs/name/current/fsimage.ckpt_0000000000000000000 using no compression21/01/01 23:46:57 INFO namenode.FSImageFormatProtobuf: Image file /apps/usr/hadoop-2.10.1/data/tmp/dfs/name/current/fsimage.ckpt_0000000000000000000 of size 323 bytes saved in 0 seconds .21/01/01 23:46:57 INFO namenode.NNStorageRetentionManager: Going to retain 1 images with txid >= 021/01/01 23:46:57 INFO namenode.FSImage: FSImageSaver clean checkpoint: txid = 0 when meet shutdown.21/01/01 23:46:57 INFO namenode.NameNode: SHUTDOWN_MSG:/************************************************************SHUTDOWN_MSG: Shutting down NameNode at hadoop01/192.168.20.2************************************************************/$ hadoop-daemon.sh start namenode # 启动nn1
同步nn1的元数据信息并启动nn2
# 如下操作在nn2执行$ hdfs namenode -bootstrapStandby$ echo $? # 确认同步成功0$ hadoop-daemon.sh start namenode # 启动nn2
查看两个namenode的角色
分别访问两个namenode的50070端口,即可看到当前角色如下:


启动所有DataNode
# 在nn1上执行如下命令,注意执行的是 hadoop-daemons.sh 脚本$ hadoop-daemons.sh start datanode
访问任意一个NameNode,并点击如下,可以看到所有DataNode节点已ok,即表示无误。

注:此时两个NameNode都是standby状态,故还无法提供任何读写操作。
将nn1切换为Active状态
$ hdfs haadmin -transitionToActive nn1 # 将nn1切换为Active状态# 分别确认 nn1 和 nn2 的集群状态无误$ hdfs haadmin -getServiceState nn1active$ hdfs haadmin -getServiceState nn2standby# HA集群运维指令$ hdfs haadmin -help # 执行此命令,即可输出其支持的选项
hdfs haadmin支持的命令解释如下:
- transitionToActive 和 transitionToStandby:将指定NameNode的状态转换为Active或Standby,这些子命令分别导致给定的NameNode转换到Active或Standby状态(必须保证当前active状态的NameNode处于启动状态,才可转换成功)。这些命令不试图执行任何防护,因此应该很少使用。相反,应该总是使用
hdfs haadmin -failover子命令。- failover:在两个namenode之间启动故障转移,这个子命令导致从第一个提供的NameNode到第二个NameNode的故障转移。如果第一个NameNode处于Standby状态,则该命令将第二个NameNode毫无错误地转换为Active状态。如果第一个NameNode处于Active状态,则会尝试将其优雅地转换为Standby状态。如果失败,将依次尝试防护方法(由dfs.ha.fencing.methods配置),直到其中一个成功。只有经过这个过程,第二个NameNode才会被转换为Active状态。如果没有成功的fencing方法,第二个NameNode将不会被转换到Active状态,返回错误。
- getServiceState:确定指定的NameNode是Active还是Standby
- getAllServiceState:返回所有namenode的状态
- checkHealth:检查指定NameNode的运行状况,连接到提供的NameNode以检查其运行状况。NameNode能够对自身执行一些诊断,包括检查内部服务是否按照预期运行。如果NameNode正常运行,该命令将返回0,否则将返回非0。可以将此命令用于监视目的。
至此,hdfs高可用已经配置完成,但集群的Active和Standby故障转移,还是需要手动转移,并且两个NameNode必须处于启动状态,才可转移成功,这样的话,HA的意义并不大,两个NameNode都正常启动着,干嘛要进行故障转移呢?所以接下俩还需配置HDFS-HA的自动故障转移
配置HDFS-HA自动故障转移
自动故障转移为HDFS增加了两个组件:Zookeeper 和 ZKFailoverController(ZKFC)进程,zookeeper是维护少量协调数据,通知客户端这些数据的改变和监视客户端故障的高可用服务。
HA的自动故障转移依赖于Zookeeper的以下功能:
- 故障检测:集群中的每个NameNode在Zookeeper中维护了一个持久会话,如果机器崩溃,Zookeeper中的会话将终止,Zookeeper通知另一个NameNode需要触发故障转移。
- 现役NameNode选择:Zookeeper提供了一个简单的机制用于唯一的选择一个节点为active状态。如果目前现役NameNode崩溃,另一个节点可能从Zookeeper获得特殊的排外锁以表明它应该成为现役NameNode。
ZKFC是自动故障转移中的另一个新组件,是Zookeeper的客户端,也监视和管理NameNode的状态。每个运行NameNode的主机也运行了一个ZKFC进程。
ZKFC负责如下:
- 健康检测:ZKFC使用一个健康检查命令定期地ping与之在相同主机的NameNode,只要该NameNode及时地回复健康状态,ZKFC认为该节点是健康的。如果该节点挂掉,冻结或进入不健康状态,健康检测器标识该节点为非健康的。
- Zookeeper会话管理:当本地NameNode是健康的,ZKFC保持一个在Zookeeper中打开的会话。如果本地NameNode处于Active状态,ZKFC也保持一个特殊的znode锁,该锁使用了Zookeeper对短暂节点的支持,如果会话终止,锁节点将自动删除。
- 基于Zookeeper的选择:如果本地NameNode是健康的,且ZKFC发现没有其他的节点当前持有znode锁,它将为自己获取该锁,如果成功,则它已经赢得了选择,并负责运行故障转移进程以使它的本地NameNode为Active。故障转移进程与前面描述的手动故障转移相似,首先如果必要保护之前的现役NameNode,然后本地NameNode转换为active状态。
关于故障自动转移流程示意图如下:

停止hdfs集群
在开始配置自动故障转移之前,应关闭集群。HDFS无法在集群运行时从手动故障转移设置过渡到自动故障转移设置。
$ stop-dfs.sh
部署Zookeeper集群
以下操作只需在Hadoop01上执行,然后将修改好的配置文件发送至其他节点,在生产中,如果说想减轻机器压力,或者说机器配置没那么高,可以将Zookeeper集群单独部署到其他节点,只要保证指定的地址无误即可。
wget http://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gztar zxf zookeeper-3.4.14.tar.gz -C /apps/usr/ln -sf /apps/usr/zookeeper-3.4.14 /apps/usr/zk# 配置mkdir /apps/usr/zk/dataecho 1 > /apps/usr/zk/data/myidcat > /apps/usr/zk/conf/zoo.cfg << EOFdataDir=/apps/usr/zk/dataclientPort=2181maxClientCnxns=0tickTime=2000initLimit=10syncLimit=5quorumListenOnAllIPs=trueserver.1=hadoop01:2888:3888server.2=hadoop02:2888:3888server.3=hadoop03:2888:3888EOF# 上面的server可以指定IP+端口# 注意,server.x :x表示节点id,必须和实际IP对应上,如果不对应,最后查看集群状态时,大概率会看到如下:# $ /apps/usr/zk/bin/zkServer.sh status# ZooKeeper JMX enabled by default# Using config: /apps/usr/zk/bin/../conf/zoo.cfg# Error contacting service. It is probably not running. # 尽管端口都在监听,但状态是没运行# 现在接着配置# 将zookeeper目录发送至其他节点$ cd /apps/usr/$ for i in 2 3;do rsync -az /apps/usr/zookeeper-3.4.14 hadoop0${i}:/apps/usr/;done# 启动hadoop01节点的zookeeper$ /apps/usr/zk/bin/zkServer.sh start# 只有leader才会监听28888ss -lnpt | egrep '2181|3888|2888'
修改hadoop02节点的配置文件
ln -sf /apps/usr/zookeeper-3.4.14/ /apps/usr/zkecho 2 > /apps/usr/zk/data/myid
修改hadoop03节点的配置文件
ln -sf /apps/usr/zookeeper-3.4.14/ /apps/usr/zkecho 3 > /apps/usr/zk/data/myid
执行以下指令启动各个zookeeper节点
$ /apps/usr/zk/bin/zkServer.sh startss -lnpt | egrep '2181|3888|2888' # 确定端口在监听# 别忘了,只有leader才会监听2888$ /apps/usr/zk/bin/zkServer.sh status # 查看集群状态(应该有一个leader,两个follower)ZooKeeper JMX enabled by defaultUsing config: /apps/usr/zk/bin/../conf/zoo.cfgMode: follower
至此,zookeeper集群部署完成。
配置hdfs
$ vim etc/hadoop/hdfs-site.xml # 编辑hdfs-site配置文件<!--开启自动故障转移--><property><name>dfs.ha.automatic-failover.enabled</name><value>true</value></property>$ vim etc/hadoop/core-site.xml # 编辑 core-site配置文件<!--指定Zookeeper集群地址--><property><name>ha.zookeeper.quorum</name><value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value></property># 发送修改好的hadoop配置文件目录到其他节点$ for i in 2 3 4;do rsync -az /apps/usr/hadoop-2.10.1/etc/hadoop/ hadoop0${i}:/apps/usr/hadoop-2.10.1/etc/hadoop/;done
初始化HA状态
# 在任意一个NameNode节点上执行即可$ hdfs zkfc -formatZK # 在ZooKeeper中初始化HA状态,看到如下输出则表示初始化成功# 这将在ZooKeeper中创建一个znode,自动故障转移系统将在其中存储其数据。# ....... 省略部分输出21/01/02 02:26:27 INFO zookeeper.ClientCnxn: Socket connection established to hadoop03/192.168.20.4:2181, initiating session21/01/02 02:26:27 INFO zookeeper.ClientCnxn: Session establishment complete on server hadoop03/192.168.20.4:2181, sessionid = 0x30001215a990000, negotiated timeout = 1000021/01/02 02:26:27 INFO ha.ActiveStandbyElector: Successfully created /hadoop-ha/mycluster in ZK.21/01/02 02:26:27 INFO ha.ActiveStandbyElector: Session connected.21/01/02 02:26:27 INFO zookeeper.ZooKeeper: Session: 0x30001215a990000 closed21/01/02 02:26:27 INFO zookeeper.ClientCnxn: EventThread shut down for session: 0x30001215a99000021/01/02 02:26:27 INFO tools.DFSZKFailoverController: SHUTDOWN_MSG:/************************************************************SHUTDOWN_MSG: Shutting down DFSZKFailoverController at hadoop01/192.168.20.2************************************************************/
查看Zookeeper集群是否多了目录数据
$ /apps/usr/zookeeper-3.4.14/bin/zkCli.sh # 在Zookeeper机器上登录到Zookeeper中(也可使用ZooInspector 等客户端工具)[zk: localhost:2181(CONNECTED) 1] ls / # 查看 /[zookeeper, hadoop-ha][zk: localhost:2181(CONNECTED) 2] ls /hadoop-ha # 查看hadoop-ha目录,确认多了此目录[mycluster]
注:同一个Zookeeper集群可以管理多个HDFS集群,但前提是HDFS集群名称不可冲突。如果你的Zookeeper集群开启了认证访问,可以参考官方文档进行配置认证访问Zookeeper。
安装fuser命令
由于在后期进行故障转移时,zkfc会连接到旧的active状态的NameNode进行补刀(再kill一次其NameNode进程,然后自己才会成为新的Active状态的NameNode),在此过程中会使用到fuser命令,需要提前安装,否则在进行故障转移时,zkfc日志中会报错如下:
$ tail /apps/usr/hadoop-2.10.1/logs/hadoop-root-zkfc-lv.log

# 安装fuser指令,所有NameNode节点上都需要执行$ yum -y install psmisc-22.20-17.el7.x86_64$ which fuser # 确认命令已存在/usr/sbin/fuser
启动HDFS节点
$ start-dfs.sh # 在任意一个NameNode节点群起集群
确认集群启动成功
$ tail /apps/usr/hadoop-2.10.1/logs/hadoop-root-zkfc-lv.log # 查看zkfc的日志输出,是否成功选出了处于Active的NameNode2021-01-02 02:44:02,872 INFO org.apache.hadoop.ipc.Server: IPC Server listener on 8019: starting2021-01-02 02:44:02,971 INFO org.apache.hadoop.ha.HealthMonitor: Entering state SERVICE_HEALTHY2021-01-02 02:44:02,971 INFO org.apache.hadoop.ha.ZKFailoverController: Local service NameNode at hadoop02/192.168.20.3:9000 entered state: SERVICE_HEALTHY2021-01-02 02:44:02,983 INFO org.apache.hadoop.ha.ActiveStandbyElector: Checking for any old active which needs to be fenced...2021-01-02 02:44:02,992 INFO org.apache.hadoop.ha.ActiveStandbyElector: Old node exists: 0a096d79636c757374657212036e6e311a086861646f6f70303120a84628d33e2021-01-02 02:44:02,994 INFO org.apache.hadoop.ha.ZKFailoverController: Should fence: NameNode at hadoop01/192.168.20.2:90002021-01-02 02:44:03,014 INFO org.apache.hadoop.ha.ZKFailoverController: Successfully transitioned NameNode at hadoop01/192.168.20.2:9000 to standby state without fencing2021-01-02 02:44:03,014 INFO org.apache.hadoop.ha.ActiveStandbyElector: Writing znode /hadoop-ha/mycluster/ActiveBreadCrumb to indicate that the local node is the most recent active...2021-01-02 02:44:03,021 INFO org.apache.hadoop.ha.ZKFailoverController: Trying to make NameNode at hadoop02/192.168.20.3:9000 active...2021-01-02 02:44:03,453 INFO org.apache.hadoop.ha.ZKFailoverController: Successfully transitioned NameNode at hadoop02/192.168.20.3:9000 to active state# 从日志输出中可以看到,hadoop02为active状态。
访问hadoop02,确认其为Active状态。

访问hadoop01,确认其为Standby状态。

至此,HDFS的自动故障转移就配置完成了,可以自行停止当前Active状态的NameNode,然后确认Standby的NameNode可以顺利成为Active状态。
确认集群读写文件、故障自动转移正常
$ hdfs dfs -mkdir -p /ha/test/ # 创建测试目录$ hdfs dfs -put zookeeper-3.4.14.tar.gz /ha/test/ # 上传文件
访问当前Active状态的NameNode,确认数据存在,如下:

为什么非要访问处于Active状态的NameNode呢?是因为Standby状态的NameNode是不允许读的,你会看到如下错误:

# 手动干掉当前处于Active状态的NameNode$ jps | grep NameNode | cut -d ' ' -f1 | xargs kill -9# 稍后可以执行如下命令启动NameNode$ hadoop-daemon.sh start namenode
确认原先处于Standby的节点为Active状态:

确认刚刚上传的文件可以在新的Active状态的NameNode看到,如下:

好,至此即可验证,hdfs的高可用及自动故障转移配置无误。接下来配置YARN的高可用。
配置YARN高可用
参考:官方文档。
RM (ResourceManager)负责跟踪集群中的资源,调度应用程序(如MapReduce任务)。在Hadoop 2.4之前,ResourceManager是YARN集群中的单点故障。高可用性以主备ResourceManager对的形式增加冗余,消除了这种单点故障。
其高可用架构图如下:

ResourceManager HA通过Active/Standby体系结构实现在任何时间点,RM之一都处于Active状态,并且一个或多个RM处于Standby模式。
如果未启用自动故障转移,则管理员必须手动将其中一个RM转换为Active。要从一个RM到另一个RM进行故障转移,他们应该首先将Active-RM转换为Standby,然后将Standby-RM转换为Active。所有这些都可以使用yarn rmadminCLI完成。
RM可以选择嵌入基于Zookeeper的ActiveStandbyElector,以确定哪个RM应该是Active。当Active发生故障或无响应时,另一个RM将自动选作Active,然后接管。请注意,无需像HDFS那样运行单独的ZKFC守护程序,因为嵌入在RM中的ActiveStandbyElector充当故障检测器和领导者选举者,而不是单独的ZKFC守护进程。
当有多个RM时,预期客户端和节点使用的配置(yarn-site.xml)会列出所有RM。客户端,ApplicationMaster(AM)和NodeManager(NM)尝试以循环方式连接到RM,直到它们到达活动RM。如果Active服务器出现故障,他们将继续轮询,直到命中“新”Active服务器为止。
修改yarn-site.xml配置文件
注:以下配置只需在hadoop01上进行修改,修改完成后,分发到其他节点即可。
$ cat yarn-site.xml # 完整配置文件内容如下<?xml version="1.0"?><configuration><!--Reducer获取数据的方式--><property><name>yarn.nodemanager.aux-services</name><value>mapreduce_shuffle</value></property><!--开启日志聚集功能--><property><name>yarn.log-aggregation-enable</name><value>true</value></property><!--日志保留时间设置为7天--><property><name>yarn.log-aggregation.retain-seconds</name><value>604800</value></property><!--开启rm的ha功能--><property><name>yarn.resourcemanager.ha.enabled</name><value>true</value></property><!--指定yarn集群id--><property><name>yarn.resourcemanager.cluster-id</name><value>cluster-yarn1</value></property><!--指定参与集群的名称,可以指定两个以上--><property><name>yarn.resourcemanager.ha.rm-ids</name><value>rm1,rm2</value></property><!--指定实际yarn集群中的所有节点地址--><property><name>yarn.resourcemanager.hostname.rm1</name><value>hadoop03</value></property><property><name>yarn.resourcemanager.hostname.rm2</name><value>hadoop04</value></property><!--指定各个节点的yarn监听端口--><property><name>yarn.resourcemanager.webapp.address.rm1</name><value>hadoop03:8088</value></property><property><name>yarn.resourcemanager.webapp.address.rm2</name><value>hadoop04:8088</value></property><!--指定Zookeeper集群的监听地址--><property><name>yarn.resourcemanager.zk-address</name><value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value></property><!--启用自动恢复--><property><name>yarn.resourcemanager.recovery.enabled</name><value>true</value></property><!--指定resourcemanager的状态信息存储在zookeeper集群中--><property><name>yarn.resourcemanager.store.class</name><value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value></property><!--是否启动一个线程检查每个任务正使用的物理内存量,如果任务超出分配值,则直接将其杀掉,默认是true--><property><name>yarn.nodemanager.pmem-check-enabled</name><value>false</value></property><!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是true--><property><name>yarn.nodemanager.vmem-check-enabled</name><value>false</value></property></configuration># 配置mapred-site.xml$ vim etc/hadoop/mapred-site.xml # 编辑此配置文件,以下是完整配置文件$ cat mapred-site.xml<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><configuration><!-- 指定MapReduce运行时框架运行在YARN上,默认是local --><property><name>mapreduce.framework.name</name><value>yarn</value></property><!--指定历史服务器端地址--><property><name>mapreduce.jobhistory.address</name><value>hadoop04:10020</value></property><!--历史服务器web端地址--><property><name>mapreduce.jobhistory.webapp.address</name><value>hadoop04:19888</value></property></configuration># 将修改后的配置文件发送到其他所有节点$ for i in 2 3 4;do rsync -az /apps/usr/hadoop-2.10.1/etc/hadoop/ hadoop0${i}:/apps/usr/hadoop-2.10.1/etc/hadoop/;done
启动yarn角色
# 在hadoop03上执行$ start-yarn.sh # 执行群起脚本后可以发现,它并没有像hdfs一样,启动集群中的所有resourcemanagerstarting yarn daemonsstarting resourcemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-resourcemanager-lv.outhadoop04: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.outhadoop01: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.outhadoop03: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.outhadoop02: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.out# 故需要在集群中的其他resourcemanager上(hadoop04)启动yarn$ yarn-daemon.sh start resourcemanagerstarting resourcemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-resourcemanager-lv.out
访问yarn的web界面
访问任意yarn节点的8088端口即可,它都会自动重定向到当前处于Active状态的节点,如下(需要你的电脑配置hosts记录,否则会访问不到相应的yarn节点):

