Hadoop学习笔记(3)-HA高可用集群搭建

概述

  HA(High Available), 高可用性集群,是保证业务连续性的有效解决方案,一般有两个或两个以上的节点,且分为活动节点及备用节点。通常把正在执行业务的称为活动节点,而作为活动节点的一个备份的则称为备用节点。当活动节点出现问题,导致正在运行的业务(任务)不能正常运行时,备用节点此时就会侦测到,并立即接续活动节点来执行业务。从而实现业务的不中断或短暂中断。

  在hadoop2.0之前,每个集群只有一个NameNode,如果那台机器坏掉,集群作为一个整体将不可用,所以为了解决这个问题Hadoop2.0引入了HA机制,可以通过在同一集群上配置运行两个冗余的NameNodes,做到主动/被动的热备份。这将允许当一个机器宕机时,快速转移到一个新的NameNode,或管理员进行利用故障转移达到优雅的系统升级的目的。

  HA一共有二种解决方案,一种是NFS(Network File System)方式,另外一种是QJM(Quorum Journal Manager)方式。

HA架构

  一个典型的HA集群,NameNode会被配置在两台独立的机器上.在任何的时间上,一个NameNode处于活动状态,而另一个在备份状态,活动状态的NameNode会响应集群中所有的客户端,同时备份的只是作为一个副本,保证在必要的时候提供一个快速的转移。

  为了使备份的节点和活动的节点保持一致,两个节点通过一个特殊的守护线程相连,这个线程叫做“JournalNodes”(JNs)。当活动状态的节点修改任何的命名空间,他都会通过这些JNs记录日志,备用的节点可以监控edit日志的变化,并且通过JNs读取到变化。备份节点查看edits可以拥有专门的namespace。在故障转移的时候备份节点将在切换至活动状态前确认他从JNs读取到的所有edits。这个确认的目的是为了保证Namespace的状态和迁移之前是完全同步的。

  为了提供一个快速的转移,备份NameNode要求保存着最新的block在集群当中的信息。为了能够得到这个,DataNode都被配置了所有的NameNode的地址,并且发送block的地址信息和心跳给两个node。

  保证只有一个活跃的NameNode在集群当中是一个十分重要的一步。否则namespace状态在两个节点间不同会导致数据都是或者其他一些不正确的结果。为了确保这个,防止所谓split - brain场景,JournalNodes将只允许一个NameNode进行写操作。故障转移期间,NameNode成为活跃状态的时候会接管JournalNodes的写权限,这会有效防止其他NameNode持续处于活跃状态,允许新的活动节点安全进行故障转移。

搭建HA集群

  hadoop-2.2.0中依然存在一个问题,就是ResourceManager只有一个,存在单点故障,hadoop-2.4.1解决了这个问题,可以有两个ResourceManager,一个是Active,一个是Standby,状态由zookeeper进行协调。

测试集群规划

  实验使用7台虚拟机,规划如下:

HostName IP Software Process
datanode01 192.168.145.140 jdk、hadoop、zookeeper DataNode、NodeManager、JournalNode、QuorumPeerMain
datanode02 192.168.145.141 jdk、hadoop、zookeeper DataNode、NodeManager、JournalNode、QuorumPeerMain
datanode03 192.168.145.142 jdk、hadoop、zookeeper DataNode、NodeManager、JournalNode、QuorumPeerMain
namenode01 192.168.145.143 jdk、hadoop NameNode、DFSZKFailoverController(ZKFC)
namenode02 192.168.145.144 jdk、hadoop NameNode、DFSZKFailoverController(ZKFC)
yarn01 192.168.145.145 jdk、hadoop ResourceManager
yarn02 192.168.145.146 jdk、hadoop ResourceManager

ssh免密登陆

  • namenode01需要配置所有datanode、yarn、namenode的免密登陆。
  • namenode02需要配置namenode01的免密登陆。
  • yarn01需要配置所有nodemanager与resourcemanager的免密登陆。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#在namenode01上生成密匙
ssh-keygen
#namenode01拷贝密匙(包括自己)
ssh-copy-id namenode01
ssh-copy-id namenode02
ssh-copy-id datanode01
ssh-copy-id datanode02
ssh-copy-id datanode03
ssh-copy-id yarn01
ssh-copy-id yarn02
#在namenode02上生成密匙
ssh-keygen
ssh-copy-id namenode01
ssh-copy-id namenode02
#在yarn01上生成密匙
ssh-keygen
#yarn01拷贝密匙
ssh-copy-id yarn02
ssh-copy-id datanode01
ssh-copy-id datanode02
ssh-copy-id datanode03

安装zookeeper

  1. 解压zookeeper
  2. 重命名zookeeper/conf下的zoo_sample.cfg为zoo.cfg : mv zoo_sample.cfg zoo.cfg
  3. 在zoo.cfg中修改dataDir=$ZOOKEEPERHOME/data 这个文件需要自己创建
    例如:dataDir=/home/application/zookeeper-3.4.5/data
  4. 在zoo.cfg中最后添加 server.id=ip:2888:3888
    例如:
    server.1=datanode01:2888:3888
    server.2=datanode02:2888:3888
    server.3=datanode03:2888:3888
    
  5. 在$ZOOKEEPERHOME/data目录中创建一个myid文件并写入id。
    例如: echo 1 >/home/application/zookeeper-3.4.5/data/myid
    id需要跟zoo.cfg中配置的一致。

core-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<configuration>
<!-- 指定hdfs的nameservice为ns1 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://ns1/</value>
</property>
<!-- 指定hadoop临时目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/home/application/hadoop-2.6.0/tmp</value>
</property>
<!-- 指定zookeeper地址 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>datanode01:2181,datanode02:2181,datanode03:2181</value>
</property>
</configuration>

hdfs-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<configuration>
<!--指定hdfs的nameservice为ns1,需要和core-site.xml中的保持一致.
这个名字是逻辑名字,可以是任意的,它将被用来配置在集群中作为HDFS的绝对路径组件。-->
<property>
<name>dfs.nameservices</name>
<value>ns1</value>
</property>
<!-- ns1下面有两个NameNode,分别是nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.ns1</name>
<value>nn1,nn2</value>
</property>
<!-- nn1的RPC通信地址 -->
<property>
<name>dfs.namenode.rpc-address.ns1.nn1</name>
<value>namenode01:9000</value>
</property>
<!-- nn1的http通信地址 -->
<property>
<name>dfs.namenode.http-address.ns1.nn1</name>
<value>namenode01:50070</value>
</property>
<!-- nn2的RPC通信地址 -->
<property>
<name>dfs.namenode.rpc-address.ns1.nn2</name>
<value>namenode02:9000</value>
</property>
<!-- nn2的http通信地址 -->
<property>
<name>dfs.namenode.http-address.ns1.nn2</name>
<value>namenode02:50070</value>
</property>
<!-- 指定NameNode的元数据在JournalNode上的存放位置 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://datanode01:8485;datanode02:8485;datanode03:8485/ns1</value>
</property>
<!-- 指定JournalNode在本地磁盘存放数据的位置 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/application/hadoop-2.6.0/journaldata</value>
</property>
<!-- 开启NameNode失败自动切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- 配置失败自动切换实现方式 -->
<property>
<name>dfs.client.failover.proxy.provider.ns1</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 配置隔离机制方法,多个机制用换行分割,即每个机制暂用一行-->
<property>
<name>dfs.ha.fencing.methods</name>
<value>
sshfence
shell(/bin/true)
</value>
</property>
<!-- 使用sshfence隔离机制时需要ssh免登陆 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<!-- 配置sshfence隔离机制超时时间 -->
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
</configuration>

mapred-site.xml

1
2
3
4
5
6
7
<configuration>
<!-- 指定mr框架为yarn方式 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>

yarn-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<configuration>
<!-- 开启RM高可用 -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 指定RM的cluster id 这是一个逻辑名称,可以是任意的 -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>yarncluster</value>
</property>
<!-- 指定RM的名字 -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- 分别指定RM的地址 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>yarn01</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>yarn02</value>
</property>
<!-- 指定zk集群地址 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>datanode01:2181,datanode02:2181,datanode03:2181</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
</configuration>

slaves

  slaves指定子节点(DataNode)位置,因为yarn与HDFS分开启动,所以在yarn01中slaves指定的是NodeManager的位置。

启动HA集群

  1. 启动zookeeper集群
  2. 启动JournalNode,一旦JNs启动,必须进行一次初始化同步在两个HA的NameNode,主要是为了元数据。
    sbin/hadoop-daemon.sh start journalnode
  3. 格式化HDFS。
    在namenode01上执行命令 hdfs namenode -format
    格式化后会在根据core-site.xml中的hadoop.tmp.dir配置生成个文件,为了同步元数据,需要将tmp文件夹copy到namenode02上。
    scp -r tmp/ namenode02:/home/application/hadoop-2.6.0/
    也可以使用命令 hdfs namenode -bootstrapStandby
  4. 格式化ZKFC
    在namenode01上执行命令 hdfs zkfc -formatZK

  5. 启动HDFS
    在namenode01上执行命令 sbin/start-dfs.sh

  6. 启动Yarn
    在yarn01上执行命令 sbin/start-yarn.sh。

  7. 因为自带的start-yarn.sh脚本并不会远程启动第二个RM,所以需要在yarn02上单独启动一个RM。
    在yarn02上执行命令 sbin/yarn-daemon.sh start resourcemanager

管理命令

1
2
3
4
5
6
7
Usage: DFSHAAdmin [-ns <nameserviceId>]
[-transitionToActive <serviceId>]
[-transitionToStandby <serviceId>]
[-failover [--forcefence] [--forceactive] <serviceId> <serviceId>]
[-getServiceState <serviceId>]
[-checkHealth <serviceId>]
[-help <command>]

  描述了常用的命令,每个子命令的详细信息你应该运行”hdfs haadmin -help “.

transitionToActive && transitionToStandby

  切换NameNode的状态(Active或者Standby),这些子命令会使NameNode分别转换状态。

failover

  启动两个NameNode之间的故障迁移。

  这个子命令会从第一个NameNode迁移到第二个,如果第一个NameNode处于备用状态,这个命令只是没有错误的转换第二个节点到活动状态。如果第一个NameNode处于活跃状态,试图将优雅地转换到备用状态。如果失败,过滤方法(如由dfs.ha.fencing.methods配置)将尝试过滤直到成功。只有在这个过程之后第二个NameNode会转换为活动状态,如果没有过滤方法成功,第二个nameNode将不会活动并返回一个错误。

getServiceState

  连接到NameNode,去判断现在的状态打印“standby”或者“active”去标准的输出。这个子命令可以被corn jobs或者是监控脚本使用,为了针对不同状态的NameNode采用不同的行为。

checkHealth

  连接NameNode检查健康,NameNode能够执行一些诊断,包括检查如果内部服务正在运行。如果返回0表明NameNode健康,否则返回非0.可以使用此命令用于监测目的。

  注意:这个功能实现的不完整,目前除了NameNode完全的关闭,其他全部返回成功。

分享