HDFS的NameNode使用主备架构实现高可用,主备选举通过zookeeper作协调器实现。选举由zkfc组件(zkfc与NameNode同一台机器,属于两个不同的进程)发起,选主流程:会尝试在 Zookeeper上创建一个路径为/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 的临时节点,Zookeeper 的写一致性会保证最终只会有一个zkfc组件创建成功而成为主NameNode。
触发主备选举
如果zkfc组件检测到 NameNode 的状态异常时,会主动删除当前在 Zookeeper 上建立的临时节点/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock,这样处于备状态的NameNode的zkfc组件监听器就会收到这个节点的 NodeDeleted 事件。收到这个事件之后,会马上进入选主流程(文章开始处介绍)。
如果处于主NameNode所在的机器整个宕掉的话,那么根据 Zookeeper 的临时节点特性,/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock节点会自动被删除,从而也会自动进行一次主备选举切换。
但如果主NameNode所在机器因负载过高、系统线程调度、IO hang又或是常见的Full GC导致的长时间STW,都可能导致出现工程实践中常见的进程”假死“,如果”假死“时间过长,超过zookeeper session timeout,会触发/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 的临时节点的删除,再次进入选主流程选出新主。这时如果原来的主NameNode突然恢复,可能会在接下来的一段时间内还认为自己还是主,可以对外提供服务,这样就出现多主,这就产生所谓的”脑裂“,这种情况对于数据一致性要求非常高的系统来说是灾难性的,导致数据错乱而无法恢复。HDFS的解决方案是fencing,即把旧的主通过某种手段隔离起来,使其不能对外提供服务。
fencing方案
HDFS的fencing实现是这样的,成功创建hadoop-ha/ d f s . n a m e s e r v i c e s / A c t i v e S t a n d b y E l e c t o r L o c k 临 时 节 点 的 同 时 , 再 创 建 路 径 为 / h a d o o p − h a / {dfs.nameservices}/ActiveStandbyElectorLock临时节点的同时,再创建路径为/hadoop-ha/ dfs.nameservices/ActiveStandbyElectorLock临时节点的同时,再创建路径为/hadoop−ha/{dfs.nameservices}/ActiveBreadCrumb的持久节点,这个节点里保存着当前主NameNode的地址信息。如果由于zkfc假死导致zookeeper session timeout,zookeeper会删除临时节点ActiveStandbyElectorLock,但是持久节点ActiveBreadCrumb会保留。触发选主产生新主后,新主会在节点状态变为Active之前,利用ActiveBreadCrumb持久节点的地址信息进行fencing,具体流程如下:
尝试RPC调用旧主的transitionToStandby方法,看能不能把它转换为 Standby 状态
如果transitionToStandby方法调用失败,那么就执行HDFS配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施
sshfence:通过 SSH 登录到目标机器上,执行命令将对应的进程杀死(通常会选择这种)
shell:执行一个用户自定义的 shell 脚本来将对应的进程隔离
只有在成功地执行完成 fencing 之后,新主才会转换为 Active 状态,开始对外提供服务。
问题
如果在fencing过程中,原主节点宕机又或是出现网络分区导致fencing失败,产生了脑裂会有什么影响? 看Rakesh Radhakrishnan下面这封邮件:
Since you powered off the Active NN machine, during fail-over SNN timed out
to connect to this machine and fencing is failed. Typically fencing methods
should be configured to not to allow multiple writers to same shared
storage. It looks like you are using 'QJM' and it supports the fencing
feature on its own. i.e. it wont allow multiple writers at a time. So I
think external fencing methods can be skipped. AFAIK, to improve the
availability of the system in the event the fencing mechanisms fail, it is
advisable to configure a fencing method which is guaranteed to return
success. You can remove the SSH fencing method from both machines
configuration. Please try the below shell based fence method just to skip
SSH fence and restart the cluster. Then fail over will happen successfully.
dfs.ha.fencing.methods
shell(/bin/true)
*Reference:-*
https://hadoop.apache.org/docs/r2.7.1/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html
"*JournalNodes will only ever allow a single NameNode to be a writer at a
time. During a failover, the NameNode which is to become active will simply
take over the role of writing to the JournalNodes, which will effectively
prevent the other NameNode from continuing in the Active state, allowing
the new Active to safely proceed with failover*."
大概意思是如果是使用QJM共享存储系统的,即使出现了多主,这个QJM也不允许多个writer向其同时发起写操作,这样写操作是安全的,这和利用Quorum机制防止脑列问题的产生是类似的。但要注意,高可用的前提是,fencing操作的超时设置和shell要保证返回true成功(允许多主的产生)。
思考
上面说的QJM能防止多个NameNode同时向QJM共享存储写数据,但会不会有客户端依然向这个旧的NameNode读到旧的数据呢?又会有什么样的影响?
使用zookeeper作分布式协调器的应用,都会存在类似问题,又怎么避免的?
分布式锁(使用redis、zk等等)也会有似的问题,如何预防?
有兴趣技术交流请关注我的公众号吧:)
参考:
https://hadoop.apache.org/docs/r2.7.1/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html
https://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-name-node/
http://mail-archives.apache.org/mod_mbox/hadoop-user/201607.mbox/