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 官方升級文件中有介紹,概括起來大致步驟如下:
-
JournalNode升級,使用新版本依次重啟 JournalNode。
-
NameNode升級準備,生成 rollback fsimage檔案。
-
使用新版本Hadoop重啟 Standby NameNode,重啟 ZKFC。
-
NameNode HA主從切換,使升級後的 NameNode 變成 Active 節點。
-
使用新版本 Hadoop 重啟另一個 NameNode,重啟 ZKFC。
-
升級 DataNode,使用新版本 Hadoop 滾動重啟所有 DataNode 節點。
-
執行 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升級過程:
-
rename當前current目錄,到previous.tmp。
-
新建current目錄,並且建立hardlink從previous.tmp到新current目錄。
-
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元件升級打下了基礎。
在此基礎上,我們可以繼續做非常有意義的工作,持續在穩定性、效能、成本等多個方面深入探索,使用技術為公司創造可見的價值。
參考資料