vivo 萬台規模 HDFS 集群升級 HDFS 3.x 實踐

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-5223HDFS-8432HDFS-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升級。測試發現100200萬block數的DataNode升級只需要90180秒,對比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. //issues.apache.org/jira/browse/HDFS-13596

  2. //issues.apache.org/jira/browse/HDFS-14396

  3. //issues.apache.org/jira/browse/HDFS-14831

  4. //issues.apache.org/jira/browse/HDFS-14942

  5. //issues.apache.org/jira/browse/HDFS-9788

  6. //issues.apache.org/jira/browse/HDFS-3107

  7. //issues.apache.org/jira/browse/HDFS-8791

  8. //issues.apache.org/jira/browse/HDFS-14313

  9. //issues.apache.org/jira/browse/HDFS-13671