vivo 萬臺規模 HDFS 叢集升級 HDFS 3.x 實踐

vivo網際網路技術發表於2022-05-16

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. 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

相關文章