Hadoop組件之-HDFS(HA實現細節)

NameNode 高可用總體架構概述

在 Hadoop 1.0 時代,Hadoop 的兩大核心組件 HDFS NameNode 和 JobTracker 都存在着單點問題,這其中以 NameNode 的單點問題尤其嚴重。html

由於 NameNode 保存了整個 HDFS 的元數據信息,一旦 NameNode 掛掉,整個 HDFS 就沒法訪問,同時 Hadoop 生態系統中依賴於 HDFS 的各個組件,node

包括 MapReduce、Hive、Pig 以及 HBase 等也都沒法正常工做,而且從新啓動 NameNode 和進行數據恢復的過程也會比較耗時。shell

這些問題在給 Hadoop 的使用者帶來困擾的同時,也極大地限制了 Hadoop 的使用場景,使得 Hadoop 在很長的時間內僅能用做離線存儲和離線計算,網絡

沒法應用到對可用性和數據一致性要求很高的在線應用場景中。session

所幸的是,在 Hadoop2.0 中,HDFS NameNode 和 YARN ResourceManger(JobTracker 在 2.0 中已經被整合到 YARN ResourceManger 之中) 的單點問題都獲得瞭解決,通過多個版本的迭代和發展,目前已經能用於生產環境。HDFS NameNode 和 YARN ResourceManger 的高可用 (High Availability,HA) 方案基本相似,二者也複用了部分代碼,可是因爲 HDFS NameNode 對於數據存儲和數據一致性的要求比 YARN ResourceManger 高得多,因此 HDFS NameNode 的高可用實現更爲複雜一些,本文從內部實現的角度對 HDFS NameNode 的高可用機制進行詳細的分析。架構

HDFS NameNode 的高可用總體架構如圖 1 所示 (圖片來源於參考文獻 [1]):運維

圖 1.HDFS NameNode 高可用總體架構

 

從上圖中,咱們能夠看出 NameNode 的高可用架構主要分爲下面幾個部分:ssh

Active NameNode 和 Standby NameNode:兩臺 NameNode 造成互備,一臺處於 Active 狀態,爲主 NameNode,分佈式

另一臺處於 Standby 狀態,爲備 NameNode,只有主 NameNode 才能對外提供讀寫服務。函數

主備切換控制器 ZKFailoverController:ZKFailoverController 做爲獨立的進程運行,對 NameNode 的主備切換進行整體控制。ZKFailoverController 能及時檢測到 NameNode 的健康情況,在主 NameNode 故障時藉助 Zookeeper 實現自動的主備選舉和切換,固然 NameNode 目前也支持不依賴於 Zookeeper 的手動主備切換。

Zookeeper 集羣:爲主備切換控制器提供主備選舉支持。

共享存儲系統:共享存儲系統是實現 NameNode 的高可用最爲關鍵的部分,共享存儲系統保存了 NameNode 在運行過程當中所產生的 HDFS 的元數據。主 NameNode 和

NameNode 經過共享存儲系統實現元數據同步。在進行主備切換的時候,新的主 NameNode 在確認元數據徹底同步以後才能繼續對外提供服務。

DataNode 節點:除了經過共享存儲系統共享 HDFS 的元數據信息以外,主 NameNode 和備 NameNode 還須要共享 HDFS 的數據塊和 DataNode 之間的映射關係。DataNode 會同時向主 NameNode 和備 NameNode 上報數據塊的位置信息。

下面開始分別介紹 NameNode 的主備切換實現和共享存儲系統的實現,在文章的最後會結合筆者的實踐介紹一下在 NameNode 的高可用運維中的一些注意事項。

  

NameNode 的主備切換實現

NameNode 主備切換主要由 ZKFailoverController、HealthMonitor 和 ActiveStandbyElector 這 3 個組件來協同實現:

ZKFailoverController 做爲 NameNode 機器上一個獨立的進程啓動 (在 hdfs 啓動腳本之中的進程名爲 zkfc),

啓動的時候會建立 HealthMonitor 和 ActiveStandbyElector 這兩個主要的內部組件,ZKFailoverController 在建立 HealthMonitor 和 ActiveStandbyElector 的同時,

也會向 HealthMonitor 和 ActiveStandbyElector 註冊相應的回調方法。

HealthMonitor 主要負責檢測 NameNode 的健康狀態,若是檢測到 NameNode 的狀態發生變化,會回調 ZKFailoverController 的相應方法進行自動的主備選舉。

ActiveStandbyElector 主要負責完成自動的主備選舉,內部封裝了 Zookeeper 的處理邏輯,一旦 Zookeeper 主備選舉完成,會回調 ZKFailoverController 的相應方法來進行 NameNode 的主備狀態切換。

NameNode 實現主備切換的流程如圖 2 所示,有如下幾步:

  1. HealthMonitor 初始化完成以後會啓動內部的線程來定時調用對應 NameNode 的 HAServiceProtocol RPC 接口的方法,對 NameNode 的健康狀態進行檢測。
  2. HealthMonitor 若是檢測到 NameNode 的健康狀態發生變化,會回調 ZKFailoverController 註冊的相應方法進行處理。
  3. 若是 ZKFailoverController 判斷須要進行主備切換,會首先使用 ActiveStandbyElector 來進行自動的主備選舉。
  4. ActiveStandbyElector 與 Zookeeper 進行交互完成自動的主備選舉。
  5. ActiveStandbyElector 在主備選舉完成後,會回調 ZKFailoverController 的相應方法來通知當前的 NameNode 成爲主 NameNode 或備 NameNode。
  6. ZKFailoverController 調用對應 NameNode 的 HAServiceProtocol RPC 接口的方法將 NameNode 轉換爲 Active 狀態或 Standby 狀態。
圖 2.NameNode 的主備切換流程

 

下面分別對 HealthMonitor、ActiveStandbyElector 和 ZKFailoverController 的實現細節進行分析:

HealthMonitor 實現分析

ZKFailoverController 在初始化的時候會建立 HealthMonitor,HealthMonitor 在內部會啓動一個線程來循環調用 NameNode 的 HAServiceProtocol RPC 接口的方法來檢測 NameNode 的狀態,並將狀態的變化經過回調的方式來通知 ZKFailoverController。

HealthMonitor 主要檢測 NameNode 的兩類狀態,分別是 HealthMonitor.State 和 HAServiceStatus。HealthMonitor.State 是經過 HAServiceProtocol RPC 接口的 monitorHealth 方法來獲取的,反映了 NameNode 節點的健康情況,主要是磁盤存儲資源是否充足。HealthMonitor.State 包括下面幾種狀態:

  • INITIALIZING:HealthMonitor 在初始化過程當中,尚未開始進行健康情況檢測;
  • SERVICE_HEALTHY:NameNode 狀態正常;
  • SERVICE_NOT_RESPONDING:調用 NameNode 的 monitorHealth 方法調用無響應或響應超時;
  • SERVICE_UNHEALTHY:NameNode 還在運行,可是 monitorHealth 方法返回狀態不正常,磁盤存儲資源不足;
  • HEALTH_MONITOR_FAILED:HealthMonitor 本身在運行過程當中發生了異常,不能繼續檢測 NameNode 的健康情況,會致使 ZKFailoverController 進程退出;

HealthMonitor.State 在狀態檢測之中起主要的做用,在 HealthMonitor.State 發生變化的時候,HealthMonitor 會回調 ZKFailoverController 的相應方法來進行處理,

具體處理見後文 ZKFailoverController 部分所述。

而 HAServiceStatus 則是經過 HAServiceProtocol RPC 接口的 getServiceStatus 方法來獲取的,主要反映的是 NameNode 的 HA 狀態,包括:

  • INITIALIZING:NameNode 在初始化過程當中;
  • ACTIVE:當前 NameNode 爲主 NameNode;
  • STANDBY:當前 NameNode 爲備 NameNode;
  • STOPPING:當前 NameNode 已中止;

HAServiceStatus 在狀態檢測之中只是起輔助的做用,在 HAServiceStatus 發生變化時,HealthMonitor 也會回調 ZKFailoverController 的相應方法來進行處理,

具體處理見後文 ZKFailoverController 部分所述。

ActiveStandbyElector 實現分析

Namenode(包括 YARN ResourceManager) 的主備選舉是經過 ActiveStandbyElector 來完成的,

ActiveStandbyElector 主要是利用了 Zookeeper 的寫一致性和臨時節點機制,具體的主備選舉實現以下:

建立鎖節點

若是 HealthMonitor 檢測到對應的 NameNode 的狀態正常,那麼表示這個 NameNode 有資格參加 Zookeeper 的主備選舉。若是目前尚未進行過主備選舉的話,

那麼相應的 ActiveStandbyElector 就會發起一次主備選舉,嘗試在 Zookeeper 上建立一個路徑爲/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 的臨時節點 (${dfs.nameservices} 爲 Hadoop 的配置參數 dfs.nameservices 的值,下同),Zookeeper 的寫一致性會保證最終只會有一個 ActiveStandbyElector 建立成功,

那麼建立成功的 ActiveStandbyElector 對應的 NameNode 就會成爲主 NameNode,ActiveStandbyElector 會回調 ZKFailoverController 的方法進一步將對應的 NameNode 切換爲 Active 狀態。而建立失敗的 ActiveStandbyElector 對應的 NameNode 成爲備 NameNode,ActiveStandbyElector 會回調 ZKFailoverController 的方法進一步將對應的 NameNode 切換爲 Standby 狀態。

註冊 Watcher 監聽

無論建立/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 節點是否成功,ActiveStandbyElector 隨後都會向 Zookeeper 註冊一個 Watcher 來監聽這個節點的狀態變化事件,ActiveStandbyElector 主要關注這個節點的 NodeDeleted 事件。

自動觸發主備選舉

若是 Active NameNode 對應的 HealthMonitor 檢測到 NameNode 的狀態異常時, ZKFailoverController 會主動刪除當前在 Zookeeper 上創建的臨時節點/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock,這樣處於 Standby 狀態的 NameNode 的 ActiveStandbyElector 註冊的監聽器就會收到這個節點的 NodeDeleted 事件。收到這個事件以後,會立刻再次進入到建立/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 節點的流程,若是建立成功,這個原本處於 Standby 狀態的 NameNode 就選舉爲主 NameNode 並隨後開始切換爲 Active 狀態。

固然,若是是 Active 狀態的 NameNode 所在的機器整個宕掉的話,那麼根據 Zookeeper 的臨時節點特性,/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 節點會自動被刪除,從而也會自動進行一次主備切換。

防止腦裂

Zookeeper 在工程實踐的過程當中常常會發生的一個現象就是 Zookeeper 客戶端「假死」,所謂的「假死」是指若是 Zookeeper 客戶端機器負載太高或者正在進行 JVM Full GC,那麼可能會致使 Zookeeper 客戶端到 Zookeeper 服務端的心跳不能正常發出,一旦這個時間持續較長,超過了配置的 Zookeeper Session Timeout 參數的話,Zookeeper 服務端就會認爲客戶端的 session 已通過期從而將客戶端的 Session 關閉。「假死」有可能引發分佈式系統常說的雙主或腦裂 (brain-split) 現象。具體到本文所述的 NameNode,假設 NameNode1 當前爲 Active 狀態,NameNode2 當前爲 Standby 狀態。若是某一時刻 NameNode1 對應的 ZKFailoverController 進程發生了「假死」現象,那麼 Zookeeper 服務端會認爲 NameNode1 掛掉了,根據前面的主備切換邏輯,NameNode2 會替代 NameNode1 進入 Active 狀態。可是此時 NameNode1 可能仍然處於 Active 狀態正常運行,即便隨後 NameNode1 對應的 ZKFailoverController 由於負載降低或者 Full GC 結束而恢復了正常,感知到本身和 Zookeeper 的 Session 已經關閉,可是因爲網絡的延遲以及 CPU 線程調度的不肯定性,仍然有可能會在接下來的一段時間窗口內 NameNode1 認爲本身仍是處於 Active 狀態。這樣 NameNode1 和 NameNode2 都處於 Active 狀態,均可以對外提供服務。這種狀況對於 NameNode 這類對數據一致性要求很是高的系統來講是災難性的,數據會發生錯亂且沒法恢復。Zookeeper 社區對這種問題的解決方法叫作 fencing,中文翻譯爲隔離,也就是想辦法把舊的 Active NameNode 隔離起來,使它不能正常對外提供服務。

ActiveStandbyElector 爲了實現 fencing,會在成功建立 Zookeeper 節點 hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 從而成爲 Active NameNode 以後,建立另一個路徑爲/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 的持久節點,這個節點裏面保存了這個 Active NameNode 的地址信息。Active NameNode 的 ActiveStandbyElector 在正常的狀態下關閉 Zookeeper Session 的時候 (注意因爲/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 是臨時節點,也會隨之刪除),會一塊兒刪除節點/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb。可是若是 ActiveStandbyElector 在異常的狀態下 Zookeeper Session 關閉 (好比前述的 Zookeeper 假死),那麼因爲/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 是持久節點,會一直保留下來。後面當另外一個 NameNode 選主成功以後,會注意到上一個 Active NameNode 遺留下來的這個節點,從而會回調 ZKFailoverController 的方法對舊的 Active NameNode 進行 fencing,具體處理見後文 ZKFailoverController 部分所述。

ZKFailoverController 實現分析

ZKFailoverController 在建立 HealthMonitor 和 ActiveStandbyElector 的同時,會向 HealthMonitor 和 ActiveStandbyElector 註冊相應的回調函數,ZKFailoverController 的處理邏輯主要靠 HealthMonitor 和 ActiveStandbyElector 的回調函數來驅動。

對 HealthMonitor 狀態變化的處理

如前所述,HealthMonitor 會檢測 NameNode 的兩類狀態,HealthMonitor.State 在狀態檢測之中起主要的做用,ZKFailoverController 註冊到 HealthMonitor 上的處理 HealthMonitor.State 狀態變化的回調函數主要關注 SERVICE_HEALTHY、SERVICE_NOT_RESPONDING 和 SERVICE_UNHEALTHY 這 3 種狀態:

  • 若是檢測到狀態爲 SERVICE_HEALTHY,表示當前的 NameNode 有資格參加 Zookeeper 的主備選舉,若是目前尚未進行過主備選舉的話,ZKFailoverController 會調用 ActiveStandbyElector 的 joinElection 方法發起一次主備選舉。
  • 若是檢測到狀態爲 SERVICE_NOT_RESPONDING 或者是 SERVICE_UNHEALTHY,就表示當前的 NameNode 出現問題了,ZKFailoverController 會調用 ActiveStandbyElector 的 quitElection 方法刪除當前已經在 Zookeeper 上創建的臨時節點退出主備選舉,這樣其它的 NameNode 就有機會成爲主 NameNode。

而 HAServiceStatus 在狀態檢測之中僅起輔助的做用,在 HAServiceStatus 發生變化時,ZKFailoverController 註冊到 HealthMonitor 上的處理 HAServiceStatus 狀態變化的回調函數會判斷 NameNode 返回的 HAServiceStatus 和 ZKFailoverController 所指望的是否一致,若是不一致的話,ZKFailoverController 也會調用 ActiveStandbyElector 的 quitElection 方法刪除當前已經在 Zookeeper 上創建的臨時節點退出主備選舉。

對 ActiveStandbyElector 主備選舉狀態變化的處理

在 ActiveStandbyElector 的主備選舉狀態發生變化時,會回調 ZKFailoverController 註冊的回調函數來進行相應的處理:

  • 若是 ActiveStandbyElector 選主成功,那麼 ActiveStandbyElector 對應的 NameNode 成爲主 NameNode,ActiveStandbyElector 會回調 ZKFailoverController 的 becomeActive 方法,這個方法經過調用對應的 NameNode 的 HAServiceProtocol RPC 接口的 transitionToActive 方法,將 NameNode 轉換爲 Active 狀態。
  • 若是 ActiveStandbyElector 選主失敗,那麼 ActiveStandbyElector 對應的 NameNode 成爲備 NameNode,ActiveStandbyElector 會回調 ZKFailoverController 的 becomeStandby 方法,這個方法經過調用對應的 NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,將 NameNode 轉換爲 Standby 狀態。
  • 若是 ActiveStandbyElector 選主成功以後,發現了上一個 Active NameNode 遺留下來的/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 節點 (見「ActiveStandbyElector 實現分析」一節「防止腦裂」部分所述),那麼 ActiveStandbyElector 會首先回調 ZKFailoverController 註冊的 fenceOldActive 方法,嘗試對舊的 Active NameNode 進行 fencing,在進行 fencing 的時候,會執行如下的操做:
  1. 首先嚐試調用這個舊 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它轉換爲 Standby 狀態。
  2. 若是 transitionToStandby 方法調用失敗,那麼就執行 Hadoop 配置文件之中預約義的隔離措施,Hadoop 目前主要提供兩種隔離措施,一般會選擇 sshfence:
  • sshfence:經過 SSH 登陸到目標機器上,執行命令 fuser 將對應的進程殺死;
  • shellfence:執行一個用戶自定義的 shell 腳原本將對應的進程隔離;

只有在成功地執行完成 fencing 以後,選主成功的 ActiveStandbyElector 纔會回調 ZKFailoverController 的 becomeActive 方法將對應的 NameNode 轉換爲 Active 狀態,開始對外提供服務。

 

NameNode 的共享存儲實現

過去幾年中 Hadoop 社區涌現過不少的 NameNode 共享存儲方案,好比 shared NAS+NFS、BookKeeper、BackupNode 和 QJM(Quorum Journal Manager) 等等。目前社區已經把由 Clouderea 公司實現的基於 QJM 的方案合併到 HDFS 的 trunk 之中而且做爲默認的共享存儲實現,本部分只針對基於 QJM 的共享存儲方案的內部實現原理進行分析。爲了理解 QJM 的設計和實現,首先要對 NameNode 的元數據存儲結構有所瞭解。

具體看這裏:http://www.cnblogs.com/nucdy/p/5892179.html

相關文章
相關標籤/搜索