【前言】
有一段时间没有更文了,一方面是之前准备的hudi系列由于一些细节还没研究得很清楚,暂时没有继续更新。另一方面,最近事情相当多,回家后收拾收拾就十一二点了,也就没有再进行总结输出了。
不过,最近还是囤积了不少可以总结复盘的知识点,后续不管怎样还是尽量保证一周至少一篇原创文章,倒逼自己总结输出~
本文来聊聊HDFS里面,如果正确将JN从一个节点扩容到多个节点。
可能有的小伙伴会好奇,怎么会有这种需求或场景,需要将JN从一个节点扩容到3个以上的节点。
实际可能的情况是:在资源不够充足的情况下,整体部署舍弃一部分服务的高可用,例如NN还是以HA模式部署,但JN仅部署1个。此后,可能条件富裕了,相应的高可用也就需要做得更完善些,这个时候就需要将JN从1个节点扩容到3个及以上的节点。
【理想中的扩容步骤】
咋一看,JN的扩容应该是一件很简单的事情,涉及的操作步骤为:
第一步:新增多个节点,并部署启动JN
第二步:在NN的配置中添加新的JN的IP
第三步:重启NN,就可以完成JN的扩容操作了
操作步骤及流程看似没有什么问题,但结果却不是我们所期望的。实际上,NN重新启动后,会持续报错并抛出异常,最终自行结束,其报错信息为:
代码语言:javascript复制172.168.3.12:8485: Journal Storage Directory /home/hncscwc/hadoop/dfs/journal/hdfsHACluster not formatted
at org.apache.hadoop.hdfs.qjournal.server.Journal.checkFormatted(Journal.java:480)
at org.apache.hadoop.hdfs.qjournal.server.Journal.getEditLogManifest(Journal.java:662)
at org.apache.hadoop.hdfs.qjournal.server.JournalNodeRpcServer.getEditLogManifest(JournalNodeRpcServer.java:191)
at org.apache.hadoop.hdfs.qjournal.protocolPB.QJournalProtocolServerSideTranslatorPB.getEditLogManifest(QJournalProtocolServerSideTranslatorPB.java:224)
at org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos$QJournalProtocolService$2.callBlockingMethod(QJournalProtocolProtos.java:25431)
at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:447)
at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:989)
at org.apache.hadoop.ipc.Server$RpcCall.run(Server.java:850)
at org.apache.hadoop.ipc.Server$RpcCall.run(Server.java:793)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:422)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1922)
at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2489)
at org.apache.hadoop.hdfs.qjournal.client.QuorumException.create(QuorumException.java:81)
at org.apache.hadoop.hdfs.qjournal.client.QuorumCall.rethrowException(QuorumCall.java:286)
at org.apache.hadoop.hdfs.qjournal.client.AsyncLoggerSet.waitForWriteQuorum(AsyncLoggerSet.java:142)
at org.apache.hadoop.hdfs.qjournal.client.QuorumJournalManager.selectInputStreams(QuorumJournalManager.java:477)
at org.apache.hadoop.hdfs.server.namenode.JournalSet.selectInputStreams(JournalSet.java:278)
at org.apache.hadoop.hdfs.server.namenode.FSEditLog.selectInputStreams(FSEditLog.java:1590)
at org.apache.hadoop.hdfs.server.namenode.FSEditLog.selectInputStreams(FSEditLog.java:1614)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:695)
at org.apache.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:317)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.loadFSImage(FSNamesystem.java:1045)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.loadFromDisk(FSNamesystem.java:707)
at org.apache.hadoop.hdfs.server.namenode.NameNode.loadNamesystem(NameNode.java:635)
at org.apache.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:696)
at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:906)
at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:885)
at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1626)
at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1694)
为什么会这样呢?
回顾《一文搞定journal node原理》文章中讲到的,NN启动后首先会向JN发送多个rpc请求,完成从JN同步editlog信息,以及JN集群之间的editlog同步。
每个RPC请求都必须有多数以上的节点成功响应,该次rpc请求才算是真的成功。
JN在收到NN的rpc请求时,会对自身情况进行判断,如果所在的持久化路径中没有保存NN集群的元数据信息(存储在VERSION文件中),则认为自身没有格式化,因此返回错误,并最终导致NN退出。
而JN保存的NN的元数据信息仅在整个集群部署时,NN通过格式化命令下发给所有JN,此后均不再进行集群信息的同步。
那是不是再次触发一次格式化就可以了呢?
这种方法,可以是可以的,但存在的问题是:格式化这个动作是将当前的元数据全部清除。也就是说,如果已有的数据全部不需要了,那么可以考虑采用这种方式。
但一般来说,进行升级扩容时,还是希望所有已经存储的数据保留,因此该方法是行不通的。
【正确姿势】
了解了问题的本质后(JN缺少NN的集群信息,认为未格式化),剩下的就是对症下药了,即只要保证新增的JN节点上有NN集群的元数据信息就可以了。
那么可行的方式有:
- 将原有JN中的文件拷贝到新增JN节点对应的持久化位置
整体扩容流程和前面讲到的雷同,先新增JN节点;然后将原有节点中的VERSION文件拷贝到新JN节点中对应配置文件中指定的存储目录,并启动JN;接着修改NN的配置项,之后重启NN即可。
注意:在JN配置文件指定的目录下,还需要手动创建 $NAMESPACE/current目录,然后将VERSION文件,放到current目录下。
$NAMESPACE为配置项"dfs.nameservices"的值。
- 通过NN对新增的节点进行初始化
同样是先新增并部署启动JN;然后修改NN的配置文件,并在ActiveNN中,通过执行下面的命令完成对JN节点的初始化动作,最后重启NN即可。
代码语言:javascript复制hdfs namenode -f ./hdfs-site.xml -initializeSharedEdits
这里使用的配置文件可以就是nn当前使用的配置文件,但需要注意的是:
配置文件中的配置项不能包含已有的JN节点信息,否则会报错失败。因为已经格式化过的JN节点不能再次进行格式化操作,重复格式化会报错。
或者通过手动指定JN节点的ip完成JN的初始化动作,具体命令如下:
代码语言:javascript复制hdfs namenode -Ddfs.namenode.shared.edits.dir="qjournal://$JN2:8485;$JN3:8485/$NAMESPACE" -initializeSharedEdits
## JN2,JN3为新增jn节点的ip
## NAMESPACE为配置项 dfs.nameservices 的值
tips:initializeSharedEdits原本用于hdfs从standalone模式升级到HA模式,即standalone模式下不会配置JN节点,到HA模式后,需要配置JN节点。因此需要对JN进行格式化,包括同步NN的集群信息,以及将该NN上存储的editlog文件同步到JN中。
在这种场景下,原理是一样的,只需要注意不要对已有的JN节点再次进行初始化操作即可。
【总结】
一句话简单总结就是:新增或首次部署的jn,需要有nn的集群信息(即先进行格式化)才能正常提供服务