Apache_Hadoop_3.2.1-HDFS-04NameNode-HA-With-QJM
以下内容均翻译自官方文档。
目的
本指南概述了HDFS 高可用特性(HA),以及如何使用QJM(Quorum Journal Manager)功能配置和管理一个高可用的HDFS集群。
本文档假定读者对HDFS集群的组件和节点类型有一定的了解。如要了解细节可以参考HDFS架构章节。
注意:使用QJM或常规共享服务器
本指南讨论如何通过QJM配置和使用HDFS HA在激活和待机Namenode之间共享edit log。关于如何通过使用NFS而不是QJM进行共享存储来配置HDFS HA,可以参加另一章节: NameNode-HA-With-NFS。关于如何使用观察者namenode来配置HDFS HA,参见…。
背景:
在 Hadoop 2.0.0 之前,NameNode 容易导致 HDFS 群集单点故障 (SPOF)。每个群集都只有一个 NameNode,如果该计算机或进程不可用,则群集作为一个整体将不可用,直到 NameNode 重新启动或在另外的计算机上启动。
这在如下两个主要方面影响了HDFS的可用性:
非计划事件如机器奔溃,集群将不可用直到操作人员重启Namenode。
计划中的维护事件如Namenode机器上的软硬件升级将导致集群短暂停机。
HDFS 高可用性功能通过提供在同一群集中运行两个(以及截至 3.0.0 多个)冗余 NameNodes 来解决上述问题,该集群具有热备用的主动/被动配置。这将允许当机器发生崩溃时能快速故障转移到另一台新的机器上,或者为计划维护目的,由管理员启动的优雅故障转移。
架构
在典型的HA集群中,两台或更多独立的机器被配置为namenode。在任何时间点,只有一个namenode处于活动状态,而其他namenode处于备用状态。活动的NameNode负责集群中的所有客户端操作,而备用服务器只是充当工作者,维护足够的状态,以便在必要时提供快速的故障转移。
为了使备用节点的状态与活动节点保持同步,两个节点都与一组称为“JournalNodes”(JNs)的独立守护进程通信。当活动节点执行任何名称空间修改时,它会将修改的记录持久地记录到这些大多数JNs中。备用节点能够从JNs中读取edits,并不断监视它们以查看edits log的更改。当备用节点看到edits时,它将其应用到自己的名称空间。在发生故障转移时,备用服务器将确保在将自己被提升到活动状态之前,已经从JournalNodes读取了所有edits。这确保了在发生故障转移之前名称空间状态是完全同步的。
为了提供快速的故障转移,备用节点还必须拥有关于集群中块位置的最新信息。为了实现这一点,DataNode配置了所有namenode的位置,并发送块位置信息和心跳给所有Namenode。
一次只有一个namenode是活动的,这对于HA集群的正确操作至关重要。否则,名称空间状态将很快在两者之间分离,从而有数据丢失或其他不正确结果的风险。为了确保这个属性并防止所谓的“裂脑场景”,JournalNodes每次只允许一个NameNode作为writer。在故障转移期间,将变为活动的NameNode将简单地接管向JournalNodes写入的角色,这将有效地防止其他NameNode继续处于活动状态,从而允许新的活动NameNode安全地继续进行故障转移。
硬件资源
为了部署一个HA集群,您应该准备以下内容:
NameNode机器 :运行活动NameNode和备用NameNode的机器之间应该具有相同的硬件,并且与在非HA集群中使用的硬件相同。
JournalNode机器:运行JournalNode的机器。JournalNode守护进程是相对轻量级的,因此这些守护进程可以合理地与其他Hadoop守护进程(例如namenode、JobTracker或YARN ResourceManager)同时配置在一台机器上。注意: 必须至少有3个JournalNode守护进程,因为edit log修改必须写入大多数JNs。这将允许系统容忍单个机器的故障。您也可以运行超过3个JournalNodes,但是为了实际增加系统能够容忍的故障数量,您应该运行奇数个JNs(即3、5、7,等等)。当使用N个JournalNodes运行时,系统最多可以容忍(N - 1) 2个故障,并继续正常运行。
注意,在HA集群中,备用NameNode也执行名称空间状态的检查点,因此没有必要在HA集群中运行Secondary NameNode、CheckpointNode或BackupNode。事实上,这样做是错误的。这还允许重新配置不支持HA的HDFS集群的人能够重用之前专用于Secondary NameNode的硬件。
部署
配置概述
与Federation配置类似,HA配置是向后兼容的,允许现有的单个NameNode配置无需更改即可工作。新配置的设计使集群中的所有节点都可以具有相同的配置,而不需要根据节点的类型将不同的配置文件部署到不同的机器上。
与HDFS Federation一样,HA集群重用nameservice ID来标识一个实际上可能由多个HA namenode组成的HDFS实例。此外,HA还添加了一个名为NameNode ID的新抽象。集群中的每个不同的NameNode都有一个不同的NameNode ID来区分它。为了让所有的NameNode支持一个配置文件,相关的配置参数都以nameservice ID和NameNode ID作为后缀。
配置详情
要配置HA namenode,必须在hdfs-site.xml配置文件中添加几个配置选项。
设置这些配置的顺序并不重要,但是为dfs. nameserservices和dfs.ha.namenode .[nameservice ID]选择的值将决定后面的键。因此,您应该在设置其他配置选项之前决定这些值。
dfs.nameservices :这个新的名称服务的逻辑名称
为这个名称服务选择一个逻辑名称,例如“mycluster”,并使用这个逻辑名称作为配置选项的值。您选择的名称是任意的。它将用于配置,并作为集群中的绝对HDFS路径的权威组件。
注意:如果您也在使用HDFS Federation,这个配置设置还应该包括其他名称服务(HA或其他名称服务)的列表,以逗号分隔的列表。<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>dfs.ha.namenodes.[nameservice ID] : NameService中的每个NameNode的唯一标识符.使用逗号分隔的NameNode id列表进行配置。datanode将使用它来确定集群中的所有
namenode。例如,如果你以前使用“mycluster”作为命名服务ID,而你想使
用“nn1”、“nn2”和“nn3”作为命名服务ID,你可以这样配置:<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2, nn3</value>
</property>注意:HA的namenode的最小数目是两个,但是您可以配置更多。由于通信开销的关系,建议不要超过5个名称,建议使用3个名称。
dfs.namenode.rpc-address.[nameservice ID].[name node ID] :要监听的每个 NameNode的完全限定RPC地址.对于之前配置的两个NameNode id,设置NameNode进程的完整地址和IPC端口。注意,这会产生两个单独的配置选项。例如:
<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>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn3</name>
<value>machine3.example.com:8020</value>
</property>注意:如果您愿意,您可以类似地配置“servicerpc-address”设置。
dfs.namenode.http-address.[nameservice ID].[name node ID]: 要监听的每个
NameNode的完全限定HTTP地址。与上面的rpc-address类似,为两个namenode的HTTP服务器设置侦听地址。例如:<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>machine1.example.com:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>machine2.example.com:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn3</name>
<value>machine3.example.com:9870</value>
</property>注意:如果启用了Hadoop的安全特性,还应该为每个NameNode设置类似的https-address。
dfs.namenode.shared.edits.dir : URI,它标识namenode将在其中写入/读取edits的
JNs组.在这里,配置提供共享edits存储的JournalNodes的地址,由活动的nameNode写入并由备用
的nameNode读取,以保持与活动的nameNode所做的所有文件系统更改同步。虽然必须指定几个
JournalNode地址,但应该只配置其中一个uri。URI的形式应该是:
qjournal://*host1:port1*;*host2:port2*;*host3:port3*/*journalId*.日志ID是这个名称服务的唯一标识符,它允许一组journalnode为多个联邦名称系统提供存
储。虽然不是必需的,但是重用nameservice ID作为日志标识符是一个好主意。例如,如果这个集群的JournalNodes运行在机 器“node1.example.com”、“node2.example.com”和“node3.example.com”上,并且
nameservice ID为“mycluster”,那么您将使用以下值作为该设置的值(JournalNode的默
认端口是8485):<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster</value>
</property>dfs.client.failover.proxy.provider.[nameservice ID]: HDFS客户端用来联系活
动NameNode的Java类.配置Java类的名称,DFS客户机将使用该名称来确定哪个NameNode是当前活动的,从而确定哪个NameNode当前为客户机请求提供服务。当前运行的hadoop中有两种实现,
ConfiguredFailoverProxyProvider and the RequestHedgingProxyProvider(对于首次调用,将并发调用所有namenode查找活跃的一个,在后续请求中,调用活动的namenode直到发生故障转移),所以使用其中一种,除非你使用一个自定义代理供应商。例如:<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>dfs.ha.fencing.methods: 一组脚本或Java类,用于在故障转移期间隔离活动的NameNode
为了保证系统的正确性,在任何给定时间只有一个NameNode处于活动状态。重要的是,在使用QJM时,只允许一个NameNode写入到JournalNodes,因此不存在在分裂大脑场景中破坏文件系统元数据的可能性。然而,当发生故障转移时,前一个活动的NameNode仍然有可能为客户端提供读请求,这些请求可能已经过时,直到NameNode在尝试写入到JournalNodes时关闭。因此,即使在使用QJM时,仍然需要配置一些防护方法。然而,为了在fencing机制失败的情况下提高系统的可用性,建议配置一种fencing方法,该方法保证返回成功作为列表中的最后一种fencing方法。注意,如果您选择不使用实际的隔离方法,您仍然必须为此设置配置一些内容,例如“shell(/bin/true)”。故障转移期间使用的隔离方法配置为carriage-return-separated列表,将按顺序尝试该列
表,直到其中一个表示隔离成功为止。Hadoop提供了两种方法:shell和sshfence。有关实现
自己的自定义隔离方法的信息,请参见org.apache.hadoop.ha.NodeFencer类。sshfence : SSH登录到活动的NameNode并终止进程
sshfence选项通过ssh登录到目标节点,并使用fuser终止侦听服务的TCP端口的进程。为了
使这个防护选项正常工作,它必须能够在不提供口令的情况下SSH登录到目标节点。因此,还必
须配置dfs.ha.fencing.ssh.private-key-files选项,它是一个逗号分隔的SSH私有密
钥文件列表。例如:<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/exampleuser/.ssh/id_rsa</value>
</property>也可以配置一个非标准的用户名或端口来执行SSH。还可以为SSH配置超时(以毫秒为单位),在
此之后,将认为此防护方法失败。可以这样配置:<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence([[username][:port]])</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>shell : 运行任意的shell命令来屏蔽活动的NameNode
shell隔离方法运行一个任意的shell命令。可以这样配置:<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>
</property>'('和')'之间的字符串直接传递给bash shell,可能不包括任何右括号。shell命令将在一
个设置为包含所有当前Hadoop配置变量的环境中运行,使用'_'字符替换'.'配置键中的字
符。所使用的配置已经将任何特定于名称的配置提升为它们的通用形式——例如,
dfs_namenode_rpc-address将包含目标节点的RPC地址,即使配置可能将该变量指定为
dfs.namenode.rpc-address.ns1.nn1。此外,还可以使用以下引用被隔离的目标节点的变量:
target_host 要隔离的节点的主机名
target_port 要隔离的节点的IPC端口
target_address 以上两个,结合为主机:端口
target_nameserviceid 要防护的NN的nameservice ID
target_namenodeid 要隔离的NN的namenode ID这些环境变量还可以在shell命令本身中用作替代。例如:
<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh --nameservice=<span class="katex-html" aria-hidden="true" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">a<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">r<span class="mord mathit" style="margin-right:0.03588em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">g<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">n<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">a<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">m<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">r<span class="mord mathit" style="margin-right:0.03588em;" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">v<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">c<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">dtarget_host:$target_port)</value>
</property>
</span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.03588em;"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.03588em;"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit"></span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span class="katex-html" aria-hidden="true">如果shell命令返回一个0的退出代码,则确定隔离成功。如果它返回任何其他退出代码,则隔
离没有成功,将尝试列表中的下一个隔离方法。注意:此隔离方法不实现任何超时。如果需要超时,应该在shell脚本本身中实现(例如通过派
生一个子shell来在一定的秒数内杀死它的父shell)。fs.defaultFS :当没有给定路径时,Hadoop FS客户端使用的默认路径前缀您现在可以选择为Hadoop客户机配置默认路径,以使用新的启用ha的逻辑URI。如果您在前面使用“mycluster”作为名称服务ID,那么这将是所有HDFS路径的权限部分的值。这可以这样配置,在您的core-site.xml文件:
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>dfs.journalnode.edits.dir: 日志节点守护进程存储其本地状态的路径这是JournalNode机器上的绝对路径,JNs使用的edits和其他本地状态将存储在其中。此配置只能使用单个路径。这个数据的冗余是通过运行多个独立的journalnode来提供的,或者通过在本地附加的RAID阵列上配置这个目录来提供的。例如:
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/path/to/journal/node/local/data</value>
</property>
部署详情
设置了所有必要的配置选项之后,必须在将要运行它们的机器上启动JournalNode守护进程。这可以通过运行“hdfs—daemon start journalnode”命令并等待daemon在每台相关的机器上启动来完成。
一旦启动了JournalNodes,首先必须同步两个HA namenode在磁盘上的元数据。
如果您正在设置一个新的HDFS集群,您应该首先在一个namenode上运行format命令(HDFS namenode -format)。
如果您已经格式化了NameNode,或者正在将一个不支持ha的集群转换为支持ha的集群,那么现
在应该通过在未格式化的NameNode上运行“hdfs NameNode -bootstrapStandby”命令,将
NameNode元数据目录的内容复制到另一个未格式化的NameNode。运行这个命令还将确保
JournalNodes(由dfs.namenode.shared.edit .dir配置)包含足够的edits事务,以便能够启动两个namenode。如果你要将一个非HA NameNode转换为HA,你应该运行“hdfs NameNode -initializeSharedEdits”命令,这将初始化日志节点与edits数据从本地NameNode edits目录。
此时,您可以像往常一样启动所有HA NameNode。
通过浏览namenode配置的HTTP地址,可以单独访问每个namenode的web页面。您应该注意到,在配置的地址旁边是NameNode的HA状态(“备用”或“活动”)。无论何时启动HA NameNode,它最初都处于备用状态。
管理命令
现在已经配置并启动了HA namenode,您将可以访问一些附加命令来管理HA HDFS集群。具体来说,您应该熟悉“hdfs haadmin”命令的所有子命令。不带任何附加参数运行此命令将显示以下使用信息:
Usage: haadmin
[-transitionToActive <serviceId>]
[-transitionToStandby <serviceId>]
[-failover [--forcefence] [--forceactive] <serviceId> <serviceId>]
[-getServiceState <serviceId>]
[-getAllServiceState]
[-checkHealth <serviceId>]
[-help <command>]
本指南描述了这些子命令的高级用法。对于每个子命令的具体使用信息,您应该运行“hdfs haadmin -help”。
transitionToActive 和 transitionToStandby : 将给定NameNode的状态转换为
Active或Standby
这些子命令分别导致给定的NameNode转换为活动状态或备用状态。这些命令不尝试执行任何防
护,因此应该很少使用。相反,推荐使用“hdfs haadmin -failover”子命令。failover:在两个namenode之间启动故障转移
此子命令将导致从第一个提供的NameNode到第二个提供的NameNode的故障转移。如果第一个
NameNode处于备用状态,此命令将简单地将第二个NameNode转换为活动状态,不会出现错误。
如果第一个NameNode处于活动状态,将尝试优雅地将其转换为备用状态。如果失败,将依次尝试fencing方法(按照dfs.ha.fencing.methods的配置),直到其中一个成功。只有在这个过程之后,第二个NameNode才会转换到活动状态。如果没有成功的fencing方法,第二个NameNode将不会转换到活动状态,并且将返回一个错误。getServiceState:确定给定的NameNode是活动的还是备用的
连接到提供的NameNode以确定其当前状态,适当地将“standby”或“active”打印到STDOUT。
这个子命令可能由cron作业或监视脚本使用,它们需要根据NameNode当前是活动的还是备用的而采取不同的行为。getAllServiceState :返回所有名称ode的状态
连接到配置的namenode以确定当前状态,并将“备用”或“活动”打印到标准输出。checkHealth:检查给定NameNode的运行状况
连接到提供的NameNode以检查其运行状况。NameNode能够对自身执行一些诊断,包括检查内部
服务是否按预期运行。如果NameNode正常运行,该命令将返回0,否则将返回非0。可以将此命令用于监视目的。
注意:这还没有实现,目前将始终返回成功,除非给定的NameNode完全关闭。
负载均衡器设置
如果你在负载均衡器(例如Azure或AWS)后面运行一组namenode,并且希望负载均衡器指向活动的NN,你可以使用/isActive HTTP端点作为运行状况探测。如果NN处于活动HA状态,http://NN_HOSTNAME/isActive 将返回200状态码响应,否则将返回405。
正在进行的编辑日志跟踪
在默认设置下,备用NameNode将只应用已完成的edit log段中的edits。如果希望有一个备用的NameNode,其中包含更多最新的名称空间信息,那么可以启用正在进行的eidt段的跟踪。此设置将尝试从JournalNodes上的内存缓存中获取edit,并可以将事务应用于备用NameNode之前的延迟时间缩短到毫秒级。如果无法从缓存中提供edit,备用服务器仍然能够检索它,但延迟时间将长得多。相关配置有:
dfs.ha.tail-edits.in-progress: 是否对正在进行的edit log 启用跟踪。这也将在
JournalNodes上启用内存中的edit缓存。默认情况下禁用。dfs.journalnode.edit-cache-size.bytes : 在JournalNode上edits的内存缓存的大
小。在一个典型的环境中,每次edits大约需要200字节,因此,例如,默认的1048576 (1MB) 可以容纳大约5000个事务。建议监控JournalNode指标
RpcRequestCacheMissAmountNumMisses RpcRequestCacheMissAmountAvgTxns,分别计算请求的数量不能被缓存服务,和额外的交易将需要在缓存中已经成功的请求。例如,如果一个请求试图从事务ID 10开始获取edit,但是缓存中最老的数据在事务ID 20,那么平均值将增加一10。
该特性主要与备用/观察者读取特性一起使用。使用此特性,可以从非活动的namenode处理读请求;因此,跟踪正在进行的edit为这些节点提供了为请求提供更新鲜数据的能力。有关此特性的更多信息,请参阅Apache JIRA票据HDFS-12943。
自动故障转移
介绍
上述各节介绍如何配置手动故障转移。在该模式下,即使活动节点出现故障,系统也不会自动触发从活动到备用 NameNode 的故障转移。本节介绍如何配置和部署自动故障转移。
组件
自动故障转移将两个新组件添加到 HDFS 部署中:ZooKeeper 仲裁和 ZKFailover 控制器进程(缩写为 ZKFC)。
Apache ZooKeeper 是一种高度可用的服务,用于维护少量协调数据、通知客户端该数据中的更改以及监视客户端的故障。自动 HDFS 故障转移的实现依赖于 ZooKeeper 进行以下操作:
故障检测 - 群集中的每台 NameNode 计算机在 ZooKeeper 中维护一个持久会话。如果计算机崩溃,ZooKeeper 会话将过期,通知其他 NameNode 应触发故障转移。
活动名称节点选择 - ZooKeeper 提供了一个简单的机制来专门选择一个节点作为活动节点。如果当前活动 NameNode 崩溃,另一个节点可能会在 ZooKeeper 中获取特殊的独占锁,指示它应成为下一个活动节点。
ZKFailover 控制器 (ZKFC) 是一个新组件,它是 ZooKeeper 客户端,它也监视和管理 NameNode 的状态。运行 NameNode 的每台计算机也运行 ZKFC,ZKFC 负责:
运行状况监视 - ZKFC 使用运行状况检查命令定期 ping 其本地 NameNode。只要 NameNode 以正常状态及时响应,ZKFC 认为节点是健康的。如果节点已崩溃、冻结或以其他方式进入不正常状态,运行状况监视器将标记为不正常。
ZooKeeper会话管理 - 当本地 NameNode 健康时,ZKFC 在 ZooKeeper 中举行会话开放。如果本地 NameNode 处于活动状态,它也会持有特殊的"锁定"znode。此锁使用 ZooKeeper 对"临时"节点的支持;如果会话过期,锁定节点将自动删除。
基于 ZooKeeper 的选集 - 如果本地 NameNode 是健康的,并且 ZKFC 看到没有其他节点当前持有锁 znode,它本身将尝试获取锁。如果成功,则它"赢得了选举",并负责运行故障转移,使其本地 NameNode 处于活动状态。故障转移过程与上述手动故障转移类似:首先,如有必要,将上一个活动进行围栏,然后将本地 NameNode 过渡到活动状态。
有关自动故障转移设计的更多详细信息,请参阅 Apache HDFS JIRA 上附加到 HDFS-2185 的设计文档。
部署Zookeeper
在典型的部署中,ZooKeeper 守护程序配置为在三个或五个节点上运行。由于 ZooKeeper 本身具有轻量级资源要求,因此可以将 ZooKeeper 节点与 HDFS NameNode 和待机节点并网放在同一硬件上。许多操作员选择将第三个 ZooKeeper 进程部署到与 YARN 资源管理器相同的节点上。建议配置 ZooKeeper 节点,将其数据存储在从 HDFS 元数据的单独磁盘驱动器上,以获得最佳性能和隔离。
ZooKeeper 的设置超出了本文档的范围。我们将假设您已设置在三个或更多节点上运行的 ZooKeeper 群集,并且通过使用 ZK CLI 连接验证了其正确操作。
在你开始之前
在开始配置自动故障转移之前,应关闭集群。当前无法在集群运行时从手动故障转移设置过渡到自动故障转移设置。
配置自动故障转移
自动故障转移的配置需要向配置添加两个新参数。在 hdfs 站点.xml,添加:
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
这指定应为自动故障转移设置群集。在核心站点.xml,添加:
<property>
<name>ha.zookeeper.quorum</name
<value>zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181</value>
</property>
这将列出运行 ZooKeeper 服务的主机端口对。
与本文档前面介绍的参数一样,可以通过将配置密钥后缀为 nameservice ID,根据每个nameservice配置这些设置。例如,在启用了federation的集群中,您可以显式启用仅由 dfs.ha.automatic-failover.enabled.my-nameservice-id 配置的一个nameservice自动故障转移。
还有其他几个配置参数可以设置为控制自动故障转移的行为;但是,对于大多数安装来说,它们并不需要。有关详细信息,请参阅配置密钥特定文档。
在Zookeeper中初始化HA状态
添加配置密钥后,下一步是在 ZooKeeper 中初始化所需的状态。您可以通过从 NameNode 主机之一运行以下命令来做到这一点。
[hdfs]$ $HADOOP_HOME/bin/hdfs zkfc -formatZK
这将在 ZooKeeper 中创建一个 znode,自动故障转移系统将存储其数据。
使用start-dfs.sh启动集群
由于在配置中已启用自动故障转移,start-dfs.sh脚本现在将自动在运行 NameNode 的任何计算机上启动 ZKFC 守护程序。当 ZKFC 启动时,它们将自动选择其中一个 NameNode 成为活动的NameNode。
手动启动集群
如果手动管理群集上的服务,则需要在运行 NameNode 的每台计算机上手动启动 zkfc 守护进程。您可以通过运行以下时间启动守护进程:
[hdfs]$ $HADOOP_HOME/bin/hdfs --daemon start zkfc
ZooKeeper的访问权限
如果您运行的是安全群集,则可能需要确保存储在 ZooKeeper 中的信息也是安全的。这样可以防止恶意客户端修改 ZooKeeper 中的元数据或可能触发错误的故障转移。
为了保护 ZooKeeper 中的信息,请先将以下内容添加到core-site.xml文件:
<property>
<name>ha.zookeeper.auth</name>
<value>@/path/to/zk-auth.txt</value>
</property>
<property>
<name>ha.zookeeper.acl</name>
<value>@/path/to/zk-acl.txt</value>
</property>
请注意这些值中的"@"字符 - 这指定配置不是内联的,而是指向磁盘上的文件。身份验证信息也可以通过凭据提供器读取(请参阅 hadoop 通用项目中的凭据提供 API 指南)。
第一个配置的文件指定 ZooKeeper 身份验证的列表,其格式与 ZK CLI 使用的格式相同。例如,您可以指定类似:
digest:hdfs-zkfcs:mypassword
…其中 hdfs-zkfcs 是 ZooKeeper 的唯一用户名,mypassword是一些用作密码的唯一字符串。
接下来,使用如下命令生成对应于此身份验证的 ZooKeeper ACL:
[hdfs]$ java -cp $ZK_HOME/lib/*:$ZK_HOME/zookeeper-3.4.2.jar org.apache.zookeeper.server.auth.DigestAuthenticationProvider hdfs-zkfcs:mypassword
output: hdfs-zkfcs:mypassword->hdfs-zkfcs:P/OQvnYyU/nF/mGYvB/xurX8dYs=
复制并粘贴该输出部分中"->"字符串的内容到文件 zk-acls.txt,前缀为字符串"digest:"。例如:
digest:hdfs-zkfcs:vlUvLnd8MlacsE80rDuu6ONESbM=:rwcda
为了使这些 ACL 生效,然后应重新运行 zkfc -formatZK 命令,如上所述。
这样做之后,您可以验证来自 ZK CLI 的 ACL,如下所示:
[zk: localhost:2181(CONNECTED) 1] getAcl /hadoop-ha
'digest,'hdfs-zkfcs:vlUvLnd8MlacsE80rDuu6ONESbM=
: cdrwa
验证自动故障转移
设置自动故障转移后,应测试其操作。为此,请首先找到活动NameNode。您可以通过访问 NameNode Web接口来判断哪个节点处于活动状态-每个节点在页面顶部报告其 HA 状态。
找到活动 NameNode后,可能会导致该节点出现故障。例如,您可以使用 NN 的 kill -9
如果测试不成功,可能是有配置错误。检查 zkfc 守护程序以及 NameNode 守护程序的日志,以便进一步诊断问题。
自动故障转移FAQ
我按任何特定顺序启动 Zkfc 和 NameNode 守护程序是否重要?
不。在任何给定节点上,您可以在其相应的 NameNode 之前或之后启动 ZKFC。
我应该进行哪些额外的监控?
您应该在运行 NameNode 的每个主机上添加监视,以确保 ZKFC 保持运行。例如,在某些类型的 ZooKeeper 故障中,ZKFC 可能会意外退出,应重新启动以确保系统已准备好自动故障转移。
此外,您应该监视 ZooKeeper 仲裁中的每台服务器。如果 ZooKeeper 崩溃,则自动故障转移将无法正常工作。
如果ZooKeeper崩溃了怎么?
如果 ZooKeeper 集群崩溃,不会触发自动故障转移。但是,HDFS 将继续运行,没有任何影响。重新启动 ZooKeeper 时,HDFS 将重新连接,没有任何问题。
我可以将一个 NameNodes 指定为主/首选吗?
不。目前,不支持这样做。无论哪个NameNode首先启动,都将变为活动状态。您可以选择按特定顺序启动群集,以便首选节点先启动。
配置自动故障转移时,如何启动手动故障转移?
即使配置了自动故障转移,也可以使用相同的 hdfs haadmin 命令启动手动故障转移。它将执行协调的故障转移。
启用HA的HDFS升级/完成/回滚
在HDFS版本之间移动时,有时可以简单地安装较新的软件并重新启动群集。但是,有时升级正在运行的HDFS版本可能需要更改磁盘上的数据。在这种情况下,在安装新软件后,必须使用HDFS升级/完成/回滚工具。此过程在HA环境中变得更加复杂,因为根据定义,NN所依赖的磁盘元数据是分布式的,既分布在对中的两个HA NN上,也分布在QJM用于共享eidts存储的情况下的 JournalNodes上。本文档部分介绍在HA设置中使用HDFS 升级/完成/回滚工具的过程。
要执行 HA 升级,操作员必须执行以下操作:
正常关闭所有 NN,并安装较新的软件。
启动所有 JN。请注意,在执行升级、回滚或最终化操作时,所有 JN 都运行至关重要。如果在运行任何这些操作时,任何一个 JN 已关闭,则操作将失败。
使用"-upgrade"标志启动其中一个 NN。
启动时,此 NN 不会像往常一样在 HA 设置中进入待机状态。相反,此 NN 将立即进入活动状态,执行其本地存储目录的升级,并执行共享edits log的升级。
此时,HA 对中的其他 NN 将与升级的 NN 不同步。为了恢复同步并再次具有高可用设置,您应该使用"-bootstrapStandby"标志运行 NN 来重新引导此 NameNode。使用"-upgrade"标志启动第二个 NN 是错误的。
请注意,如果您要在完成或回滚升级之前随时重新启动 NameNodes,则应正常启动 NN,即没有任何特殊的启动标志。
要查询升级状态,操作员将使用"hdfs dfsadmin-upgrade query"命令,且至少有一个 NN 正在运行。无论每个NN的升级过程是否完成,该命令都将返回。
要完成 HA 升级,操作员将在 NN 运行时使用"hdfs dfsadmin-finalizeUpgrade"命令,并且至少有一个处于活动状态。发生这种情况时,活动 NN 将执行共享日志的完成,其本地存储目录包含上一个 FS 状态的 NN 将删除其本地状态。
若要执行升级的回滚,应首先关闭两个 NN。操作员应在 NN 上运行回滚命令,在 NN 上启动升级过程,该命令将在本地目录上以及共享日志(无论是 NFS 还是 JNs 上)执行回滚。之后,应启动此 NN,操作员应在另一个 NN 上运行"引导陷阱",使两个 NN 与此回滚文件系统状态同步。




