在典型的 HA 集群中,通常有两台不同的机器充当 NN。在任何时间,只有一台机器处于 Active 状态;另一台机器是处于 Standby 状态。Active NN 负责集群中所有客户端的操作; 而 Standby NN 主要用于备用,它主要维持足够的状态,如果必要,可以提供快速的故障恢 复。 为了让 Standby NN 的状态和 Active NN 保持同步,即元数据保持一致,它们都将会和 JournalNodes 守护进程通信。当 Active NN 执行任何有关命名空间的修改,它需要持久化到 一半以上的 JournalNodes 上(通过 edits log 持久化存储),而 Standby NN 负责观察 edits log 的变化,它能够读取从 JNs 中读取 edits 信息,并更新其内部的命名空间。一旦 Active NN 出现故障,Standby NN 将会保证从 JNs 中读出了全部的 Edits,然后切换成 Active 状态。 Standby NN 读取全部的 edits 可确保发生故障转移之前,是和 Active NN 拥有完全同步的命 名空间状态。 为了提供快速的故障恢复,Standby NN 也需要保存集群中各个文件块的存储位置。为 了实现这个,集群中所有的 Database 将配置好 Active NN 和 Standby NN 的位置,并向它们 发送块文件所在的位置及心跳,如下图所示:
在任何时候,集群中只有一个 NN 处于 Active 状态是极其重要的。否则,在两个 Active NN 的状态下 NameSpace 状态将会出现分歧,这将会导致数据的丢失及其它不正确的结果。为 了保证这种情况不会发生,在任何时间,JNs 只允许一个 NN 充当 writer。在故障恢复期 间,将要变成 Active 状态的 NN 将取得 writer 的角色,并阻止另外一个 NN 继续处于 Active 状态。 为了部署 HA 集群,你需要准备以下事项: (1)、NameNode machines:运行 Active NN 和 Standby NN 的机器需要相同的硬件配 置; (2)、JournalNode machines:也就是运行 JN 的机器。JN 守护进程相对来说比较轻 量,所以这些守护进程可以可其他守护线程(比如 NN,YARN ResourceManager)运行在 同一台机器上。在一个集群中,最少要运行 3 个 JN 守护进程,这将使得系统有一定的容错 能力。当然,你也可以运行 3 个以上的 JN,但是为了增加系统的容错能力,你应该运行奇 数个 JN(3、5、7 等),当运行 N 个 JN,系统将最多容忍(N-1)/2 个 JN 崩溃。 在 HA 集群中,Standby NN 也执行 namespace 状态的 checkpoints,所以不必要运行 Secondary NN、CheckpointNode 和 BackupNode;事实上,运行这些守护进程是错误的。
关闭之前的服务,并清理环境 [hadoop@hadoop1 hadoop]$ sbin/stop-yarn.sh [hadoop@hadoop1 hadoop]$ sbin/stop-dfs.sh [hadoop@hadoop1 hadoop]$ jps 1543 Jps ##1-4hadoop都执行 rm -fr /tmp/* Zookeeper 集群至少三台,总节点数为奇数个 安装 JDK 安装 zookeeper [hadoop@hadoop2 ~]$ tar zxf zookeeper-3.4.9.tar.gz 添加从节点信息 server.1=172.25.254.2:2888:3888 server.2=172.25.254.3:2888:3888 server.3=172.25.254.4:2888:3888各节点配置文件相同,并且需要在新建的/tmp/zookeeper 目录中创建 myid 文件,写入 一个唯一的数字,取值范围在 1-255。比如:172.25.0.2 节点的 myid 文件写入数 字“1”,此数字与配置文件中的定义保持一致,(server.1=172.25.0.2:2888:3888 )其它节点依次类推。
[hadoop@hadoop2 ~]$ mkdir /tmp/zookeeper [hadoop@hadoop2 ~]$ echo 1 > /tmp/zookeeper/myid [hadoop@hadoop2 ~]$ mkdir /tmp/zookeeper [hadoop@hadoop2 ~]$ echo 1 > /tmp/zookeeper/myid [hadoop@hadoop4 ~]$ mkdir /tmp/zookeeper [hadoop@hadoop4 ~]$ echo 3 > /tmp/zookeeper/myid 在各节点启动服务: [hadoop@hadoop2 zookeeper-3.4.9]$ bin/zkServer.sh start ZooKeeper JMX enabled by default Using config: /home/hadoop/zookeeper-3.4.9/bin/../conf/zoo.cfg Starting zookeeper ... STARTED [hadoop@hadoop3 zookeeper-3.4.9]$ bin/zkServer.sh start [hadoop@hadoop4 zookeeper-3.4.9]$ bin/zkServer.sh start [hadoop@hadoop2 zookeeper-3.4.9]$ bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /home/hadoop/zookeeper-3.4.9/bin/../conf/zoo.cfg Mode: follower [hadoop@hadoop3 zookeeper-3.4.9]$ bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /home/hadoop/zookeeper-3.4.9/bin/../conf/zoo.cfg Mode: leader [hadoop@hadoop4 zookeeper-3.4.9]$ bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /home/hadoop/zookeeper-3.4.9/bin/../conf/zoo.cfg Mode: follower 连接 zookeeper [hadoop@hadoop2 zookeeper-3.4.9]$ bin/zkCli.sh [zk: localhost:2181(CONNECTED) 0] help ZooKeeper -server host:port cmd args stat path [watch] set path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val path history redo cmdno printwatches on|off delete path [version] sync path listquota path rmr path get path [watch] create [-s] [-e] path data acl addauth scheme auth quit getAcl path close connect host:port [zk: localhost:2181(CONNECTED) 1] get /zookeeper/quota cZxid = 0x0 ctime = Wed Dec 31 19:00:00 EST 1969 mZxid = 0x0 mtime = Wed Dec 31 19:00:00 EST 1969 pZxid = 0x0 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 0 配置参数详解:clientPort 客户端连接 server 的端口,即对外服务端口,一般设置为 2181 吧。 dataDir 存储快照文件 snapshot 的目录。默认情况下,事务日志也会存储在这里。建议同时配置参 数 dataLogDir, 事务日志的写性能直接影响 zk 性能。 tickTime ZK 中的一个时间单元。ZK 中所有时间都是以这个时间单元为基础,以毫秒计,用来调节 心跳和超时。例如,session 的最小超时时间是 2*tickTime。 dataLogDir 事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升 ZK 性能。 (No Java system property) globalOutstandingLimit 最大请求堆积数。默认是 1000。ZK 运行的时候, 尽管 server 已经没有空闲来处理更多的 客户端请求了,但是还是允许客户端将请求提交到服务器上来,以提高吞吐性能。当然, 为了防止 Server 内存溢出,这个请求堆积数还是需要限制下的。 (Java system property:zookeeper.globalOutstandingLimit.) preAllocSize 预先开辟磁盘空间,用于后续写入事务日志。默认是 64M,每个事务日志大小就是 64M。 如 果 ZK 的 快 照 频 率 较 大 的 话 , 建 议 适 当 减 小 这 个 参 数 。 (Java systemproperty:zookeeper.preAllocSize) snapCount 每进行 snapCount 次事务日志输出后,触发一次快照(snapshot), 此时,ZK 会生成一个 snapshot.文件,同时创建一个新的事务日志文件 log.。默认是 100000.(真正的代码实现 中,会进行一定的随机数处理,以避免所有服务器在同一时间进行快照而影响性能)(Java system property:zookeeper.snapCount) traceFile 用于记录所有请求的 log,一般调试过程中可以使用,但是生产环境不建议使用,会严重影 响性能。(Java system property:? requestTraceFile) maxClientCnxns 单个客户端与单台服务器之间的连接数的限制,是 ip 级别的,默认是 60,如果设置为 0, 那么表明不作任何限制。请注意这个限制的使用范围,仅仅是单台客户端机器与单台 ZK 服务器之间的连接数限制,不是针对指定客户端 IP,也不是 ZK 集群的连接数限制,也不 是单台 ZK 对所有客户端的连接数限制。指定客户端 IP 的限制策略,这里有一个 patch,可 以尝试一下:http://rdc.taobao.com/team/jm/archives/1334(No Java system property) clientPortAddress 对于多网卡的机器,可以为每个 IP 指定不同的监听端口。默认情况是所有 IP 都监听 clientPort 指定的端口。 New in 3.3.0 minSessionTimeoutmaxSessionTimeout Session 超时时间限制,如果客户端设置的超时时间不在这个范围,那么会被强制设置为最 大或最小时间。默认的 Session 超时时间是在 2 * tickTime ~ 20 * tickTime 这个范围 New in 3.3.0 fsync.warningthresholdms 事务日志输出时,如果调用 fsync 方法超过指定的超时时间,那么会在日志中输出警告信 息。默认是 1000ms。(Java system property: fsync.warningthresholdms)New in 3.3.4 autopurge.purgeInterval 在上文中已经提到,3.4.0 及之后版本,ZK 提供了自动清理事务日志和快照文件的功能, 这个参数指定了清理频率,单位是小时,需要配置一个 1 或更大的整数,默认是 0,表示不 开启自动清理功能。(No Java system property) New in 3.4.0 autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留 3 个。(No Java system property) New in 3.4.0 electionAlg 在之前的版本中, 这个参数配置是允许我们选择 leader 选举算法,但是由于在以后的版本 中,只会留下一种“ TCP-based version of fast leader election” 算法,所以这个参数目前看来没 有用了,这里也不详细展开说了。(No Java system property) initLimit Follower 在启动过程中,会从 Leader 同步所有最新数据,然后确定自己能够对外服务的起 始状态。Leader 允许 F 在 initLimit 时间内完成这个工作。通常情况下,我们不用太在意这 个参数的设置。如果 ZK 集群的数据量确实很大了,F 在启动的时候,从 Leader 上同步数据 的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了。(No Java system property) syncLimit 在运行过程中,Leader 负责与 ZK 集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果 L 发出心跳包在 syncLimit 之后,还没有从 F 那里收到响应, 那么就认为这个 F 已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一 些问题。(No Java system property) leaderServes 默认情况下,Leader 是会接受客户端连接,并提供正常的读写服务。但是,如果你想让 Leader 专注于集群中机器的协调,那么可以将这个参数设置为 no,这样一来,会大大提高 写操作的性能。(Java system property: zookeeper.leaderServes)。 server.x=[hostname]:nnnnn[:nnnnn] 这里的 x 是一个数字,与 myid 文件中的 id 是一致的。右边可以配置两个端口,第一个端口 用于 F 和 L 之间的数据同步和其它通信,第二个端口用于 Leader 选举过程中投票通信。 (No Java system property) group.x=nnnnn[:nnnnn]weight.x=nnnnn 对机器分组和权重设置,可以 参见这里(No Java system property) cnxTimeout Leader 选举过程中,打开一次连接的超时时间,默认是 5s。(Java system property: zookeeper. cnxTimeout) zookeeper.DigestAuthenticationProvider.superDigest ZK 权 限 设 置 相 关 , 具 体 参 见 《 使 用 super 身 份 对 有 权 限 的 节 点 进 行 操 作 》 和 《 ZooKeeper 权限控制》 skipACL 对所有客户端请求都不作 ACL 检查。如果之前节点上设置有权限限制,一旦服务器上打开 这个开头,那么也将失效。(Java system property: zookeeper.skipACL) forceSync 这个参数确定了是否需要在事务日志提交的时候调用 FileChannel.force 来保证数据完全同步 到磁盘。(Java system property: zookeeper.forceSync) jute.maxbuffer 每个节点最大数据量,是默认是 1M。这个限制必须在 server 和 client 端都进行设置才会生 效。(Java system property: jute.maxbuffer)
Hadoop 配置 [hadoop@hadoop1 hadoop]$ vim core-site.xml <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://masters</value> #指定 hdfs 的 namenode 为 masters (名称可自定义) </property> <property> <name>ha.zookeeper.quorum</name> <value>172.25.254.2:2181,172.25.254.3:2181,172.25.254.4:2181</value> #指定 zookeeper 集群主机地址 </property> </configuration> [hadoop@hadoop1 hadoop]$ vim hdfs-site.xml <configuration> <!-- 指定 hdfs 的 nameservices 为 masters,和 core-site.xml 文件中的设置保持一 致 --> <property> <name>dfs.replication</name> <value>3</value> </property> <!-- masters 下面有两个 namenode 节点,分别是 h1 和 h2 (名称可自定义) --> <property> <name>dfs.nameservices</name> <value>masters</value> </property> <!-- 指定 h1 节点的 rpc 通信地址 --> <property> <name>dfs.ha.namenodes.masters</name> <value>h1,h2</value> </property> <!-- 指定 h1 节点的 http 通信地址 --> <property> <name>dfs.namenode.rpc-address.masters.h1</name> <value>172.25.254.1:9000</value> </property> <property> <name>dfs.namenode.http-address.masters.h1</name> <value>172.25.254.1:50070</value> </property> <!-- 指定 h2 节点的 rpc 通信地址 --> <property> <name>dfs.namenode.rpc-address.masters.h2</name> <value>172.25.254.5:9000</value> </property> <!-- 指定 h2 节点的 http 通信地址 --> <property> <name>dfs.namenode.http-address.masters.h2</name> <value>172.25.254.5:50070</value> </property> <!-- 指定 NameNode 元数据在 JournalNode 上的存放位置 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://172.25.254.2:8485;172.25.254.3:8485;172.25.254.4:8485/masters</value> </property> <!-- 指定 JournalNode 在本地磁盘存放数据的位置 --> <property> <name>dfs.journalnode.edits.dir</name> <value>/tmp/journaldata</value> </property> <!-- 开启 NameNode 失败自动切换 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- 配置失败自动切换实现方式 --> <property> <name>dfs.client.failover.proxy.provider.masters</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 免密码 -->v <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/home/hadoop/.ssh/id_rsa</value> </property> <!-- 配置 sshfence 隔离机制超时时间 --> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property> </configuration> 启动 hdfs 集群(按顺序启动) 在三个 DN 上依次启动 zookeeper 集群 [hadoop@hadoop4 zookeeper-3.4.9]$ bin/zkServer.sh start ZooKeeper JMX enabled by default Using config: /home/hadoop/zookeeper-3.4.9/bin/../conf/zoo.cfg Starting zookeeper ... already running as process 1057. [hadoop@hadoop4 zookeeper-3.4.9]$ jps 1057 QuorumPeerMain 1134 Jps 在三个 DN 上依次启动 journalnode(第一次启动 hdfs 必须先启动 journalnode) [hadoop@hadoop2 hadoop]$ sbin/hadoop-daemon.sh start journalnode [hadoop@hadoop3 hadoop]$ sbin/hadoop-daemon.sh start journalnode [hadoop@hadoop4 hadoop]$ sbin/hadoop-daemon.sh start journalnode [hadoop@hadoop4 hadoop]$ jps 1057 QuorumPeerMain 1315 JournalNode 1354 Jps 格式化 HDFS 集群 [hadoop@hadoop1 hadoop]$ bin/hdfs namenode -format Namenode 数据默认存放在/tmp,需要把数据拷贝到 h2 $ scp -r /tmp/hadoop-hadoop 172.25.254.5:/tmp 格式化 zookeeper (只需在 h1 上执行即可) [hadoop@hadoop1 hadoop]$ bin/hdfs zkfc -formatZK 启动 hdfs 集群(只需在 h1 上执行即可) [hadoop@hadoop1 hadoop]$ sbin/start-dfs.sh Starting namenodes on [server1 server5] Starting datanodes Starting journal nodes [172.25.254.2 172.25.254.3 172.25.254.4] Starting ZK Failover Controllers on NN hosts [server1 server5] [hadoop@hadoop1 hadoop]$ jps 3136 NameNode 3488 DFSZKFailoverController 3538 Jps [hadoop@hadoop5 hadoop]$ jps 1506 NameNode 1650 Jps 1593 DFSZKFailoverController [hadoop@hadoop2 ~]$ jps 1872 Jps 1716 DataNode 1800 JournalNode 1081 QuorumPeerMain浏览器测试显示1上是active,5是standby
测试故障自动切换
[hadoop@hadoop1 hadoop]$ jps 3136 NameNode 3488 DFSZKFailoverController 3594 Jps [hadoop@hadoop1 hadoop]$ kill -9 3136 [hadoop@hadoop1 hadoop]$ jps 3488 DFSZKFailoverController 3605 Jps杀掉 h1 主机的 namenode 进程后依然可以访问,此时 h2 转为 active 状态接 管 namenode [hadoop@hadoop1 hadoop]$ sbin/hadoop-daemon.sh start namenode 启动 h1 上的 namenode,此时为 standby 状态。