Hadoop高可用配置详解

本文主要介绍HDFS HA特性,以及如何使用QJM(Quorum Journal Manager)特性实现HDFS HA。

背景

HDFS集群中只有一个Namenode,这就会引入单点问题;即如果Namenode故障,那么这个集群将不可用,直到Namenode重启或者其他Namenode接入。

有两种方式会影响集群的整体可用性:

  1. 意外的突发事件,比如物理机器crash,集群将不可用,直到管理员重启Namenode。

  2. 系统维护,比如软件升级等,需要关闭Namenode,也会导致集群暂时性的失效。

HDFS HA特性即解决这个问题,它通过在集群中同时运行2个(redundant)Namenodes,并让active和passive之间热备(hot standby)。当Active Namenode故障失效后,即可快速故障转移到新的Namenode上(passive Namenode);也可以在计划维护期间,基于管理员发起(administrator-inited)的友好的failover。

Architecture

在典型的HA架构中,有两个独立的机器作为Namenode,任何时刻,只有一个Namenode处于Active状态,另一个处于standby状态(passive,备份);Active Namenode用于接收Client端请求,Standy节点作为slave保持集群的状态数据以备快速failover。

为了让Standby Node与Active Node保持同步,这两个Node都与一组称为JNS的互相独立的进程保持通信(Journal Nodes)。当Active Node上更新了namespace,它将记录修改日志发送给JNS的多数派。Standby noes将会从JNS中读取这些edits,并持续关注它们对日志的变更。Standby Node将日志变更应用在自己的namespace中,当failover发生时,Standby将会在提升自己为Active之前,确保能够从JNS中读取所有的edits;即在failover发生之前,Standy持有的namespace应该与Active保持完全同步。

为了支持快速failover,Standby node持有集群中blocks的最新位置是非常必要的。为了达到这一目的,Datanodes上需要同时配置这两个Namenode的地址,同时和它们都建立心跳链接,并把block位置发送给它们。

任何时刻,只有一个Active Namenode是非常重要的,否则将会导致集群操作的混乱,那么两个Namenode将会分别有两种不同的数据状态,可能会导致数据丢失,或者状态异常,这种情况通常称为“split-brain”(脑裂,三节点通讯阻断,即集群中不同的Datanodes却看到了两个Active Namenodes)。对于JNS(Journal Nodes)而言,任何时候只允许一个Namenode作为writer;在failover期间,原来的Standby Node将会接管Active的所有职能,并负责向JNS写入日志记录,这就阻止了其他Namenode基于处于Active状态的问题。

硬件资源

为了构建HA集群架构,你需要准备如下资源:

  1. Namenode机器:两台配置对等的物理机器,它们分别运行Active和Standby Node。

  2. JouralNode机器:运行JouralNodes的机器。JouralNode守护进程相当的轻量级,它们可以和hadoop的其他进程部署在一起,比如Namenodes、jobTracker、ResourceManager等。不过为了形成多数派(majority),至少需要3个JouralNodes,因为edits操作必须在多数派上写入成功。当然JNS的个数可以 > 3,且通常为奇数(3,5,7),这样可以更好的容错和形成多数派。如果你运行了N个JNS,那么它可以允许(N-1)/2个JNS进程失效并且不影响工作。

此外,在HA集群中,standby namenode还会对namespace进行checkpoint操作(继承Backup Namenode的特性),因此,就不需要在HA集群中运行SecondaryNamenode、CheckpointNode或者BackupNode。事实上,HA架构中运行上述节点,将会出错(不允许)。

部署

想要配置高可用的NameNode,你必须为hdfs-site.xml配置文件添加多个配置项。在其中设置这些配置的顺序不重要,但你为dfs.nameservicesdfs.ha.namenodes.[nameservice ID]这两个配置项设定的值,将影响后面的部分选项。

因此,你应该在配置其它选项之前先配置这两个项目:

dfs.nameservices - 集群的新逻辑名

为你的集群逻辑名选择一个新名称,比如mycluster。并使用此罗继明为其他选项进行配置。你可以任意起名,它会被配置为HDFS的绝对路径,你在使用HDFS绝对路径时可以hdfs://mycluster/dir/file而不是像以前hdfs://namenode:port/dir/file

记住:如果你还使用了HDFS Federation(一种多NameNode的配置,不同于HA对NameNode进行冗余,HDFS Federation是对NameNode进行分割。一般用于解决NameNode中数据过多导致单机经常宕机的情况),无论你是否使用了HA,此配置项都应该包含其他集群逻辑名或其他NameNode。以逗号分割成列表作为值

和HDFS Federation一样,HA集群重用了“nameservice ID”来标识一个HDFS 实例(事实上它可能包含多个HA Namenods);此外,“NameNode ID”概念被添加到HA中,集群中每个Namenode都有一个不同的ID;为了能够让一个配置文件支持所有的Namenodes(适用与Federation环境),那么相关的配置参数都以“nameservice ID”或“Namenode ID”作为后缀。

示例:

<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
可以为任意可读字符串
dfs.ha.namenodes.[nameservice ID] - 对此集群逻辑名下的所有NameNode进行标识

以逗号分割此逻辑名下所有的NameNode标识,这将用于DataNode区分NameNode。比如:如果你用“mycluster”作为集群逻辑名,用nn1和nn2标识2个NameNode。你可以这样配置:

注意:目前一个集群逻辑名最大支持2个NameNode标识

其中“mycluster”需要和上面的nameservice ID匹配,此处我们定义“mycluster”下有2个namenode ID。

然后可以配置接下的必须项目

dfs.namenode.rpc-address.[nameservice ID].[name node ID] - 完整的RPC监听地址


对所有之前已经配置了的NameNode标识,设定完整的NameNode线程地址以及IPC端口。
请注意,两个NameNode标识要分开设定。比如:

<property>
 <name>dfs.namenode.rpc-address.mycluster.nn1</name>
 <value>machine1.example.com:8020</value>
</property>
<property>
 <name>dfs.namenode.rpc-address.mycluster.nn2</name>
 <value>machine2.example.com:8020</value>
</property>

注意:你也可以根据需要配置RPC服务地址。

dfs.namenode.http-address.[nameservice ID].[name node ID] - 完整的HTTP监听地址


与RPC地址相同,要设定每个NameNode的HTTP监听地址

比如:

<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>machine1.example.com:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>machine2.example.com:50070</value>
</property>

注意:如果你的Hadoop安全防护开启,你还应该为每一个NameNode设置类似的https-address(笔者注:注意http和https的区别,后者是启用了安全防护的地址,即使跑开Hadoop和大数据的概念不谈只说http这个技术的话,https安全地址的概念也是存在的)

dfs.namenode.shared.edits.dir - 声明了多个NameNode读写临时元数据(edits、持久元数据是fsimage)的共享URL组

这是在配置一组属于JournalNodes的地址,它提供一个用于两个NameNode临时元数据进行共享存储的空间。由提供服务的NameNode(Active NameNode)写入,并由备用的NameNode(Standby NameNode)读取,通过这样的一写一读来保证备用的NameNode中的数据是最新的。每个集群逻辑名必须配置大于等于3个JournalNode地址,并且应为单数个,这些地址应该以这样的格式书写:qjournal://host1:port1;host2:port2;host3:port3/journalID。JournalID是JournalNode组这个集群逻辑名中的唯一标识符,在使用了HDFS Federation的情况下,JournalID可以让一个JournalNode为整个HDFS Federation提供共享存储,虽然JournalID不是必须的,但它在需要多次使用JournalNode的场景中会让你方便一些。
比如,如果你想使用node1.example.com,node2.example.com,node3.example.com三台机器作为JournalNode,端口使用8485,并使用mycluster作为集群逻辑名,那么你应该如此设置。

dfs.client.failover.proxy.provider.[nameservice ID] - 这个HDFS客户端的Java类用于连接提供服务的NameNode

这个类可以帮助HDFS的客户端确定哪个NameNode是主的、正在提供服务的。正在服务的NameNode将通过它回应客户端的问询。除非你自己写了一个自定义功能了的ConfiguredFailoverProxyProvider,否则你就直接用它即可。

理想情况下,同一时间只有一个NameNode提供服务。这非常重要,因为两个NameNode其实都有写JournalNode的能力,但理想情况下只有一个提供服务的NameNode会这么做。
然而,在故障转移时就未必了。可能集群判断死亡的NameNode其实还有写JournalNode,更可怕的是它坚持认为自己仍然正常,这会出现集群中有2个NameNode同时提供服务的情况,我们称之为脑裂。这时我们就要设定一个脚本或是一个JAVA程序,在这种时候强行关闭疯了的NameNode。
为了提高系统面对脑裂的可用性,你的防脑裂脚本必须在成功后可以返回成功标志而不是什么都不做。哪怕你为了方便不使用任何的防脑裂程序或配置了没有实际作用的防脑裂程序,你仍然必须配置/bin/true来返回成功
故障转移过程中使用的防脑裂方法可以配置为一个使用回车符分割的列表,当脑裂出现后Hadoop会顺序调用直到返回成功。有两种方法给Hadoop使用:shell和sshfence。如果你想自己定义防脑裂程序,请参考org.apache.hadoop.ha.NodeFencerclass.

sshfence- 远程登录到NameNode并杀死进程

sshfence选项

sshfence选项会登录到目标节点使用fuser杀死TCP端口的监听进程,为了让这个防脑裂选项生效,它必须有能力无密码登陆到目标节点。所以你还必须配置dfs.ha.fencing.ssh.private-key-files选项,这是一个逗号分隔的ssh私钥文件列表,例如:

shell - 运行任何命令去干掉疯了的NameNode

shell选项允许运行任何命令,比如这样:

<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>
</property>

字符串()中间的部分会以/bin/bash执行,括号本身不会被传递

此shell命令在执行时会加载所有Hadoop配置的变量,使用_替换所有Hadoop配置文件中的.,比如shell脚本中可以直接使用$dfs_namenode_rpc指代目标节点的PRC地址。

此外,下列变量也可以使用

变量名含义
$target_hosthostname of the node to be fenced
$target_portIPC port of the node to be fenced
$target_addressthe above two, combined as host:port
$target_nameserviceidthe nameservice ID of the NN to be fenced
$target_namenodeidthe namenode ID of the NN to be fenced

这些环境变量也可以在配置shell的时候使用,比如:

如果shell脚本返回的退出代码为0(Linux系统中为0代表成功,1-255代表各种错误)。防脑裂脚本一定要接到成功回复才会停止,否则下一个防脑裂方法就会被继续尝试。

注意:shell选项不同于sshfence选项,它不支持超时设定。如果你需要超时设定,请在执行shell的时候进行自定义,比如到一定秒数停止。




fs.defaultFS 在不指定时,默认使用的HDFS客户端绝对路径前缀


或者你可以配置HDFS客户端的默认路径去使用新的HA逻辑URI,如果之前你使用了mycluster这样的名字作为你的集群逻辑名,这将是所有HDFS路径的共用值。在core-site.xml中你可以这样配置:

dfs.journalnode.edits.dir - JournalNode存储数据的实际路径


这个路径用于JournalNode机器存储edits或其他状态文件。你只能使用单一路径来配置此选项。因为JournalNode本就由多个。当然你也可以将这个路径远程放在一个NFS或是远程RAID阵列中。

部署细节

刚才已经配置了所有必要的配置项,你必须在相应机器上启动JournalNode daemons。
你可以使用hadoop-daemon.sh start journalnode

一旦journalnodes成功启动,首先你要同步两个NameNode节点的磁盘元数据

  • 如果你是一个新建立的HDFS集群,你应该首先在一个NameNode运行格式化命令(hdfs namenode -format)

  • 如果你已经格式化NameNode,或将一个非HA集群切换到HA模式,你应该现在拷贝你已经格式化了的NameNode的元数据目录到另一个NameNode。并且在未格式化的NameNode上运行命令hdfs namenode -bootstrapStandby。运行此命令也将确保Journalnode(如配置了dfs.namenode.shared.edits.dir)也拥有足够的edit去启动两个NameNode

  • 如果要转换一个非高可用的NameNode为高可用模式,你应该运行命令hdfs namenode -initializeSharedEdits这将根据本地NameNode的元数据目录对JournalNodes进行初始化

之后你可以打开你的两个HA-NameNode。并分别浏览其配置的http-address地址,可以看到NameNode的状态(Active或Standby)

管理命令

现在你的HA-NameNode已经配置完毕,你可以使用一些命令来管理你的HA-HDFS集群,但实际上你要记住的命令只有一条hdfs haadmin,它会显示帮助

本文描述了各个子命令的高级运用,对于每个子命令的更具体使用信息,你可以运行hdfs haadmin -help <command>

  • transitionToActive和transitionToStandby -令某一个NameNode转换成你指定的状态
  • failover -故障转移

    此子命令将使第一个NameNode的Active状态转移到第二个NameNode上。如果第一个NameNode未提供服务,那么该命令只是简单地令第二个NameNode开始提供服务并不报错。
    但如果第一个NameNode处于提供服务的状态下,命令将尝试让第一个NameNode变为备用状态并将第二个NameNode变为提供服务状态。
    如果这种转变失败,将进行防脑裂程序试图干掉第一个NameNode进程,此防脑裂过程将依次尝试直到返回成功
    如果防脑裂进程超时且仍未成功,第二个NameNode不会转为提供服务状态,并会返回错误。

  • getServiceState -确认指定的NameNode处于提供服务状态还是备用状态

    连接到所指定的NameNode,以确定其当前状态,无论其处于“备用状态”或“提供服务状态”都打印其状态到标准输出。该子命令可用于计划任务或需要根据NameNode状态来进行判断的监控脚本。

  • checkHealth -检查指定NameNode的健康情况

    连接到指定的NameNode检查其健康状况。NameNode有能力执行一些对自身的诊断,包括内部服务的运行状态是否符合预期。
    如果NameNode的是健康的,此命令将返回0。
    否则便不为0。有时可以使用这个命令来进行监控。

    注意:这尚且不可行,除非指定NameNode完全停止,否则(譬如脑裂等异常情况)仍会返回成功

    自动故障转移

以上的配置属于手动故障转移。在这种模式下,即使原本提供服务的NameNode进程异常,系统仍不会自动切换两个NameNode之间的状态。

接下来将讲解如何配置自动故障转移

自动故障转移增加了两个新的组件:

  1. ZooKeeper集群
  2. ZKFailoverController(简写为ZKFC)

Apache ZooKeeper维持少量的协调数据,通知客户端数据改变,并监视连接是否失败。

自动故障转移依赖于ZooKeeper的以下几点:

  1. 故障检测:每一个NameNode都将与ZooKeeper维持一个长连接,当NameNode停止服务或其他异常时,这个长连接随着NameNode的失去作用而断开,在连接断开后ZooKeeper将通知其他NameNode应开始故障转移
  2. NameNode选举:ZooKeeper提供了一个简单的机制来专门选举出一个节点作为主节点,如果当前激活的NameNode崩溃,ZooKeeper中的一系列特殊排他锁将指示另一个节点应该成为下一个提供服务的NameNode

    ZKFailoverController(ZKFC)是一个新的组件,它是一个ZooKeeper客户端,驻留在NameNode所在机器上监控NameNode的状态。凡是有NameNode的机器都应运行ZKFC。

  3. 健康监控:ZKFC通过一系列基础健康检查命令ping本机的NameNode,如果NameNode可以及时响应,那么就认为它是健康的,如果节点崩溃、冻结或以其他方式进入异常状态,ZFKC会将其标记为不健康状态

  4. ZooKeeper连接管理:当本地NameNode被认为是健康的,ZKFC会保持一个对ZooKeeper的连接。如果本地的NameNode是正在提供服务的,它还会在ZooKeeper的Znode中保持一个。该锁依赖长连接存在,如果长连接断开,这个锁会被自动删除。

  5. ZooKeeper基础选举:如果本地NameNode是健康的,而ZKFC认为没有其它NameNode持有锁,那么它会尝试获取锁。如果成功的话,它会负责运行故障转移,并将激活本机的NameNode使其开始提供服务。

注意:有关自动故障切换的设计更多详细信息,请参阅连接到HDFS-2185上的Apache HDFS JIRA的设计文档。

配置ZooKeeper

典型部署中,ZooKeeper会被配置在3-5个节点上运行,由于ZooKeeper本身需求的资源轻量,可以将它布置在两个NameNode所在的机器上。
许多公司选择将第三个ZooKeeper节点部署在ResourceManager上。此外还有个明智的做法是将ZooKeeper节点上存储的HDFS元数据放在另外一块单独的物理磁盘中

本文档不讲解如何安装配置ZooKeeper。请查看ZooKeeper三节点建议安装

事实上,之所以将这篇文档和Hadoop简单单点安装分开来而不是从一开始就是一个HA集群,就是不希望大家将制作HA定位在一个初级阶段的步骤——HA的制作应该在你尝试了Hadoop的大部分安装组件之后的一个进阶探索,绝不该是个基础配置

开始之前

在开始切换到自动故障转移之前,请关闭集群!

配置自动故障转移

配置自动故障转移需要在你的hdfs-site.xml中再加入一个配置项

同时在你的yarn-site.xml中也再加入一个配置项

这个列表列出了ZooKeeper主机和其对应的端口

与之前相同,这些基础配置项可以根据集群逻辑名来作为配置项的后缀。比如你的集群启用了HDFS-Federation,你可以通过设置dfs.ha.automatic-failover.enabled.my-nameservice-id来明确的为某一组NameNode来开启故障切换

也有几个参数可以分别用于控制自动故障切换的具体行为,但他们并不是必须项,更详细的请参阅具体配置文档。

为ZooKeeper初始化HA状态

在任一NameNode主机中这样做

hdfs zkfc -formatZK

这将在ZooKeeper的Znode中创建自动故障切换所需的数据

开启集群

$HADOOP_HOME/sbin/start-all.sh

设定ZooKeeper管理员 {#toc_22}

如果你希望运行一个安全的集群,你可能会希望自己的ZooKeeper这么重要的组件不会被轻易登陆更改。

那么设定一个admin账户是必要的,请修改core-site.xml文件:

请注意,这些@打头的部分指代路径开始的文件,而非固定的'@'。

配置低一个文件指定ZooKeeper管理员认证的列表,在zkClient中使用这样的格式:

其中,hdfs-zkfcs是ZooKeeper唯一的用户名。mypassword是唯一的密码。

接下来,生成对应于此验证的ZooKeeper ACL,使用如下命令:

复制并粘贴在此之后输出的部分- >字符串到文件zk-acls.txt,使用前缀字符digest。 例如:

为了让这些ACL生效,你应该重新格式化hdfs zkfc -formatZK

验证故障转移

你已经完成了自动故障转移配置,现在你应该对此进行测试。

首先找到进行服务的NameNode。你可以到它们各自的WEB页面上去看,如果你和上面配置的端口相同,那会是50070端口。正在服务的NameNode会是Active状态,不在服务的会是Standby状态。

一旦你已经找到了你的Active NameNode,你就可以用一些手段让它失效。例如,你可以使用kill -9 <namenode的PID>来模拟JVM崩溃。或者,你可以关机或拔下机器的网络接口来模拟不同类型的中断。完成这些操作后,理想状态下另一个Standby NameNode会自动在几秒钟内变成Active状态。其起效的时间根据ha.zookeeper.session-timeout.ms这个配置项来设置,但默认为5秒

如果测试不成功,你可能遇到了错误。检查日志的zkfc守护进程以及节点守护进程为了进一步诊断问题。或是在下面回帖,我会尽可能想办法帮助你。

自动故障转移的常见问题

  • 开启NameNode和ZKFC的顺序重要吗?

    不,但最好先启动NameNode,否则zkfc会认为这个NameNode失效了进行故障恢复。但终归不会造成大影响(前提是你的防脑裂程序不会把这台机器关机什么的,事实上很多防脑裂方法会关掉这台机器,在Linux的高可用集群比如RHCS或Keepalived中很常见)

  • 额外的监控放在哪台机器上好

    你应该在ZKFC运行的机器上再添加额外的进程监控,以保证ZKFC不会出现意外。当然ZKFC本身不会轻易挂掉,但无论如何它挂掉的后果我们无法接受。

  • 如果Zookeeper挂掉了会发生什么?

其实也不会发生什么不可挽回的事情,你的HDFS还会正常运行。但一旦这台机器的NameNode挂掉了,将不会故障转移。这意味着你的集群将停止服务,这在很多运维情况下是不可接受的。当然,仅仅是停止服务而已,不会造成无法挽回的结果。

  • 我可以指定某个NameNode优先级更高更容易成为主节点吗?

抱歉,并不可以。谁先启动谁就是主节点。

  • 如何手动进行故障转移?

即使你使用了自动故障转移,fairover命令仍然是有效的。

在HA开启的情况下进行HDFS升级/升级确认/回滚

在HDFS的版本间进行切换,有时较新的版本可以简单的安装并重启集群即可。但有时升级HDFS需要更改硬盘上的数据(大版本升级时可能遇到)。在这种情况下必须在升级后使用HDFS Upgrade/Finalize/Rollback操作。在HA环境下这个过程更加复杂,因为NN依赖的元数据分布在多台机器上,并且在JournalNode中存储以及ZooKeeper中有注册。接下来将讲解在HA状态下进行HDFS升级/升级确认/回滚的步骤:

要执行HA升级,你必须做到以下几点:

  1. 关闭所有NameNode
  2. 启动所有JournalNode,这非常重要,一定要保证所有JournalNode都是开启的
  3. 在任意NameNode节点上使用-upgrade命令
  4. 开始后,这个NameNode不会进入StandBy状态,而是会立刻进入工作状态并进行元数据的升级工作
  5. 此时其他NameNode的HA将出于对正在升级的NameNode的同步,而你应该在另一个NameNode中运行-bootstrapStandby来同步数据。

注意:你应该让集群正常跑一下,看有没有任何异常,再进行确定或回滚

要完成一次HA情况下的升级

在NameNode启动且存在Active NameNOde的情况下,你要使用hdfs dfsadmin -finalizeUpgrade命令来完成升级,主NameNode会清理以前的过期Fsimage元数据。

要回滚放弃这次升级

首先你要关闭所有的NameNode,并在运行了-upgrade命令的NameNode机器上运行-rollback命令,接下来将显示本地硬盘哪里被回滚了以及对其他地方元数据的回滚。最后,你还要运行-bootstrapStandby来让另一个NameNode也获得回滚后的数据。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值