乐趣区

关于大数据:vivo-万台规模-HDFS-集群升级-HDFS-3x-实践

vivo 互联网大数据团队 -Lv Jia

Hadoop 3.x 的第一个稳固版本在 2017 年底就曾经公布了,有很多重大的改良。

在 HDFS 方面,反对了 Erasure Coding、More than 2 NameNodes、Router-Based Federation、Standby NameNode Read、FairCallQueue、Intra-datanode balancer 等新个性。这些新个性在稳定性、性能、老本等多个方面带来诸多收益,咱们打算将 HDFS 集群降级到 HDFS 3.x 版本。

本篇文章会介绍咱们是如何将 CDH 5.14.4 HDFS 2.6.0 滚动降级到 HDP-3.1.4.0-315 HDFS 3.1.1 版本,是业界为数不多的从 CDH 集群滚动降级到 HDP 集群的案例。在降级中遇到哪些问题?这些问题是如何解决掉的?本篇文章具备十分高的参考借鉴价值。

一、背景

vivo 离线数仓 Hadoop 集群基于 CDH 5.14.4 版本构建,CDH 5.14.4 Hadoop 版本:2.6.0+CDH 5.14.4+2785,是 Cloudera 公司基于 Apache Hadoop 2.6.0 版本打入了一些优化 patch 后的 Hadoop 发行版。

近几年随着 vivo 业务倒退,数据爆炸式增长,离线数仓 HDFS 集群从一个扩大到十个,规模靠近万台。随着 HDFS 集群规模的增长,以后版本的 HDFS 的一些 痛点问题 也裸露进去:

  • 在以后低版本的 HDFS,线上环境 NameNode 经常出现 RPC 性能问题,用户 Hive/Spark 离线工作也会因为 NameNode RPC 性能变慢导致工作提早。
  • 一些 RPC 性能问题在 HDFS 3.x 版本均已修复,以后只能通过打入 HDFS 高版本 patch 的形式解决线上 NameNode RPC 性能问题。
  • 频繁的 patch 合并减少了 HDFS 代码保护的复杂度,每一个 patch 的上线都须要重启 NameNode 或者 DataNode,减少了 HDFS 集群的运维老本。
  • 线上 HDFS 集群应用 viewfs 对外提供服务,公司外部业务线泛滥,很多业务部门申请了独立的 HDFS 客户端拜访离线数仓集群。当批改线上 HDFS 配置后,更新 HDFS 客户端配置是一件十分耗时且麻烦的事件。
  • HDFS 2.x 不反对 EC,冷数据无奈应用 EC 来升高存储老本。

Hadoop 3.x 的第一个稳固版本在 2017 年底就曾经公布了,有了很多重大的改良。在 HDFS 方面,反对了 Erasure Coding、More than 2 NameNodes、Router-Based Federation、Standby NameNode Read、FairCallQueue、Intra-datanode balancer 等新个性。HDFS 3.x 新个性在稳定性、性能、老本等多个方面带来诸多 收益

  • HDFS Standby NameNode Read、FairCallQueue 新个性以及 HDFS 3.x NameNode RPC 优化 patch 能极大晋升咱们以后版本 HDFS 集群稳定性与 RPC 性能。
  • HDFS RBF 代替 viewfs,简化 HDFS 客户端配置更新流程,解决线上更新泛滥 HDFS 客户端配置的痛点问题。
  • HDFS EC 利用冷数据存储,升高存储老本。

基于以上痛点问题与收益,咱们决定将离线数仓 HDFS 集群降级到 HDFS 3.x 版本。

二、HDFS 降级版本抉择

因为咱们 Hadoop 集群基于 CDH 5.14.4 版本构建,咱们首先思考降级到 CDH 高版本。CDH 7 提供 HDFS 3.x 发行版,遗憾是 CDH 7 没有免费版,咱们只能抉择降级到 Apache 版本或者 Hortonworks 公司提供的 HDP 发行版。

因为 Apache Hadoop 没有提供管理工具,对于万台规模的 HDFS 集群,治理配置、散发配置极其不不便。因而,咱们抉择了Hortonworks HDP 发行版,HDFS 管理工具抉择 Ambari。

Hortonworks 提供的最新的稳固的收费的 Hadoop 发行版为 HDP-3.1.4.0-315 版本。Hadoop 版本为 Apache Hadoop 3.1.1 版本。

三、HDFS 降级计划制订

3.1 降级计划

HDFS 官网提供两种降级计划:Express 和 RollingUpgrade

  • Express 降级过程是进行现有 HDFS 服务,而后应用新版本 HDFS 启动服务,会影响线上业务失常运行。
  • RollingUpgrade 降级过程是滚动降级,不停服务,对用户无感知。

鉴于 HDFS 停服对业务影响较大,咱们最终抉择 RollingUpgrade计划。

3.2 降级计划

RollingUpgrade 计划中,有两种回退形式:Rollback 和 RollingDowngrade 

  • Rollback 会把 HDFS 版本连同数据状态回退到降级前的那一刻,会造成数据失落。
  • RollingDowngrade 只回退 HDFS 版本,数据不受影响。

咱们线上 HDFS 集群是不能容忍数据失落的,咱们最终抉择 RollingDowngrade 的回退计划。

3.3  HDFS 客户端降级计划

线上 Spark、Hive、Flink、OLAP 等计算组件重度依赖 HDFS Client,局部计算组件版本过低,须要降级到高版本能力反对 HDFS 3.x,降级 HDFS Client 有较高风险。

咱们在测试环境通过多轮测试,验证了 HDFS 3.x 兼容 HDFS 2.x client 读写。

因而,咱们本次 HDFS 降级只降级 NameNode、JournalNode、DataNode 组件,HDFS 2.x Client 等 YARN 降级后再降级。

3.4 HDFS 滚动降级步骤

RollingUpgrade 降级的操作流程在 Hadoop 官网降级文档中有介绍,概括起来大抵步骤如下:

  1. JournalNode 降级,应用新版本顺次重启 JournalNode。
  2. NameNode 降级筹备,生成 rollback fsimage 文件。
  3. 应用新版本 Hadoop 重启 Standby NameNode,重启 ZKFC。
  4. NameNode HA 主从切换,使降级后的 NameNode 变成 Active 节点。
  5. 应用新版本 Hadoop 重启另一个 NameNode,重启 ZKFC。
  6. 降级 DataNode,应用新版本 Hadoop 滚动重启所有 DataNode 节点。
  7. 执行 Finalize,确认 HDFS 集群降级到新版本。

四、管理工具如何共存

HDFS 2.x 集群,HDFS、YARN、Hive、HBase 等组件,应用 CM 工具治理。因为只降级 HDFS,HDFS 3.x 应用 Ambari 治理,其它组件如 YARN、Hive 依然应用 CM 治理。HDFS 2.x client 不降级,持续应用 CM 治理。Zookeeper 应用原 CM 部署的 ZK。

具体实现:CM Server 节点部署 Amari Server,CM Agent 节点部署 Ambari Agent。

如上图所示,应用 Ambari 工具在 master/slave 节点部署 HDFS 3.x NameNode/DataNode 组件,因为端口抵触,Ambari 部署的 HDFS 3.x 会启动失败,不会对线上 CM 部署的 HDFS 2.x 集群产生影响。

HDFS 降级开始后,master 节点进行 CM JN/ZKFC/NN,启动 Ambari JN/ZKFC/NN,slave 节点进行 CM DN,启动 Ambari DN。HDFS 降级的同时实现管理工具从 CM 切换到 Ambari。

五、HDFS 滚动降级降级过程中遇到的问题

5.1 HDFS 社区已修复的不兼容问题

HDFS 社区已修复滚动降级、降级过程中要害不兼容的问题。相干 issue 号为:HDFS-13596、HDFS-14396、HDFS-14831。

【HDFS-13596】: 修复 Active NamNode 降级后将 EC 相干的数据结构写入 EditLog 文件,导致 Standby NameNode 读取 EditLog 异样间接 Shutdown 的问题。

【HDFS-14396】:修复 NameNode 降级到 HDFS 3.x 版本后,将 EC 相干的数据结构写入 Fsimage 文件,导致 NameNode 降级到 HDFS 2.x 版本辨认 Fsimage 文件异样的问题。

【HDFS-14831】:修复 NameNode 降级后对 StringTable 的批改导致 HDFS 降级后 Fsimage 不兼容问题。

咱们降级的 HDP HDFS 版本引入了上述三个 issue 相干的代码。除此之外,咱们在降级过程中还遇到了其它的不兼容问题:

5.2 JournalNode 降级呈现 Unknown protocol

JournalNode 降级过程中,呈现的问题:

Unknown protocol: org.apache.hadoop.hdfs.qjournal.protocol.InterQJournalProtocol

org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.ipc.RpcNoSuchProtocolException): Unknown protocol: org.apache.hadoop.hdfs.qjournal.protocol.InterQJournalProtocol
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.getProtocolImpl(ProtobufRpcEngine.java:557)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:596)
        at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1073)
        at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2281)
        at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2277)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:415)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1924)
        at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2275)
        at org.apache.hadoop.ipc.Client.getRpcResponse(Client.java:1498)
        at org.apache.hadoop.ipc.Client.call(Client.java:1444)
        at org.apache.hadoop.ipc.Client.call(Client.java:1354)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:228)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:116)
        at com.sun.proxy.$Proxy14.getEditLogManifestFromJournal(Unknown Source)
        at org.apache.hadoop.hdfs.qjournal.protocolPB.InterQJournalProtocolTranslatorPB.getEditLogManifestFromJournal(InterQJournalProtocolTranslatorPB.java:75)
        at org.apache.hadoop.hdfs.qjournal.server.JournalNodeSyncer.syncWithJournalAtIndex(JournalNodeSyncer.java:250)
        at org.apache.hadoop.hdfs.qjournal.server.JournalNodeSyncer.syncJournals(JournalNodeSyncer.java:226)
        at org.apache.hadoop.hdfs.qjournal.server.JournalNodeSyncer.lambda$startSyncJournalsDaemon$0(JournalNodeSyncer.java:186)
        at java.lang.Thread.run(Thread.java:748)

报错起因:HDFS 3.x 新增了 InterQJournalProtocol,新减少的 InterQJournalProtocol 用于 JournalNode 之间同步旧的 edits 数据。

HDFS-14942 对此问题进行了优化,日志级别从 ERROR 改成 DEBUG。此问题不影响降级,当三个 HDFS 2.x JN 全副降级为 HDFS 3.x JN 时,JN 之间能失常同步数据。

5.3 NameNode 降级 DatanodeProtocol.proto 不兼容

NameNode 降级后,DatanodeProtocol.proto 不兼容,导致 Datanode BlockReport 无奈进行。

(1)HDFS 2.6.0 版本

DatanodeProtocol.proto

message HeartbeatResponseProto {
  repeated DatanodeCommandProto cmds = 1; // Returned commands can be null
  required NNHAStatusHeartbeatProto haStatus = 2;
  optional RollingUpgradeStatusProto rollingUpgradeStatus = 3;
  optional uint64 fullBlockReportLeaseId = 4 [default = 0];
  optional RollingUpgradeStatusProto rollingUpgradeStatusV2 = 5;
}

(2)HDFS 3.1.1 版本

DatanodeProtocol.proto

message HeartbeatResponseProto {
  repeated DatanodeCommandProto cmds = 1; // Returned commands can be null
  required NNHAStatusHeartbeatProto haStatus = 2;
  optional RollingUpgradeStatusProto rollingUpgradeStatus = 3;
  optional RollingUpgradeStatusProto rollingUpgradeStatusV2 = 4;
  optional uint64 fullBlockReportLeaseId = 5 [default = 0];
}

咱们能够看到两个版本 HeartbeatResponseProto 的 第 4、5 个参数地位调换了

这个问题的 起因在于,Hadoop 3.1.1 版本 commit 了 HDFS-9788,用来解决 HDFS 降级时兼容低版本问题,而 HDFS 2.6.0 版本没有 commit,导致了 DatanodeProtocol.proto 不兼容。

HDFS 降级过程中,不须要兼容低版本 HDFS,只须要兼容低版本 HDFS client。

因而,HDFS 3.x 不须要 HDFS-9788 兼容低版本的性能,咱们在 Hadoop 3.1.1 版本回退了 HDFS-9788 的批改来放弃和 HDFS 2.6.0 版本的 DatanodeProtocol.proto 兼容。

5.4  NameNode 降级 layoutVersion 不兼容

NameNode 降级后,NameNode layoutVersion 扭转,导致 EditLog 不兼容,HDFS 3.x 降级到 HDFS 2.x NameNode 无奈启动。

2021-04-12 20:15:39,571 ERROR org.apache.hadoop.hdfs.server.namenode.EditLogInputStream: caught exception initializing XXX:8480/getJournal
id=test-53-39&segmentTxId=371054&storageInfo=-60%3A1589021536%3A0%3Acluster7
org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream$LogHeaderCorruptException: Unexpected version of the file system log file: -64. Current version = -60.
        at org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream.readLogVersion(EditLogFileInputStream.java:397)
        at org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream.init(EditLogFileInputStream.java:146)
        at org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream.nextopImpl(EditLogFileInputStream.java:192)
        at org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream.nextop(EditLogFileInputStream.java:250)
        at org.apache.hadoop.hdfs.server.namenode.EditLogInputStream.read0p(EditLogInputStream.java:85)
        at org.apache.hadoop.hdfs.server.namenode.EditLogInputStream.skipUntil(EditLogInputStream.java:151)
        at org.apache.hadoop.hdfs.server.namenode.RedundantEditLogInputStream.next0p(RedundantEditLogInputStream.java:178)
        at org.apache.hadoop.hdfs.server.namenode.EditLogInputStream.readop(EditLogInputStream.java:85)
        at org.apache.hadoop.hdfs.server.namenode.EditLogInputStream.skipUntil(EditLogInputStream.java:151)
        at org.apache.hadoop.hdfs.server.namenode.RedundantEditLogInputStream.next0p(RedundantEditLogInputStream.java:178)
        at org.apache.hadoop.hdfs.server.namenode.EditLogInputStream.read0p(EditLogInputStream.java:85)
        at org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.LoadEditRecords(FSEditLogLoader.java:188)
        at org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.LoadFSEdits(FSEditLogLoader.java:141)
        at org.apache.hadoop.hdfs.server.namenode.FSImage.loadEdits(FSImage.java:903)
        at org.apache.hadoop.hdfs.server.namenode.FSImage.LoadFSImage(FSImage.java:756)
        at org.apache.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:324)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.LoadFSImage(FSNamesystem.java:1150)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.LoadFromDisk(FSNamesystem.java:797)
        at org.apache.hadoop.hdfs.server.namenode.NameNode.LoadNamesystem (NameNode.java:614)
        at org.apache.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:676)
        at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:844)
        at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:823)
        at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode (NameNode.java:1547)
        at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1615)

HDFS 2.6.0 降级到 HDFS 3.1.1,NameNode layoutVersion 值 -60 变更成 -64。要解决这个问题,首先搞清楚 NameNode layoutVersion 什么状况下会变更?

HDFS 版本升级引入新个性,NameNode layoutVersion 追随新个性变更。Hadoop 官网降级文档指出,HDFS 滚动降级过程中要禁用新个性,保障降级过程中 layoutVersion 不变,降级后的 HDFS 3.x 版本能力回退到 HDFS 2.x 版本。

接下来,找出 HDFS 2.6.0 降级到 HDFS 3.1.1 引入了哪一个新个性导致 namenode layoutVersion 变更?查看 HDFS-5223、HDFS-8432、HDFS-3107 相干 issue,HDFS 2.7.0 版本引入了 truncate 性能,NameNode layoutVersion 变成 -61。查看 HDFS 3.x 版本 NameNodeLayoutVersion 代码:

NameNodeLayoutVersion

public enum Feature implements LayoutFeature {ROLLING_UPGRADE(-55, -53, -55, "Support rolling upgrade", false),
  EDITLOG_LENGTH(-56, -56, "Add length field to every edit log op"),
  XATTRS(-57, -57, "Extended attributes"),
  CREATE_OVERWRITE(-58, -58, "Use single editlog record for" +
    "creating file with overwrite"),
  XATTRS_NAMESPACE_EXT(-59, -59, "Increase number of xattr namespaces"),
  BLOCK_STORAGE_POLICY(-60, -60, "Block Storage policy"),
  TRUNCATE(-61, -61, "Truncate"),
  APPEND_NEW_BLOCK(-62, -61, "Support appending to new block"),
  QUOTA_BY_STORAGE_TYPE(-63, -61, "Support quota for specific storage types"),
  ERASURE_CODING(-64, -61, "Support erasure coding");

TRUNCATE、APPEND\_NEW\_BLOCK、QUOTA\_BY\_STORAGE\_TYPE、ERASURE\_CODING 四个 Feature 设置了 minCompatLV 为 -61。

查看最终 NameNode layoutVersion 取值逻辑:

FSNamesystem

static int getEffectiveLayoutVersion(boolean isRollingUpgrade, int storageLV,
    int minCompatLV, int currentLV) {if (isRollingUpgrade) {if (storageLV <= minCompatLV) {
      // The prior layout version satisfies the minimum compatible layout
      // version of the current software.  Keep reporting the prior layout
      // as the effective one.  Downgrade is possible.
      return storageLV;
    }
  }
  // The current software cannot satisfy the layout version of the prior
  // software.  Proceed with using the current layout version.
  return currentLV;
}

getEffectiveLayoutVersion 获取最终失效的 layoutVersion,storageLV 是以后 HDFS 2.6.0 版本 layoutVersion -60,minCompatLV 是 -61,currentLV 是降级后的 HDFS 3.1.1 版本 layoutVersion -64。

从代码判断逻辑能够看出,HDFS 2.6.0 版本 layoutVersion -60 小于等于 minCompatLV 是 -61 不成立,因而,降级到 HDFS 3.1.1 版本后,namenode layoutVersion 的取值为 currentLV -64。

从上述代码剖析能够看出,HDFS 2.7.0 版本引入了 truncate 性能后,HDFS 社区只反对 HDFS 3.x 降级到 HDFS 2.7 版本的 NameNode layoutVersion 是兼容的。

咱们对 HDFS truncate 性能进行评估,联合业务场景剖析,咱们 vivo 外部离线剖析临时没有应用 HDFS truncate 性能的场景。基于此,咱们批改了 HDFS 3.1.1 版本的 minCompatLV 为 -60,用来反对 HDFS 2.6.0 降级到 HDFS 3.1.1 版本后可能降级到 HDFS 2.6.0。

minCompatLV 批改为 -60:

NameNodeLayoutVersion

public enum Feature implements LayoutFeature {ROLLING_UPGRADE(-55, -53, -55, "Support rolling upgrade", false),
  EDITLOG_LENGTH(-56, -56, "Add length field to every edit log op"),
  XATTRS(-57, -57, "Extended attributes"),
  CREATE_OVERWRITE(-58, -58, "Use single editlog record for" +
    "creating file with overwrite"),
  XATTRS_NAMESPACE_EXT(-59, -59, "Increase number of xattr namespaces"),
  BLOCK_STORAGE_POLICY(-60, -60, "Block Storage policy"),
  TRUNCATE(-61, -60, "Truncate"),
  APPEND_NEW_BLOCK(-62, -60, "Support appending to new block"),
  QUOTA_BY_STORAGE_TYPE(-63, -60, "Support quota for specific storage types"),
  ERASURE_CODING(-64, -60, "Support erasure coding");

5.5 DataNode 降级 layoutVersion 不兼容

DataNode 降级后,DataNode layoutVersion 不兼容,HDFS 3.x DataNode 降级到 HDFS 2.x DataNode 无奈启动。

2021-04-19 10:41:01,144 WARN org.apache.hadoop.hdfs.server.common.Storage: Failed to add storage directory [DISK]file:/data/dfs/dn/
org.apache.hadoop.hdfs.server.common.IncorrectVersionException: Unexpected version of storage directory /data/dfs/dn. Reported: -57. Expecting = -56.
        at org.apache.hadoop.hdfs.server.common.StorageInfo.setLayoutVersion(StorageInfo.java:178)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.setFieldsFromProperties(DataStorage.java:665)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.setFieldsFromProperties(DataStorage.java:657)
        at org.apache.hadoop.hdfs.server.common.StorageInfo.readProperties(StorageInfo.java:232)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.doTransition(DataStorage.java:759)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.LoadStorageDirectory(DataStorage.java:302)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.LoadDataStorage(DataStorage.java:418)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.addStorageLocations(DataStorage.java:397)
        at org.apache.hadoop.hdfs.server.datanode.DataStorage.recoverTransitionRead(DataStorage.java:575)
        at org.apache.hadoop.hdfs.server.datanode.DataNode.initStorage(DataNode.java:1560)
        at org.apache.hadoop.hdfs.server.datanode.DataNode.initBLockPool(DataNode.java:1520)
        at org.apache.hadoop.hdfs.server.datanode.BPOfferService.verifyAndSetNamespaceInfo(BPOfferService.java:341)
        at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.connectToNNAndHandshake(BPServiceActor.java:219)
        at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.run(BPServiceActor.java:673)
        at java.lang.Thread.run(Thread.java:748)

HDFS 2.6.0 DataNode layoutVersion 是 -56,HDFS 3.1.1 DataNode layoutVersion 是 -57。

DataNode layoutVersion 扭转的起因:Hadoop 社区自 HDFS-2.8.0  commit HDFS-8791 后,对 DataNode 的 Layout 进行了降级,DataNode Block Pool 数据块目录存储构造从 256 x 256 个目录变成了 32 x 32 个目录。目标是通过缩小 DataNode 目录层级来优化 Du 操作引发的性能问题。

DataNode Layout 降级过程:

  1. rename 以后 current 目录,到 previous.tmp。
  2. 新建 current 目录,并且建设 hardlink 从 previous.tmp 到新 current 目录。
  3. rename 目录 previous.tmp 为 previous 目录。

Layout 降级流程图:

DN Layout 降级过程中存储目录构造:

hardlink 的 link 关联模式图:

查看 DataNodeLayoutVersion 代码,定义了 32 x 32 个目录构造的 layoutVersion 是 -57。阐明 DataNode Layout 降级须要扭转 layoutVersion。

DataNodeLayoutVersion

public enum Feature implements LayoutFeature {FIRST_LAYOUT(-55, -53, "First datanode layout", false),
  BLOCKID_BASED_LAYOUT(-56,
      "The block ID of a finalized block uniquely determines its position" +
      "in the directory structure"),
  BLOCKID_BASED_LAYOUT_32_by_32(-57,
      "Identical to the block id based layout (-56) except it uses a smaller"
      + "directory structure (32x32)");

咱们在测试环境进行 DataNode Layout 降级发现有如下问题:DataNode 创立新的 current 目录并建设 hardlink 的过程十分耗时,100 万 block 数的 DataNode 从 Layout 降级开始到对外提供读写服务须要 5 分钟。这对于咱们靠近万台 DataNode 的 HDFS 集群是不能承受的,难以在预约的降级工夫窗口内实现 DataNode 的降级。

因而,咱们在 HDFS 3.1.1 版本回退了 HDFS-8791,DataNode 不进行 Layout 降级。测试发现 100~200 万 block 数的 DataNode 降级只须要 90~180 秒,比照 Layout 降级工夫大幅缩短。

回退了 HDFS-8791,DataNode Du 带来的性能问题怎么解决呢?

咱们梳理了 HDFS 3.3.0 版本的 patch,发现了 HDFS-14313 从内存中计算 DataNode 应用空间,不再应用 Du 操作,完满的解决了 DataNode Du 性能问题。咱们在降级后的 HDFS 3.1.1 版本打入 HDFS-14313,解决了 DataNode 降级后 Du 操作带来的 io 性能问题。

5.6 DataNode Trash 目录解决

上图所示,DataNode 降级过程中,DataNode 在删除 Block 时,是不会真的将 Block 删除的,而是先将 Block 文件放到磁盘 BlockPool 目录下一个 trash 目录中,为了可能应用原来的 rollback_fsimage 复原降级过程中删除的数据。咱们集群磁盘的均匀水位始终在 80%,原本就很缓和,降级期间 trash 中的大量 Block 文件会对集群稳定性造成很大威逼。

思考到咱们的计划回退形式是滚动降级而非 Rollback,并不会用到 trash 中的 Block。所以咱们应用脚本定时对 trash 中的 Block 文件进行删除,这样能够大大减少 Datanode 上磁盘的存储压力。

5.7  其它问题

上述就是咱们 HDFS 降级降级过程中遇到的所有不兼容问题。除了不兼容问题,咱们还在降级的 HDP HDFS 3.1.1 版本引入了一些 NameNode RPC 优化 patch。

HDFS 2.6.0 版本 FoldedTreeSet 红黑树数据结构导致 NameNode 运行一段时间后 RPC 性能降落,集群呈现大量 StaleDataNode,导致工作读取 block 块失败。Hadoop 3.4.0 HDFS-13671 修复了这个问题,将 FoldedTreeSet 回退为原来的 LightWeightResizableGSet 链表数据结构。咱们也将 HDFS-13671 patch 引入咱们降级的 HDP HDFS 3.1.1 版本。

降级后 HDFS-13671 的优化成果:集群 StaleDataNode 数量大幅缩小。

六、测试与上线

咱们在 2021 年 3 月份启动离线数仓集群 HDFS 降级专项,在测试环境搭建了多套 HDFS 集群进行了 viewfs 模式下多轮 HDFS 降级、降级演练。一直的总结与欠缺降级计划,解决降级过程中遇到的问题。

6.1 全量组件 HDFS 客户端兼容性测试

在 HDFS 降级中只降级了 Server 端,HDFS Client 还是 HDFS 2.6.0 版本。因而,咱们要保障业务通过 HDFS 2.6.0 Client 能失常读写 HDFS 3.1.1 集群。

咱们在测试环境,搭建了线上环境相似的 HDFS 测试集群,联结计算组共事与业务部门,对 Hive、Spark、OLAP(kylin、presto、druid)、算法平台应用 HDFS 2.6.0 Client 读写 HDFS 3.1.1,模仿线上环境进行了全量业务的兼容性测试。确认 HDFS 2.6.0 Client 能失常读写 HDFS 3.1.1 集群,兼容性失常。

6.2 降级操作脚本化

咱们严格梳理了 HDFS 降级降级的命令,梳理了每一步操作的危险与注意事项。通过 CM、Ambari API 启停 HDFS 服务。将这些操作都整顿成 python 脚本,缩小人为操作带来的危险。

6.3 降级点检

咱们梳理了 HDFS 降级过程中的关键点检事项,确保 HDFS 降级过程中呈现问题能第一工夫发现,进行回退,降底对业务的影响。

6.4 正式降级

咱们在测试环境中进行了屡次 HDFS 降级降级演练,实现 HDFS 兼容性测试相干的工作,公司外部写了多篇 WIKI 文档进行记录。

确认测试环境 HDFS 降级降级没问题之后,咱们开始了降级之路。

相干的具体里程碑上线过程如下:

  • 2021 年 3~4 月,梳理 HDFS 3.x 版本新个性与相干 patch,浏览 HDFS 滚动降级降级的源码,确定最终降级的 HDFS 3.x 版本。实现 HDFS 2.x 已有优化 patch 与 HDFS 3.x 高版本 patch 移植到降级的 HDFS 3.x 版本。
  • 2021 年 5~8 月 ,进行 HDFS 降级降级演练,全量 Hive、Spark、OLAP(kylin、presto、druid) 兼容性测试,确定 HDFS 降级降级计划没有问题。
  • 2021 年 9 月 ,yarn 日志聚合 HDFS 集群(百台) 降级到 HDP HDFS 3.1.1,期间修复日志聚合大量 ls 调用导致的 RPC 性能问题,业务未受到影响。
  • 2021 年 11 月 ,7 个离线数仓 HDFS 集群(5000 台左右) 降级到 HDP HDFS 3.1.1,用户无感知,业务未受到影响。
  • 2022 年 1 月 ,实现离线数仓 HDFS 集群(10 个集群规模靠近万台) 降级到 HDP HDFS 3.1.1,用户无感知,业务未受到影响。

降级之后,咱们对离线数仓各个集群进行了察看,目前 HDFS 服务运行失常。

七、总结

咱们耗时一年工夫将万台规模的离线数仓 HDFS 集群从 CDH HDFS 2.6.0 降级到了 HDP HDFS 3.1.1 版本,管理工具从 CM 胜利切换到了 Ambari。

HDFS 降级过程漫长,然而收益是十分多的,HDFS 降级为后续 YARN、Hive/Spark、HBase 组件降级打下了根底。

在此基础上,咱们能够持续做十分有意义的工作,继续在稳定性、性能、老本等多个方面深刻摸索,应用技术为公司发明可见的价值。

参考资料

  1. https://issues.apache.org/jira/browse/HDFS-13596
  2. https://issues.apache.org/jira/browse/HDFS-14396
  3. https://issues.apache.org/jira/browse/HDFS-14831
  4. https://issues.apache.org/jira/browse/HDFS-14942
  5. https://issues.apache.org/jira/browse/HDFS-9788
  6. https://issues.apache.org/jira/browse/HDFS-3107
  7. https://issues.apache.org/jira/browse/HDFS-8791
  8. https://issues.apache.org/jira/browse/HDFS-14313
  9. https://issues.apache.org/jira/browse/HDFS-13671
退出移动版