大資料謝列3:Hdfs的HA實現

lillcol發表於2021-01-27

在之前的文章:大資料系列:一文初識Hdfs
大資料系列2:Hdfs的讀寫操作 中Hdfs的組成、讀寫有簡單的介紹。

在裡面介紹Secondary NameNode和Hdfs讀寫的流程。
並且在文章結尾也說了,Secondary NameNode並不是我常說的HA,(High Availability)

本文承接之前的內容,對Hdfs的HA實現做個簡單的介紹。


NameNode的重要性

先來看看Hdfs讀寫的流程圖:

可以看到無論是讀還是寫,我們都必須和儲存後設資料的NameNode進行互動。

一旦NameNode出問題,整個叢集的讀寫基本上就涼涼了。

所以,在設計的時候本就有做一些容錯的措施。


容錯的措施

如果NameNode出了問題,整個檔案Hdfs將不可用。

設想一個場景:
如果以為某種原因我們執行NameNode的機器被刪除。

此時所有Hdfs的檔案都會丟失,因為我們雖然在DataNode中儲存了檔案的Block資訊,但是我們無法通過這些Block重新構建檔案。

所以讓NameNode有一定抗拒錯誤的能力是很重要的。

Hadoop本身提供了兩種機制。


備份後設資料

第一種機制:
將Hdfs後設資料中持久化狀態的檔案做多個備份。

Hadoop 可以進行相關的配置,把這些持久的狀態備份到多個檔案系統。這個過程是同步且原子的 。
一般都是配置為本份到本地磁碟,同時遠端NFS 掛載

Secondary NameNode

第二種機制:
執行一個Secondary NameNode,負責定期合併名稱namespace iamgeedit log,以防止edit log變得過大。

合併流程如下,詳細過程之前有介紹過,此處不累贅:


存在問題

雖然有兩個方式讓Hdfs有一定的容錯能力,但是還是存在一些問題。

Secondary NameNode擁有一個合併的namespace iamge像的副本,在NameNode失敗時可以使用該iamge

但是Secondary NameNode中的狀態是滯後於NameNode的,如果NameNode是當機的情況下,資料丟失必不可少。

此時通常的做法是將NFS上的NameNode後設資料檔案複製到Secondary NameNode,並將其作為新的NameNode執行。

因為NFS中的與NameNode的後設資料是同步的,所以可以通過NFS中的資料恢復。

在多個檔案系統上覆制NameNode後設資料和使用Secondary NameNode建立checkpoints的組合可以防止資料丟失。

然而這種組合的機制並不能提供Hdfs的高可用性。

主要原因是這種方式恢復太耗時間了。

NameNode是唯一的儲存了檔案與Blcok對映關係的後設資料儲存庫
,一旦它罷工,所有的Client的作業包括Mapreduce、讀、寫、列出檔案無法進行。
在新的NameNode上線之前,整個叢集處於停止服務的狀態

如果NameNode要重新上線服務需要經過以下下的步驟:

  1. 載入名稱空間到記憶體
  2. 重放edit log
  3. DataNode接收足夠的Block報告並離開安全模式。

在具有許多檔案和Block的大型叢集上,NameNode的冷啟動所需的時間可能是30分鐘或更長。

這麼拉垮可不太能接受,無論是日常運維還是計劃停機之類的操作。

所以,歸根結底就是NameNode仍然是存在單點故障(SPOF,Single Point Of Failure)

下面就看看怎麼解決這個問題。


HDFS high availability (HA)

一般情況下會有兩個場景和HA息息相關。

  1. 在出現計劃外事件(如伺服器當機)的情況下,在重新啟動NameNode之前,叢集將不可用。
  2. 有計劃的維護事件,例如NameNode機器上的軟體或硬體升級,將導致叢集停機。

為了解決上述的問題,Hadoop 提供了HDFS high availability (HA)的支援。

通過active-standby的方式配置兩個NameNode(3.0後可以支援超過兩個),在Active/Passive動配置中使用熱備份。

在機器崩潰的情況下,實現快速故障轉移到新的NameNode
或者出於計劃維護的目的,允許管理員發起優雅的故障轉移。

為了實現這個目標,在架構層面需要做一些改變:

  1. NameNode間必須要通過一些高可用的共享記憶體共享edit log,一旦standby NameNode接管工作的時候,它通過讀取共享edit log 直至結尾以保持它的狀態與active NameNode一致,然後繼續讀取由active NameNode寫入的新條目。
  2. DataNodes必須向兩個NameNode傳送Block報告,因為NameNode對映關係是儲存在DataNode的記憶體而不是磁碟。
  3. 客戶端必須使用一種機制來處理NameNode失效問題,讓使用者對於NameNode的切換是透明的。(就是不需要使用者自己去切換NameNode
  4. standby NameNode應該承擔之前Secondary NameNode的角色,定期為active NameNode 的名稱空間建立檢查點。

在典型的HA叢集中,會配置兩個或更多的NameNodes,但是在任何時候,都只能有一個NameNode處於活動狀態,其他NameNode處於備用狀態。
Active狀態的NameNode負責叢集中的所有Client的操作,
Standby狀態的NameNode僅需要儲存足夠的狀態的即可。

有兩種實現方式


Quorum Journal Manager (QJM)

QJM是一個專用的HDFS實現,設計的唯一目的是提供高度可用的編輯日誌,並且是大多數HDFS安裝的推薦選擇。

QJM作為一組journalnodes(JNs)獨立守護程式執行,每次編輯都必須寫入大部分JNs節點。

為了使備節點與主節點保持狀態同步,兩個節點都與JNs程式通訊。

Active NameNode執行任何關於名稱空間修改時,它會持久地記錄修改記錄到大部分的JNs

通常,有三個journal節點,因此係統可以容忍其中一個節點的丟失。
儘管這種方式類似ZooKeeper工作方式,但是必須說明的是QJM實現沒有使用ZooKeeper
同時還要注意,HDFS的HA確實使用了ZooKeeper來選擇active NameNode,後續會介紹

standby NameNode能夠從JNs中讀取edits ,並不斷監視它們對edit log的修改。

standby NameNode````看到這edits時,它將它們應用到自己的名稱空間。

standby NameNode必須持有叢集中NameNode位置的最新資訊。
所以,DataNodes需要配置所有NameNode的位置,並向所有NameNode傳送Block 位置資訊和心跳。

因為standby NameNode 在記憶體中有最新的狀態:最新的edit log和最新的NameNode對映
所以Active NameNode發生故障時,standby NameNode可以很快接管(在幾十秒內)。

實際觀察到的故障轉移時間要長一些(大約一分鐘左右),因為系統需要保守地判斷active NameNode是否發生了故障。


NFS

QJM方共享不同,edit log記錄到JNs不同,

此處的共享記憶體一般使用過一個支援NFS(Network File System:網路檔案系統)的NAS(Network Attached Storage:網路附屬儲存)建立應共享檔案,通常會掛載在每隔一個NameNode的機器上,NameNode可以對其進行讀、寫操作。

但是,目前只支援一個共享edit目錄。
因此,系統的可用性受到共享edit目錄的可用性的限制,
為了消除所有單點故障,共享編輯目錄需要有冗餘。

具體來說,就是到儲存的多條網路路徑,以及儲存本身的冗餘(磁碟、網路和電源)。
所以,建議使用高質量的專用NAS裝置作為共享儲存伺服器,而不是簡單的Linux伺服器。


故障轉移和規避

HA叢集中,standby NameNode還執行名稱空間狀態檢查點操作,
因此在HA叢集中沒有必要執行 Secondary NameNode, CheckpointNode, or BackupNode

同時無論是QJM還是NFS中,
在同一時刻只有一個NameNode處於Active是至關重要的。
否則,名稱空間狀態將很快在兩者之間出現分歧(腦裂),可能導致資料丟失或其他不正確的結果。
為了避免這個情況,同一時間只允許一個NameNode 對 JournalNodes 或 NFS的共享記憶體寫入。

在故障轉移期間,即將成為Active狀態的NameNode將接管向共享eidt檔案寫入資料的角色,
這將有效地阻止其他NameNode繼續處於Active狀態,從而允許新的Active狀態安全地進行故障轉移。

但是要如何實現呢?


故障轉移

從active NameNodestandby NameNode```` 的過渡由系統中稱為故障轉移控制器(failover controller)的新實體進行管理。

可以理解為active NameNode掛了standby NameNode上位的過程

有各種各樣的故障轉移控制器,但是預設的實現是使用ZooKeeper來確保只有一個NameNodeactive的。

每個NameNode執行一個輕量級的故障轉移控制器程式,
它的任務是監視它的NameNode是否出現故障(使用一個簡單的心跳機制),
並在NameNode出現故障時觸發故障轉移。

故障轉移也可以由管理員手動啟動,
例如,在例行維護的情況下。
這被稱為優雅的故障轉移( graceful failover),因為故障轉移控制器為兩個NameNode安排了有序的角色轉換。

但是,
在意料之外的故障轉移的情況下,不可能確保失敗的NameNode已經停止執行。

例如,網路或網路分割槽速度較慢的情況下,
即使以前active NameNode仍然在執行
也可能會觸發故障轉移。

為了避免這種情況,HA實現了確保以前active NameNode不會造成任不良影響的方法稱為規避(fencing)。


規避

JNs一次只允許一個NameNode寫入編輯日誌;
但是,以前活躍的NameNode仍然可能向客戶端提供過時的讀請求,
因此可以設定一個SSH 規避命令來殺死NameNode程式。

對於使用NFS檔案的共享edit log,因為很難控制一次只允許一個NameNode寫入,
需要更強有力地規避方法,一般可以選擇如下方法:

  1. 撤銷NameNode對共享儲存目錄的訪問(通常使用特定於供應商的NFS命令)
  2. 通過遠端管理命令遮蔽相應網路埠(port)
  3. 做絕一點可以通過一個叫做“一槍爆頭”方式進行規避(STONITH,short the other node in the head),通過一個特定的供電單元對相應的NameNode進行斷電操作。

這就是推薦使用QJM的主要原因。

有了上述只是儲備,我們很容易就可以把這個架構圖畫出來,以QJM為例

那麼轉移過程大致如下:


客戶端故障轉移

對於客戶端而言,我們要讓使用者對於故障轉移是無感的。
可以通過配置檔案實現一個故障轉移

在配置檔案中通過配置幾個引數實現故障轉移:

dfs.nameservices:Hdfs邏輯名稱

<property>
  <name>dfs.nameservices</name>
  <value>mycluster</value>
</property>

dfs.ha.NameNodes.[nameservice ID]:nameservice中每個NameNode的唯一識別符號

<property>
  <name>dfs.ha.NameNodes.mycluster</name>
  <value>nn1,nn2, nn3</value>
</property>

dfs.NameNode.rpc-address.[nameservice ID].[name node ID]:每個NameNode監聽的完全限定RPC地址

<property>
  <name>dfs.NameNode.rpc-address.mycluster.nn1</name>
  <value>machine1.example.com:8020</value>
</property>
<property>
  <name>dfs.NameNode.rpc-address.mycluster.nn2</name>
  <value>machine2.example.com:8020</value>
</property>
<property>
  <name>dfs.NameNode.rpc-address.mycluster.nn3</name>
  <value>machine3.example.com:8020</value>
</property>

更多的配置可以檢視
NameNode HA With QJM
NameNode HA With NFS


關於HA暫時介紹到這裡,後續有空再進行詳細的介紹。
之後會寫一下關於Yarn的文章,感興趣可以關注【兔八哥雜談】

相關文章