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免密登录

切记,先配置主机解析记录,再配置免密登录。

  1. # 配置主机名解析记录
  2. $ cat >> /etc/hosts <<EOF
  3. 192.168.20.2 hadoop01
  4. 192.168.20.3 hadoop02
  5. 192.168.20.4 hadoop03
  6. 192.168.20.5 hadoop04
  7. EOF
  8. # 修改后的hosts文件发送至其他节点
  9. $ for i in $(seq 1 4);do rsync -az /etc/hosts hadoop0${i}:/etc/;done
  10. # 下面是配置免密登录,需要在resourcemanager和NameNode两个角色的机器上执行
  11. # 如果可以,最好在集群中所有节点都配置免密登录到集群中的其他节点
  12. # 为了方便后续群起集群,我们必须在resourcemanager和NameNode两个角色的机器上配置免密登录到其他节点
  13. $ ssh-keygen -t rsa # 执行后一路回车
  14. $ for i in $(seq 1 4);do ssh-copy-id hadoop0${i} ;done # 执行后根据提示,该输入yes输入yes,该输入密码就输入密码

一定要确保所有NameNode和resourcemanager节点可以免密登录到其他节点,否则后续自动故障转移会出问题。

修改系统限制

  1. $ mv /etc/security/limits.conf{,.bak}
  2. cat > /etc/security/limits.conf << EOF
  3. * - nofile 655360
  4. * - memlock unlimited
  5. * - stack 655360
  6. * - nproc unlimited
  7. EOF
  8. cat > /etc/sysctl.conf << EOF
  9. kernel.sysrq = 0
  10. kernel.core_uses_pid = 1
  11. fs.file-max=655360
  12. kernel.msgmnb = 65536
  13. kernel.msgmax = 65536
  14. kernel.shmmax = 68719476736
  15. kernel.shmall = 4294967296
  16. kernel.pid_max = 655360
  17. net.ipv4.tcp_tw_reuse = 1
  18. net.ipv4.tcp_tw_recycle = 0
  19. net.ipv4.tcp_max_tw_buckets = 10000
  20. net.ipv4.tcp_fin_timeout = 30
  21. net.ipv4.tcp_timestamps = 0
  22. net.ipv4.tcp_sack = 1
  23. net.ipv4.tcp_window_scaling = 1
  24. net.ipv4.tcp_ecn = 0
  25. net.ipv4.tcp_keepalive_time = 600
  26. net.ipv4.tcp_keepalive_intvl = 30
  27. net.ipv4.tcp_keepalive_probes = 3
  28. net.ipv4.tcp_max_orphans = 655360
  29. net.ipv4.tcp_max_syn_backlog = 262144
  30. net.ipv4.tcp_mem = 65536 131072 262144
  31. net.ipv4.udp_mem = 65536 131072 262144
  32. net.ipv4.tcp_rmem = 4096 87380 16777216
  33. net.ipv4.tcp_wmem = 4096 16384 16777216
  34. net.ipv4.ip_local_port_range = 1024 65535
  35. net.ipv4.route.gc_timeout = 100
  36. # 禁止icmp重定向报文
  37. net.ipv4.conf.all.accept_redirects = 0
  38. # 禁止icmp源路由
  39. net.ipv4.conf.all.accept_source_route = 0
  40. net.core.somaxconn = 65535
  41. net.core.rmem_default = 8388608
  42. net.core.wmem_default = 8388608
  43. net.core.rmem_max = 16777216
  44. net.core.wmem_max = 16777216
  45. net.core.netdev_max_backlog = 262144
  46. vm.swappiness = 10
  47. vm.overcommit_memory = 1
  48. vm.max_map_count = 262144
  49. EOF
  50. sysctl -p
  51. #将修改后的文件发送至其他节点
  52. $ for i in $(seq 1 4);do rsync -az /etc/security/limits.conf hadoop0${i}:/etc/security/;done
  53. $ for i in $(seq 1 4);do rsync -az /etc/sysctl.conf hadoop0${i}:/etc/;done
  54. # 其余节点需执行下面命令刷新内核参数
  55. $ sysctl -p

配置jdk

自行去oracle官网下载java包 jdk-8u261-linux-x64.tar.gz ,然后上传至服务器。

配置jdk环境的操作,是需要在所有节点进行的。

  1. $ mkdir /apps/usr -p && systemctl stop firewalld && systemctl disable firewalld && setenforce 0
  2. $ tar zxf jdk-8u261-linux-x64.tar.gz -C /apps/usr/
  3. $ ln -sf /apps/usr/jdk1.8.0_261 /apps/usr/jdk
  4. $ cat >> /etc/profile << EOF
  5. export JAVA_HOME=/apps/usr/jdk
  6. export CLASSPATH=\$JAVA_HOME/lib
  7. export PATH=\$JAVA_HOME/bin:\$PATH
  8. EOF
  9. $ source /etc/profile
  10. $ java -version # 查看版本信息
  11. java version "1.8.0_261"
  12. Java(TM) SE Runtime Environment (build 1.8.0_261-b12)
  13. Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)

配置ntp时间同步

在hadoop01配置时间服务器,其他客户端制定定时任务即可。

  1. $ yum -y install ntp
  2. $ vim /etc/ntp.conf # 修改配置文件
  3. # 找到(17行左右):
  4. #restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
  5. # 改为(网段是你自己的网段,表示允许哪个网段的客户端来同步时间):
  6. restrict 192.168.20.0 mask 255.255.255.0 nomodify notrap
  7. # 找到
  8. server 0.centos.pool.ntp.org iburst
  9. server 1.centos.pool.ntp.org iburst
  10. server 2.centos.pool.ntp.org iburst
  11. server 3.centos.pool.ntp.org iburst
  12. # 将其注释掉,表示不使用互联网上的时间同步服务器
  13. #server 0.centos.pool.ntp.org iburst
  14. #server 1.centos.pool.ntp.org iburst
  15. #server 2.centos.pool.ntp.org iburst
  16. #server 3.centos.pool.ntp.org iburst
  17. # 末尾追加
  18. server 127.127.1.0
  19. fudge 127.127.1.0 stratum 5
  20. # 在上级时钟源失效时,NTP会使用127.127.1.0的本地时钟,将local时间作为ntp服务器时间提供给ntp客户端。
  21. # NTP把本地主机的时钟也看作外部时钟源来处理,分配的地址是127.127.1.0
  22. # 让硬件时间和系统时间一起同步
  23. $ echo 'SYNC_HWCLOCK=yes' >> /etc/sysconfig/ntpd
  24. # 重启ntp服务器生效
  25. $ systemctl restart ntpd && systemctl enable ntpd
  26. # 配置其他客户端定时同步时间(哪些机器要同步上面时间服务器的时间,就进行以下配置)
  27. $ yum -y install ntp
  28. $ crontab -e # 写入以下定时任务
  29. */5 * * * * /usr/sbin/ntpdate 192.168.20.2 &> /dev/null

部署hadoop

没有特别说明的,以下操作在hadoop01上进行即可。

  1. $ wget https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/current2/hadoop-2.10.1.tar.gz
  2. $ tar zxf hadoop-2.10.1.tar.gz -C /apps/usr/
  3. # 配置hadoop环境变量
  4. $ cat >> /etc/profile << EOF
  5. export HADOOP_HOME=/apps/usr/hadoop-2.10.1
  6. export PATH=\${HADOOP_HOME}/bin:\${HADOOP_HOME}/sbin:\${PATH}
  7. EOF
  8. $ source /etc/profile
  9. $ cd /apps/usr/hadoop-2.10.1/
  10. # 修改hadoop的jdk环境变量
  11. $ sed -i 's/export JAVA_HOME=.*/export JAVA_HOME=\/apps\/usr\/jdk/g' etc/hadoop/hadoop-env.sh
  12. $ vim etc/hadoop/hdfs-site.xml # 修改此文件
  13. <configuration> # 找到此行,写入以下配置
  14. <!--默认副本数为3-->
  15. <property>
  16. <name>dfs.replication</name>
  17. <value>3</value>
  18. </property>
  19. <!--自定义hdfs集群名称-->
  20. <property>
  21. <name>dfs.nameservices</name>
  22. <value>mycluster</value>
  23. </property>
  24. <!--自定义参加集群的NameNode名称,mycluster名称需和上面定义的一致-->
  25. <property>
  26. <name>dfs.ha.namenodes.mycluster</name>
  27. <value>nn1,nn2</value>
  28. </property>
  29. <!--为上面定义的nn1nn2指定监听的RPC端口-->
  30. <property>
  31. <name>dfs.namenode.rpc-address.mycluster.nn1</name>
  32. <value>hadoop01:9000</value>
  33. </property>
  34. <property>
  35. <name>dfs.namenode.rpc-address.mycluster.nn2</name>
  36. <value>hadoop02:9000</value>
  37. </property>
  38. <!--设置两个namenodeHTTP服务器要监听的地址-->
  39. <property>
  40. <name>dfs.namenode.http-address.mycluster.nn1</name>
  41. <value>hadoop01:50070</value>
  42. </property>
  43. <property>
  44. <name>dfs.namenode.http-address.mycluster.nn2</name>
  45. <value>hadoop02:50070</value>
  46. </property>
  47. <!--指定Jn的监听地址,也就是指定namenode共享数据的存放到哪里-->
  48. <property>
  49. <name>dfs.namenode.shared.edits.dir</name>
  50. <value>qjournal://hadoop01:8485;hadoop02:8485;hadoop03:8485/mycluster</value>
  51. </property>
  52. <!--指定HDFS客户端用来联系Active NameNodeJava类-->
  53. <property>
  54. <name>dfs.client.failover.proxy.provider.mycluster</name>
  55. <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
  56. </property>
  57. <!--指定namenode隔离机制,在故障转移期间用来保护激活的NameNode-->
  58. <property>
  59. <name>dfs.ha.fencing.methods</name>
  60. <value>sshfence</value>
  61. </property>
  62. <!--指定用于ssh到其他节点的私钥路径-->
  63. <property>
  64. <name>dfs.ha.fencing.ssh.private-key-files</name>
  65. <value>/root/.ssh/id_rsa</value>
  66. </property>
  67. <!--关闭权限检查,如果上面指定的用户私钥就是hdfs的启动用户,那么此项不建议配置-->
  68. <property>
  69. <name>dfs.permissions.enable</name>
  70. <value>false</value>
  71. </property>
  72. </configuration>
  73. $ vim etc/hadoop/core-site.xml # 修改此文件
  74. <configuration> # 找到此行,写入以下配置
  75. <!--指定HDFSNameNode的地址为hdfs-site中定义的集群名称-->
  76. <property>
  77. <name>fs.defaultFS</name>
  78. <value>hdfs://mycluster</value>
  79. </property>
  80. <!--指定Jnedits相关数据的存放路径-->
  81. <property>
  82. <name>dfs.journalnode.edits.dir</name>
  83. <value>/apps/usr/hadoop-2.10.1/data/tmp/jn/</value>
  84. </property>
  85. <!--指定Hadoop运行时产生文件的存储目录-->
  86. <property>
  87. <name>hadoop.tmp.dir</name>
  88. <value>/apps/usr/hadoop-2.10.1/data/tmp</value>
  89. </property>
  90. </configuration>
  91. # 修改slavaes文件,以便日后可以群起集群,而不是一个个节点去启动
  92. # 下面slaves写的是主机名,每行主机名后不可以有空格,并且必须保证hosts文件里写了正确的解析记录
  93. $ cat > etc/hadoop/slaves << EOF
  94. hadoop01
  95. hadoop02
  96. hadoop03
  97. hadoop04
  98. EOF
  99. # 发送修改好的hadoop目录到其他节点
  100. $ for i in 2 3 4;do rsync -az /apps/usr/hadoop-2.10.1 hadoop0${i}:/apps/usr/;done
  101. # 其他节点执行以下命令,以便配置hadoop的环境变量
  102. $ cat >> /etc/profile << EOF
  103. export HADOOP_HOME=/apps/usr/hadoop-2.10.1
  104. export PATH=\${HADOOP_HOME}/bin:\${HADOOP_HOME}/sbin:\${PATH}
  105. EOF
  106. $ source /etc/profile

启动hdfs集群

注:新集群启动并格式化NameNode后,会产生一个空的fsimage和edits文件,但由于是高可用集群,JournalNode的作用就是来保存及同步两个NameNode的edits文件的,所以在启动及格式化namenode之前,需要先将JournalNode启动,以免出现问题。

启动JournalNode

  1. # 所有JournalNode节点上执行此指令,启动Journalnode
  2. $ hadoop-daemon.sh start journalnode
  3. $ ss -lnptu | grep 8485 # 确定所有JournalNode节点已启动
  4. tcp LISTEN 0 128 *:8485 *:* users:(("java",pid=22753,fd=256))

格式化nn1并启动

在格式化之前,请确保防火墙已关闭或者已放行相关IP的流量。

  1. # 以下操作在nn1执行
  2. $ hdfs namenode -format # 格式化namenode(仅在集群第一次构建完成后需要格式化),输出如下信息表示格式化成功
  3. # .......省略部分输出
  4. 21/01/01 23:46:57 INFO util.GSet: 0.029999999329447746% max memory 889 MB = 273.1 KB
  5. 21/01/01 23:46:57 INFO util.GSet: capacity = 2^15 = 32768 entries
  6. 21/01/01 23:46:57 INFO namenode.FSImage: Allocated new BlockPoolId: BP-1160453677-192.168.20.2-1609516017755
  7. 21/01/01 23:46:57 INFO common.Storage: Storage directory /apps/usr/hadoop-2.10.1/data/tmp/dfs/name has been successfully formatted.
  8. 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 compression
  9. 21/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 .
  10. 21/01/01 23:46:57 INFO namenode.NNStorageRetentionManager: Going to retain 1 images with txid >= 0
  11. 21/01/01 23:46:57 INFO namenode.FSImage: FSImageSaver clean checkpoint: txid = 0 when meet shutdown.
  12. 21/01/01 23:46:57 INFO namenode.NameNode: SHUTDOWN_MSG:
  13. /************************************************************
  14. SHUTDOWN_MSG: Shutting down NameNode at hadoop01/192.168.20.2
  15. ************************************************************/
  16. $ hadoop-daemon.sh start namenode # 启动nn1

同步nn1的元数据信息并启动nn2

  1. # 如下操作在nn2执行
  2. $ hdfs namenode -bootstrapStandby
  3. $ echo $? # 确认同步成功
  4. 0
  5. $ hadoop-daemon.sh start namenode # 启动nn2

查看两个namenode的角色

分别访问两个namenode的50070端口,即可看到当前角色如下:

Hadoop HDFS之HA集群部署 - 图1

Hadoop HDFS之HA集群部署 - 图2

启动所有DataNode

  1. # 在nn1上执行如下命令,注意执行的是 hadoop-daemons.sh 脚本
  2. $ hadoop-daemons.sh start datanode

访问任意一个NameNode,并点击如下,可以看到所有DataNode节点已ok,即表示无误。

Hadoop HDFS之HA集群部署 - 图3

注:此时两个NameNode都是standby状态,故还无法提供任何读写操作。

将nn1切换为Active状态

  1. $ hdfs haadmin -transitionToActive nn1 # 将nn1切换为Active状态
  2. # 分别确认 nn1 和 nn2 的集群状态无误
  3. $ hdfs haadmin -getServiceState nn1
  4. active
  5. $ hdfs haadmin -getServiceState nn2
  6. standby
  7. # HA集群运维指令
  8. $ 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状态。

关于故障自动转移流程示意图如下:

Hadoop HDFS之HA集群部署 - 图4

停止hdfs集群

在开始配置自动故障转移之前,应关闭集群。HDFS无法在集群运行时从手动故障转移设置过渡到自动故障转移设置。

  1. $ stop-dfs.sh

部署Zookeeper集群

以下操作只需在Hadoop01上执行,然后将修改好的配置文件发送至其他节点,在生产中,如果说想减轻机器压力,或者说机器配置没那么高,可以将Zookeeper集群单独部署到其他节点,只要保证指定的地址无误即可。

  1. wget http://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
  2. tar zxf zookeeper-3.4.14.tar.gz -C /apps/usr/
  3. ln -sf /apps/usr/zookeeper-3.4.14 /apps/usr/zk
  4. # 配置
  5. mkdir /apps/usr/zk/data
  6. echo 1 > /apps/usr/zk/data/myid
  7. cat > /apps/usr/zk/conf/zoo.cfg << EOF
  8. dataDir=/apps/usr/zk/data
  9. clientPort=2181
  10. maxClientCnxns=0
  11. tickTime=2000
  12. initLimit=10
  13. syncLimit=5
  14. quorumListenOnAllIPs=true
  15. server.1=hadoop01:2888:3888
  16. server.2=hadoop02:2888:3888
  17. server.3=hadoop03:2888:3888
  18. EOF
  19. # 上面的server可以指定IP+端口
  20. # 注意,server.x :x表示节点id,必须和实际IP对应上,如果不对应,最后查看集群状态时,大概率会看到如下:
  21. # $ /apps/usr/zk/bin/zkServer.sh status
  22. # ZooKeeper JMX enabled by default
  23. # Using config: /apps/usr/zk/bin/../conf/zoo.cfg
  24. # Error contacting service. It is probably not running. # 尽管端口都在监听,但状态是没运行
  25. # 现在接着配置
  26. # 将zookeeper目录发送至其他节点
  27. $ cd /apps/usr/
  28. $ for i in 2 3;do rsync -az /apps/usr/zookeeper-3.4.14 hadoop0${i}:/apps/usr/;done
  29. # 启动hadoop01节点的zookeeper
  30. $ /apps/usr/zk/bin/zkServer.sh start
  31. # 只有leader才会监听28888
  32. ss -lnpt | egrep '2181|3888|2888'

修改hadoop02节点的配置文件

  1. ln -sf /apps/usr/zookeeper-3.4.14/ /apps/usr/zk
  2. echo 2 > /apps/usr/zk/data/myid

修改hadoop03节点的配置文件

  1. ln -sf /apps/usr/zookeeper-3.4.14/ /apps/usr/zk
  2. echo 3 > /apps/usr/zk/data/myid

执行以下指令启动各个zookeeper节点

  1. $ /apps/usr/zk/bin/zkServer.sh start
  2. ss -lnpt | egrep '2181|3888|2888' # 确定端口在监听
  3. # 别忘了,只有leader才会监听2888
  4. $ /apps/usr/zk/bin/zkServer.sh status # 查看集群状态(应该有一个leader,两个follower)
  5. ZooKeeper JMX enabled by default
  6. Using config: /apps/usr/zk/bin/../conf/zoo.cfg
  7. Mode: follower

至此,zookeeper集群部署完成。

配置hdfs

  1. $ vim etc/hadoop/hdfs-site.xml # 编辑hdfs-site配置文件
  2. <!--开启自动故障转移-->
  3. <property>
  4. <name>dfs.ha.automatic-failover.enabled</name>
  5. <value>true</value>
  6. </property>
  7. $ vim etc/hadoop/core-site.xml # 编辑 core-site配置文件
  8. <!--指定Zookeeper集群地址-->
  9. <property>
  10. <name>ha.zookeeper.quorum</name>
  11. <value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
  12. </property>
  13. # 发送修改好的hadoop配置文件目录到其他节点
  14. $ 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状态

  1. # 在任意一个NameNode节点上执行即可
  2. $ hdfs zkfc -formatZK # 在ZooKeeper中初始化HA状态,看到如下输出则表示初始化成功
  3. # 这将在ZooKeeper中创建一个znode,自动故障转移系统将在其中存储其数据。
  4. # ....... 省略部分输出
  5. 21/01/02 02:26:27 INFO zookeeper.ClientCnxn: Socket connection established to hadoop03/192.168.20.4:2181, initiating session
  6. 21/01/02 02:26:27 INFO zookeeper.ClientCnxn: Session establishment complete on server hadoop03/192.168.20.4:2181, sessionid = 0x30001215a990000, negotiated timeout = 10000
  7. 21/01/02 02:26:27 INFO ha.ActiveStandbyElector: Successfully created /hadoop-ha/mycluster in ZK.
  8. 21/01/02 02:26:27 INFO ha.ActiveStandbyElector: Session connected.
  9. 21/01/02 02:26:27 INFO zookeeper.ZooKeeper: Session: 0x30001215a990000 closed
  10. 21/01/02 02:26:27 INFO zookeeper.ClientCnxn: EventThread shut down for session: 0x30001215a990000
  11. 21/01/02 02:26:27 INFO tools.DFSZKFailoverController: SHUTDOWN_MSG:
  12. /************************************************************
  13. SHUTDOWN_MSG: Shutting down DFSZKFailoverController at hadoop01/192.168.20.2
  14. ************************************************************/

查看Zookeeper集群是否多了目录数据

  1. $ /apps/usr/zookeeper-3.4.14/bin/zkCli.sh # 在Zookeeper机器上登录到Zookeeper中(也可使用ZooInspector 等客户端工具)
  2. [zk: localhost:2181(CONNECTED) 1] ls / # 查看 /
  3. [zookeeper, hadoop-ha]
  4. [zk: localhost:2181(CONNECTED) 2] ls /hadoop-ha # 查看hadoop-ha目录,确认多了此目录
  5. [mycluster]

注:同一个Zookeeper集群可以管理多个HDFS集群,但前提是HDFS集群名称不可冲突。如果你的Zookeeper集群开启了认证访问,可以参考官方文档进行配置认证访问Zookeeper。

安装fuser命令

由于在后期进行故障转移时,zkfc会连接到旧的active状态的NameNode进行补刀(再kill一次其NameNode进程,然后自己才会成为新的Active状态的NameNode),在此过程中会使用到fuser命令,需要提前安装,否则在进行故障转移时,zkfc日志中会报错如下:

  1. $ tail /apps/usr/hadoop-2.10.1/logs/hadoop-root-zkfc-lv.log

Hadoop HDFS之HA集群部署 - 图5

  1. # 安装fuser指令,所有NameNode节点上都需要执行
  2. $ yum -y install psmisc-22.20-17.el7.x86_64
  3. $ which fuser # 确认命令已存在
  4. /usr/sbin/fuser

启动HDFS节点

  1. $ start-dfs.sh # 在任意一个NameNode节点群起集群

确认集群启动成功

  1. $ tail /apps/usr/hadoop-2.10.1/logs/hadoop-root-zkfc-lv.log # 查看zkfc的日志输出,是否成功选出了处于Active的NameNode
  2. 2021-01-02 02:44:02,872 INFO org.apache.hadoop.ipc.Server: IPC Server listener on 8019: starting
  3. 2021-01-02 02:44:02,971 INFO org.apache.hadoop.ha.HealthMonitor: Entering state SERVICE_HEALTHY
  4. 2021-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_HEALTHY
  5. 2021-01-02 02:44:02,983 INFO org.apache.hadoop.ha.ActiveStandbyElector: Checking for any old active which needs to be fenced...
  6. 2021-01-02 02:44:02,992 INFO org.apache.hadoop.ha.ActiveStandbyElector: Old node exists: 0a096d79636c757374657212036e6e311a086861646f6f70303120a84628d33e
  7. 2021-01-02 02:44:02,994 INFO org.apache.hadoop.ha.ZKFailoverController: Should fence: NameNode at hadoop01/192.168.20.2:9000
  8. 2021-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 fencing
  9. 2021-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...
  10. 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...
  11. 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
  12. # 从日志输出中可以看到,hadoop02为active状态。

访问hadoop02,确认其为Active状态。

Hadoop HDFS之HA集群部署 - 图6

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

Hadoop HDFS之HA集群部署 - 图7

至此,HDFS的自动故障转移就配置完成了,可以自行停止当前Active状态的NameNode,然后确认Standby的NameNode可以顺利成为Active状态。

确认集群读写文件、故障自动转移正常

  1. $ hdfs dfs -mkdir -p /ha/test/ # 创建测试目录
  2. $ hdfs dfs -put zookeeper-3.4.14.tar.gz /ha/test/ # 上传文件

访问当前Active状态的NameNode,确认数据存在,如下:

Hadoop HDFS之HA集群部署 - 图8

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

Hadoop HDFS之HA集群部署 - 图9

  1. # 手动干掉当前处于Active状态的NameNode
  2. $ jps | grep NameNode | cut -d ' ' -f1 | xargs kill -9
  3. # 稍后可以执行如下命令启动NameNode
  4. $ hadoop-daemon.sh start namenode

确认原先处于Standby的节点为Active状态:

Hadoop HDFS之HA集群部署 - 图10

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

Hadoop HDFS之HA集群部署 - 图11

好,至此即可验证,hdfs的高可用及自动故障转移配置无误。接下来配置YARN的高可用。

配置YARN高可用

参考:官方文档

RM (ResourceManager)负责跟踪集群中的资源,调度应用程序(如MapReduce任务)。在Hadoop 2.4之前,ResourceManager是YARN集群中的单点故障。高可用性以主备ResourceManager对的形式增加冗余,消除了这种单点故障。

其高可用架构图如下:

Hadoop HDFS之HA集群部署 - 图12

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上进行修改,修改完成后,分发到其他节点即可。

  1. $ cat yarn-site.xml # 完整配置文件内容如下
  2. <?xml version="1.0"?>
  3. <configuration>
  4. <!--Reducer获取数据的方式-->
  5. <property>
  6. <name>yarn.nodemanager.aux-services</name>
  7. <value>mapreduce_shuffle</value>
  8. </property>
  9. <!--开启日志聚集功能-->
  10. <property>
  11. <name>yarn.log-aggregation-enable</name>
  12. <value>true</value>
  13. </property>
  14. <!--日志保留时间设置为7天-->
  15. <property>
  16. <name>yarn.log-aggregation.retain-seconds</name>
  17. <value>604800</value>
  18. </property>
  19. <!--开启rmha功能-->
  20. <property>
  21. <name>yarn.resourcemanager.ha.enabled</name>
  22. <value>true</value>
  23. </property>
  24. <!--指定yarn集群id-->
  25. <property>
  26. <name>yarn.resourcemanager.cluster-id</name>
  27. <value>cluster-yarn1</value>
  28. </property>
  29. <!--指定参与集群的名称,可以指定两个以上-->
  30. <property>
  31. <name>yarn.resourcemanager.ha.rm-ids</name>
  32. <value>rm1,rm2</value>
  33. </property>
  34. <!--指定实际yarn集群中的所有节点地址-->
  35. <property>
  36. <name>yarn.resourcemanager.hostname.rm1</name>
  37. <value>hadoop03</value>
  38. </property>
  39. <property>
  40. <name>yarn.resourcemanager.hostname.rm2</name>
  41. <value>hadoop04</value>
  42. </property>
  43. <!--指定各个节点的yarn监听端口-->
  44. <property>
  45. <name>yarn.resourcemanager.webapp.address.rm1</name>
  46. <value>hadoop03:8088</value>
  47. </property>
  48. <property>
  49. <name>yarn.resourcemanager.webapp.address.rm2</name>
  50. <value>hadoop04:8088</value>
  51. </property>
  52. <!--指定Zookeeper集群的监听地址-->
  53. <property>
  54. <name>yarn.resourcemanager.zk-address</name>
  55. <value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
  56. </property>
  57. <!--启用自动恢复-->
  58. <property>
  59. <name>yarn.resourcemanager.recovery.enabled</name>
  60. <value>true</value>
  61. </property>
  62. <!--指定resourcemanager的状态信息存储在zookeeper集群中-->
  63. <property>
  64. <name>yarn.resourcemanager.store.class</name>
  65. <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
  66. </property>
  67. <!--是否启动一个线程检查每个任务正使用的物理内存量,如果任务超出分配值,则直接将其杀掉,默认是true-->
  68. <property>
  69. <name>yarn.nodemanager.pmem-check-enabled</name>
  70. <value>false</value>
  71. </property>
  72. <!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是true-->
  73. <property>
  74. <name>yarn.nodemanager.vmem-check-enabled</name>
  75. <value>false</value>
  76. </property>
  77. </configuration>
  78. # 配置mapred-site.xml
  79. $ vim etc/hadoop/mapred-site.xml # 编辑此配置文件,以下是完整配置文件
  80. $ cat mapred-site.xml
  81. <?xml version="1.0"?>
  82. <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
  83. <configuration>
  84. <!-- 指定MapReduce运行时框架运行在YARN上,默认是local -->
  85. <property>
  86. <name>mapreduce.framework.name</name>
  87. <value>yarn</value>
  88. </property>
  89. <!--指定历史服务器端地址-->
  90. <property>
  91. <name>mapreduce.jobhistory.address</name>
  92. <value>hadoop04:10020</value>
  93. </property>
  94. <!--历史服务器web端地址-->
  95. <property>
  96. <name>mapreduce.jobhistory.webapp.address</name>
  97. <value>hadoop04:19888</value>
  98. </property>
  99. </configuration>
  100. # 将修改后的配置文件发送到其他所有节点
  101. $ 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角色

  1. # 在hadoop03上执行
  2. $ start-yarn.sh # 执行群起脚本后可以发现,它并没有像hdfs一样,启动集群中的所有resourcemanager
  3. starting yarn daemons
  4. starting resourcemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-resourcemanager-lv.out
  5. hadoop04: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.out
  6. hadoop01: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.out
  7. hadoop03: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.out
  8. hadoop02: starting nodemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-nodemanager-lv.out
  9. # 故需要在集群中的其他resourcemanager上(hadoop04)启动yarn
  10. $ yarn-daemon.sh start resourcemanager
  11. starting resourcemanager, logging to /apps/usr/hadoop-2.10.1/logs/yarn-root-resourcemanager-lv.out

访问yarn的web界面

访问任意yarn节点的8088端口即可,它都会自动重定向到当前处于Active状态的节点,如下(需要你的电脑配置hosts记录,否则会访问不到相应的yarn节点):

Hadoop HDFS之HA集群部署 - 图13