在之前的文章:大資料系列:一文初識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 iamge
和edit 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
要重新上線服務需要經過以下下的步驟:
- 載入名稱空間到記憶體
- 重放
edit log
- 從
DataNode
接收足夠的Block
報告並離開安全模式。
在具有許多檔案和Block
的大型叢集上,NameNode
的冷啟動所需的時間可能是30分鐘或更長。
這麼拉垮可不太能接受,無論是日常運維還是計劃停機之類的操作。
所以,歸根結底就是NameNode
仍然是存在單點故障(SPOF,Single Point Of Failure
)
下面就看看怎麼解決這個問題。
HDFS high availability (HA)
一般情況下會有兩個場景和HA息息相關。
- 在出現計劃外事件(如伺服器當機)的情況下,在重新啟動
NameNode
之前,叢集將不可用。 - 有計劃的維護事件,例如
NameNode
機器上的軟體或硬體升級,將導致叢集停機。
為了解決上述的問題,Hadoop
提供了HDFS high availability (HA)
的支援。
通過active-standby
的方式配置兩個NameNode
(3.0後可以支援超過兩個),在Active/Passive
動配置中使用熱備份。
在機器崩潰的情況下,實現快速故障轉移到新的NameNode
,
或者出於計劃維護的目的,允許管理員發起優雅的故障轉移。
為了實現這個目標,在架構層面需要做一些改變:
NameNode
間必須要通過一些高可用的共享記憶體共享edit log
,一旦standby NameNode
接管工作的時候,它通過讀取共享edit log
直至結尾以保持它的狀態與active NameNode
一致,然後繼續讀取由activeNameNode
寫入的新條目。DataNodes
必須向兩個NameNode
傳送Block
報告,因為NameNode
對映關係是儲存在DataNode
的記憶體而不是磁碟。- 客戶端必須使用一種機制來處理
NameNode
失效問題,讓使用者對於NameNode
的切換是透明的。(就是不需要使用者自己去切換NameNode
) 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來選擇activeNameNode
,後續會介紹
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 NameNode
到standby
NameNode```` 的過渡由系統中稱為故障轉移控制器(failover controller)的新實體進行管理。
可以理解為
active NameNode
掛了standby NameNode
上位的過程
有各種各樣的故障轉移控制器,但是預設的實現是使用ZooKeeper來確保只有一個NameNode
是active
的。
每個NameNode
執行一個輕量級的故障轉移控制器程式,
它的任務是監視它的NameNode
是否出現故障(使用一個簡單的心跳機制),
並在NameNode
出現故障時觸發故障轉移。
故障轉移也可以由管理員手動啟動,
例如,在例行維護的情況下。
這被稱為優雅的故障轉移( graceful failover
),因為故障轉移控制器為兩個NameNode
安排了有序的角色轉換。
但是,
在意料之外的故障轉移的情況下,不可能確保失敗的NameNode
已經停止執行。
例如,網路或網路分割槽速度較慢的情況下,
即使以前activeNameNode
仍然在執行
也可能會觸發故障轉移。
為了避免這種情況,HA實現了確保以前active NameNode
不會造成任不良影響的方法稱為規避(fencing
)。
規避
JNs一次只允許一個NameNode
寫入編輯日誌;
但是,以前活躍的NameNode
仍然可能向客戶端提供過時的讀請求,
因此可以設定一個SSH 規避命令來殺死NameNode
程式。
對於使用NFS檔案的共享edit log
,因為很難控制一次只允許一個NameNode
寫入,
需要更強有力地規避方法,一般可以選擇如下方法:
- 撤銷
NameNode
對共享儲存目錄的訪問(通常使用特定於供應商的NFS命令) - 通過遠端管理命令遮蔽相應網路埠(port)
- 做絕一點可以通過一個叫做“一槍爆頭”方式進行規避(
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的文章,感興趣可以關注【兔八哥雜談】