Hadoop HDFS高可用(HA)

版本记录:

  • 2016-07-19 凌晨 初稿

在Hadoop 1.x 中,Namenode是集群的单点故障,一旦Namenode出现故障,整个集群将不可用,重启或者开启一个新的Namenode才能够从中恢复。值得一提的是,Secondary Namenode并没有提供故障转移的能力。集群的可用性受到影响表现在:

  • 当机器发生故障,如断电时,管理员必须重启Namenode才能恢复可用。
  • 在日常的维护升级中,需要停止Namenode,也会导致集群一段时间不可用。

架构

Hadoop HA(High Available)通过同时配置两个处于Active/Passive模式的Namenode来解决上述问题,分别叫Active Namenode和Standby Namenode. Standby Namenode作为热备份,从而允许在机器发生故障时能够快速进行故障转移,同时在日常维护的时候使用优雅的方式进行Namenode切换。Namenode只能配置一主一备,不能多于两个Namenode。

主Namenode处理所有的操作请求(读写),而Standby只是作为slave,维护尽可能同步的状态,使得故障时能够快速切换到Standby。为了使Standby Namenode与Active Namenode数据保持同步,两个Namenode都与一组Journal Node进行通信。当主Namenode进行任务的namespace操作时,都会确保持久会修改日志到Journal Node节点中的大部分。Standby Namenode持续监控这些edit,当监测到变化时,将这些修改应用到自己的namespace。

当进行故障转移时,Standby在成为Active Namenode之前,会确保自己已经读取了Journal Node中的所有edit日志,从而保持数据状态与故障发生前一致。

为了确保故障转移能够快速完成,Standby Namenode需要维护最新的Block位置信息,即每个Block副本存放在集群中的哪些节点上。为了达到这一点,Datanode同时配置主备两个Namenode,并同时发送Block报告和心跳到两台Namenode。

确保任何时刻只有一个Namenode处于Active状态非常重要,否则可能出现数据丢失或者数据损坏。当两台Namenode都认为自己的Active Namenode时,会同时尝试写入数据(不会再去检测和同步数据)。为了防止这种脑裂现象,Journal Nodes只允许一个Namenode写入数据,内部通过维护epoch数来控制,从而安全地进行故障转移。

有两种方式可以进行edit log共享:

  • 使用NFS共享edit log(存储在NAS/SAN)
  • 使用QJM共享edit log

使用NFS共享存储

Hadoop HDFS高可用(HA)_第1张图片

如图所示,NFS作为主备Namenode的共享存储。这种方案可能会出现脑裂(split-brain),即两个节点都认为自己是主Namenode并尝试向edit log写入数据,这可能会导致数据损坏。通过配置fencin脚本来解决这个问题,fencing脚本用于:

  • 将之前的Namenode关机
  • 禁止之前的Namenode继续访问共享的edit log文件

使用这种方案,管理员就可以手工触发Namenode切换,然后进行升级维护。但这种方式存在以下问题:

  • 只能手动进行故障转移,每次故障都要求管理员采取措施切换。
  • NAS/SAN设置部署复杂,容易出错,且NAS本身是单点故障。
  • Fencing 很复杂,经常会配置错误。
  • 无法解决意外(unplanned)事故,如硬件或者软件故障

因此需要另一种方式来处理这些问题:

  • 自动故障转移(引入ZooKeeper达到自动化)
  • 移除对外界软件硬件的依赖(NAS/SAN)
  • 同时解决意外事故及日常维护导致的不可用

Quorum-based 存储 + ZooKeeper

QJM(Quorum Journal Manager)是Hadoop专门为Namenode共享存储开发的组件。其集群运行一组Journal Node,每个Journal 节点暴露一个简单的RPC接口,允许Namenode读取和写入数据,数据存放在Journal节点的本地磁盘。当Namenode写入edit log时,它向集群的所有Journal Node发送写入请求,当多数节点回复确认成功写入之后,edit log就认为是成功写入。例如有3个Journal Node,Namenode如果收到来自2个节点的确认消息,则认为写入成功。

而在故障自动转移的处理上,引入了监控Namenode状态的ZookeeperFailController(ZKFC)。ZKFC一般运行在Namenode的宿主机器上,与Zookeeper集群协作完成故障的自动转移。整个集群架构图如下:

Hadoop HDFS高可用(HA)_第2张图片

QJM

Namenode使用QJM 客户端提供的RPC接口与Namenode进行交互。写入edit log时采用基于仲裁的方式,即数据必须写入JournalNode集群的大部分节点。
在Journal Node节点上(服务端)
服务端Journal运行轻量级的守护进程,暴露RPC接口供客户端调用。实际的edit log数据保存在Journal Node本地磁盘,该路径在配置中使用dfs.journalnode.edits.dir属性指定。
Journal Node通过epoch数来解决脑裂的问题,称为JournalNode fencing。具体工作原理如下:
1)当Namenode变成Active状态时,被分配一个整型的epoch数,这个epoch数是独一无二的,并且比之前所有Namenode持有的epoch number都高。

2)当Namenode向Journal Node发送消息的时候,同时也带上了epoch。当Journal Node收到消息时,将收到的epoch数与存储在本地的promised epoch比较,如果收到的epoch比自己的大,则使用收到的epoch更新自己本地的epoch数。如果收到的比本地的epoch小,则拒绝请求。

3)edit log必须写入大部分节点才算成功,也就是其epoch要比大多数节点的epoch高。

Hadoop HDFS高可用(HA)_第3张图片

这种方式解决了NFS方式的3个问题:

  • 不需要额外的硬件,使用原有的物理机
  • Fencing通过epoch数来控制,避免出错。
  • 自动故障转移:Zookeeper处理该问题。

使用Zookeeper进行自动故障转移

前面提到,为了支持故障转移,Hadoop引入两个新的组件:Zookeeper Quorum和ZKFailoverController process(简称ZKFC)。

Zookeeper的任务包括:

  • 失败检测: 每个Namnode都在ZK中维护一个持久性session,如果Namnode故障,session过期,使用zk的事件机制通知其他Namenode需要故障转移。
  • Namenode选举:如果当前Active namenode挂了,另一个namenode会尝试获取ZK中的一个排它锁,获取这个锁就表名它将成为下一个Active NN。

在每个Namenode守护进程的机器上,同时也会运行一个ZKFC,用于完成以下任务:

  • Namenode健康健康
  • ZK Session管理
  • 基于ZK的Namenode选举

如果ZKFC所在机器的Namenode健康状态良好,并且用于选举的znode锁未被其他节点持有,则ZKFC会尝试获取锁,成功获取这个排它锁就代表获得选举,获得选举之后负责故障转移,如果有必要,会fencing掉之前的namenode使其不可用,然后将自己的namenode切换为Active状态。

部署与配置

硬件资源

为了允许HA集群,需要以下资源:
1)Namenode机器:运行Active Namenode和Standby Namenode的机器配置应保持一样,也与不使用HA情况下的配置一样。
2)JournalNode机器:运行JournalNode的机器,这些守护进程比较轻量级,所以可以将其部署在Namenode或者YARN ResourceManager。至少需要部署3个Journalnode节点,以便容忍一个节点故障。通常配置成奇数,例如总数为N,则可以容忍(N-1)/2台机器发生故障后集群仍然可以正常工作。

需要注意的是,Standby Namenode同时完成了原来Secondary namenode的checkpoint功能,因此不需要独立再部署Secondary namenode。

HA配置

Nameservices: 服务的逻辑名称

<property>
  <name>dfs.nameservicesname>
  <value>myclustervalue>
property>

Namenode配置:
dfs.ha.namenodes.[nameservices]: nameserviecs对应的namenode:

<property>
  <name>dfs.ha.namenodes.myclustername>
  <value>nn1,nn2value> 
property>

Namenode RPC地址:

<property>
  <name>dfs.namenode.rpc-address.mycluster.nn1name>
  <value>machine1.example.com:8020value>
property>
<property>
  <name>dfs.namenode.rpc-address.mycluster.nn2name>
  <value>machine2.example.com:8020value>
property>

Namenode HTTP Server配置:

<property>
  <name>dfs.namenode.http-address.mycluster.nn1name>
  <value>machine1.example.com:50070value>
property> 
<property>
  <name>dfs.namenode.http-address.mycluster.nn2name>
  <value>machine2.example.com:50070value>
property>

edit log保存目录,也就是Journal Node集群地址,分号隔开:


<property>
  <name>dfs.namenode.shared.edits.dirname>
 <value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/myclustervalue>
property>

客户端故障转移代理类,目前只提供了一种实现:

<property>
  <name>dfs.client.failover.proxy.provider.myclustername>
  <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvidervalue>
property>

edit日志保存路径:

<property>
  <name>dfs.journalnode.edits.dirname>
  <value>/path/to/journal/node/local/datavalue>
property>

fencing方法配置:

 <property>
      <name>dfs.ha.fencing.methodsname>
      <value>sshfencevalue>
    property>

    <property>
      <name>dfs.ha.fencing.ssh.private-key-filesname>
      <value>/home/exampleuser/.ssh/id_rsavalue>
 property>

虽然在使用QJM作为共享存储时,不会出现同时写入的脑裂现象。但是旧的namenode依然可以接受读请求,这可能会导致数据过时,直到原有namenode尝试写入journal node时才关机。因此也推荐配置一种合适的fencing方法。

部署启动

配置完成之后,使用如下命令启动JQM集群:

hadoop-daemon.sh  start  journalnode

配置并启动Zookeeper集群,与常规的而配置方式完成一样,主要包括数据保存位置、节点id、时间配置等,在zoo.cfg中配置。这里不列出详细步骤。使用之前,需要格式化zk的文件:

hdfs zkfc -formatZK

格式化Namenode:

hdfs  namenode -format

启动两个namenode:

//master
hadoop-daemon.sh start namenode27
//备用namenode上
hdfs namenode -bootstrapStandby

其他组件的启动方式与常规方式一样。

(完)

你可能感兴趣的:(Hadoop,架构与设计)