1、概述
Hadoop2.X中的HDFS(Vsersion2.0)相比於Hadoop1.X增加了兩個重要功能,HA和Federation。HA解決了Hadoop1.X Namenode中一直存在的單點故障問題,HA策略通過熱備的方式爲主NameNode提供一個備用者,並且這個備用者的狀態一直和主Namenode的元數據保持一致,一旦主NameNode掛了,備用NameNode可以立馬轉換變換爲主NameNode,從而提供不間斷的服務。另外,Federation特性,主要是允許一個 HDFS 集羣中存在多個 NameNode 同時對外提供服務,這些 NameNode 分管一部分目錄(水平切分),彼此之間相互隔離,但共享底層的 DataNode 存儲資源。本文檔主要是總結我自己個人利用爲QJM(Quorum Journal Manager)爲公司測試集羣(hadoop2.2.0)部署HA策略的過程以及自己在部署過程中遇到的一些問題。
2、HDFSHA基本架構
先來看個手動擋切換的HA架構圖:
在一個典型的 HDFS HA 場景中,通常由兩個NameNode 組成,一個處於Active狀態,另一個處於Standby狀態。Active NameNode 對外提供服務,比如處理來自客戶端的 RPC 請求,而 Standby NameNode 則不對外提供服務,僅同步 Active NameNode的狀態,以便能夠在它失敗時快速進行切換。
爲了能夠實時同步 Active 和 Standby 兩個 NameNode 的元數據信息(實際上editlog),需提供一個共享存儲系統,可以是 NFSQJ(Quorum Journal Manager)或者 Bookeeper,Active NameNode 將數據寫入共享存儲系統,我們可以在Active NameNode的50070端口上看到相應的NameNode Journal Status信息:
同時Standby NameNode監聽該系統(QJM管理下的Journalnode進程對應的存儲路徑),一旦發現有新數據寫入,則讀取這些數據,並加載到自己內存中,以保證自己內存狀態與 Active NameNode 保持基本一致,那麼在緊急情況下 standby NameNode便可快速切爲Active NameNode。另外,在Hadoop1.X中的Secondary NameNode或者自己通過nfs熱備的NameNode信息在Hadoop2.X中已經不再需要了,他們被Standby NameNode取代了。 在Yarn的官網中,我還看到一段關於JournalNode錯誤兼容性信息:
大概意思是主備NameNode 之間通過一組JournalNode 同步元數據信息(我的通俗理解就是QJM類似一個數據池,池裏邊裝着多個JournalNode進程存儲editlog,Active NameNode往池裏邊的JournalNode進程寫editlog,StandBy NameNode向池裏邊的JournalNode取數據同步),一條數據只要成功寫入多數 JournalNode 即認爲寫入成功。啓動的JournalNode的個數必須爲奇數個。如果你的HA策略中啓動了N個JournalNode進程那麼整個QJM最多允許(N-1)/2個進程死掉,這樣才能保證editLog成功完整地被寫入。比如 3個 JournalNode 時,最多允許 1 個 JournalNode掛掉,5 個 JournalNode 時,最多允許 2 個 JournalNode 掛掉。
3、 HDFS HA部署
3.1、部署和測試環境
HDFS HA的部署和驗證是在公司的測試集羣中完成,其中測試集羣中數據節點一共有4個主機名分別爲hadoop-slave1、hadoop-slave02、hadoop-slave03、hadoop-slave04,master節點的主機名爲hadoop-master。因爲JournalNode和Zookeeper進程是非常輕量級的,可以其他服務共用節點。現在的部署情況是:
hadoop-master:作爲Active NameNode
haoop-slave01: 作爲StandBy NameNode
hadoop-slave02: 作爲DataNode,並且啓動一個JournalNode、啓動一個Zookeeper
hadoop-slave03: 作爲DataNode,並且啓動一個JournalNode、啓動一個Zookeeper
hadoop-slave04: 作爲DataNode,並且啓動一個JournalNode、啓動一個Zookeeper
其他軟件:
Apache Hadoop 2.2.0、JDK1.6
3.2、修改配置文件
主要配置${HADOOP_HOME}/etc/hadoop/下的hdfs-site.xml。下面是一些配置參數以及說明:
(1) dfs.nameservices
HDFS的命名服務邏輯名稱,可以自己定義。在已經配置HA策略的HDFS會用到這個邏輯名稱,同時該名稱也會被基於HDFS的系統用,例如HBASE等。另外,如果需要啓動HDFS Federation的話,可以通過該參數指定多個服務邏輯名稱,用“,”作爲分隔符。
我的配置如下:
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
<description>Logical name forthis new nameservice</description>
</property>
(2) dfs.ha.namenodes.[$nameserviceID]
命名服務下面包含的NameNode列表,可爲每個NameNode 指定一個自定義的 ID 名稱,比如命名服務 testCluster 下有兩個 NameNode,分別命名爲 nn1 和 nn2(到目前爲止一個命名服務下最多包含2個NameNode),我的配置如下:
<property>
<name>dfs.ha.namenodes.testCluster</name>
<value>nn1,nn2</value>
<description>Unique identifiers for each NameNode in the nameservice </description>
</property>
(3) dfs.namenode.rpc-address.[$nameserviceID].[$name node ID]
這個參數很容易理解,主要是爲每個NameNode設置RPC地址,我的配置如下:
<property>
<name>dfs.namenode.rpc-address.testCluster.nn1</name>
<value>hadoop-master:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.testCluster.nn2</name>
<value>hadoop-slave01:8020</value>
</property>
(4) dfs.namenode.http-address.[$nameserviceID].[$name node ID]
這個參數主要是爲NameNode設置對外的HTTP地址, 通過此配置的指定你可以執行在瀏覽器中管理HDFS界面等操作。我的配置如下:
<property>
<name>dfs.namenode.http-address.testCluster.nn1</name>
<value>hadoop-master:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.testCluster.nn2</name>
<value>hadoop-slave01:50070</value>
</property>
(5) dfs.namenode.shared.edits.dir
設置一組JournalNode的URL地址,ActiveNameNode會將Edit Log寫入這些JournalNode所配置的本地目錄(可以用nfs等共享文件系統,由參數dfs.journalnode.edits.dir控制)中,而StandByNameNode通過DataNode的心跳通知去讀取這些Edit Log,並且作用在內存中的目錄樹中,其配置格式爲:qjournal://host1:port1;host2:port2;host3:port3/journalId,我的配置如下:
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://hadoop-slave02:8485;hadoop-slave03:8485;hadoop-slave04:8485/testcluster</value>
<description>journalNodeList</description>
</property>
(6) dfs.journalnode.edits.dir
這個就是剛剛提到的JournalNode所在節點上的一個目錄,用於存放 editlog 和其他狀態信息,該參數只能設置一個目錄,你可以對磁盤做 RIAD 提高數據可靠性。
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/hadoop/hadoop-2.2.0/journal/node/local/data</value>
</property>
(7) dfs.client.failover.proxy.provider.[$nameserviceID]
該參數設置HDFS客戶端與ActiveName進行交互的JAVA實現類,HDFS客戶端通過該參數來尋找到集羣中的Active NameNode,此類默認實現ConfiguredFailoverProxyProvider,我的配置如下:
<property>
<name>dfs.client.failover.proxy.provider.testcluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
(8) dfs.ha.fencing.methods
這個參數比較重要,主要用於在主備節點切換時實現隔離機制的,在官方網站中做了相當詳細的配置說明,其大概意思爲:主備架構解決單點故障問題時,必須要認真解決的是腦裂問題,即出現兩個 master 同時對外提供服務,導致系統處於不一致狀態,可能導致數據丟失等潛在問題。在 HDFS HA中,JournalNode 只允許一個 NameNode 寫數據,不會出現兩個 Active NameNode 的問題,但是,當主備切換時,之前的 Active NameNode 可能仍在處理客戶端的 RPC 請求,爲此,需要增加隔離機制(fencing)將之前的 Active NameNode 殺死。HDFS 允許用戶配置多個隔離機制,當發生主備切換時,將順次執行這些隔離機制,直到一個返回成功。Hadoop 2.0 內部打包了兩種類型的隔離機制,分別是 shell 和 sshfence。
1)sshfence方式
sshfence 通過 ssh 登錄到前一個 ActiveNameNode 並將其殺死。爲了讓該機制成功執行,需配置免密碼 ssh 登陸(注意:這個爲主備節點配置雙向的RSA免密碼登陸),這可通過參數 dfs.ha.fencing.ssh.private-key-files 指定一個私鑰文件。我的配置如下:
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/hadoop/.ssh/id_rsa</value>
</property>
另外,在設置一個超時時間,一旦 ssh 超過該時間,則認爲執行失敗。我的配置如下:
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
2) shell方式(我沒有采用這種方式)
執行自定義的Shell腳本命令隔離舊的ActiveNameNode。相比於sshfence方式,個人認爲這種方式有個好處就是,你在shell腳本里邊可以將之前的 Active NameNode 直接kill掉,然後立馬啓動NameNode,此時剛剛啓動的NameNode就是立馬處於一個StandBy狀態,立馬就可以進入HA狀態,如果採用sshfence方式還要手動自己重啓剛剛被kill掉的NameNode從而才能進入HA(這些的前提都是,採用手動HA方式,之前的Acitve NameNode不是宕機而僅僅是NameNode進程掛掉)。配置可以爲:
<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>
</property>
注意, Hadoop 中所有參數將以環境變量的形似提供給該 shell,但所有的“.”被替換成了“_”,比如“dfs.namenode.rpc-address.ns1.nn1”變爲“dfs_namenode_rpc-address”。
3.3、啓動各種服務
HDFS集羣進程啓動的大概順序爲:啓動所有的JournalNodeà啓動nn1和nn2à啓動所有DataNode。具體詳細步驟如下:
(1) 啓動所有JournalNode
在所有的配置有JournalNode的服務節點上,以我的配置就是在hadoop-slave02、hadoop-slave03和hadoop-slave04上分別執行:
$HADOOP_HOME/sbin/hdfs-daemon.sh startjournalnode
(2) 初始化JournalNode
此步驟要注意的是,如果你是將非HA HDFS的集羣轉化成爲HA HDFS那麼這一步驟就需要,如果都是HA HDFS就不需要執行此步驟。該步驟的主要作用是將非HA HDFS中NameNode的edit log去初始化JourNalnodes。具體操作在nn1上執行:
$HADOOP_HOME/bin/hdfs namenode -initializeSharedEdits [-force | -nonInteractive]。
此命令默認是交互式的,需要用戶輸入各種YOR N,如果嫌麻煩就直接執行:
$HADOOP_HOME/bin/hdfs namenode–initializeSharedEdits –force
(3) 啓動nn1和nn2
子步驟1:
進入nn1,如果是新集羣則format(注意,如果不是新集羣千萬不要format):
$HADOOP_HOME/bin/hadoop namenode -format
子步驟2:進入nn1,接着啓動nn1:
hadoop-daemon.sh start namenode
子步驟3:進入nn2,執行下面命令讓nn2從nn1上將最新的FSp_w_picpath信息拉回來:
注意:如果是nn2的NameNode已經是被format掉了或者是將非HA HDFS的集羣轉化成爲HA HDFS則不需要執行這一個步驟。
$HADOOP_HOME/bin/hdfs namenode -bootstrapStandby -force
子步驟4:進入nn2,然後啓動nn2:
hadoop-daemon.sh start namenode
子步驟5:啓動所有的DataNode
在各個DataNode節點執行:
hadoop-daemon.sh start datanode
或者直接在nn1節點直接執行:
hadoop-daemons.sh start namenode
各個服務到現在爲止已經啓動完畢,主備節點都還處於StandBy狀態。我們可以看到主備節點的信息:
在這裏說說一個遇到的“錯誤”問題,我在分別啓動nn1和nn2之後,還沒有將其中一個切換爲Acive NameNode時,在nn1和nn2的日誌上都報了以下這個“錯誤”:
其實這個錯誤信息完全可以不用管,出現這個問題原因上面信息已經很明顯了,只要接下來將其中一個切換成Acive NameNode就ok了。
3.4、手動切換Active NameNode
nn1和nn2啓動後都處於StandBy狀態,此時都不能夠對外提供服務,現在需要將nn1切換爲Active NameNode,進入nn1節點輸入:
$HADOOP_HOME/bin/hdfs haadmin-transitionToActive nn1
切換後我們再看看50070頁面,nn1已經被切換爲Active了:
在來看看之前還沒有切換Acive NameNode的“錯誤”信息已經消失了,下面分別是nn1和nn2的日誌信息,非常正常:
另外,如果你現在想將nn2轉化爲Acive NameNode,則在進入nn2所在節點,輸入命令:
$HADOOP_HOME/bin/hdfs haadmin-failover --forcefence --forceactive nn1 nn2
看看nn2上的日誌:
在這裏說說我在切換時遇到過的幾個小問題:
在住備節點上一定要配置雙向的RSA免密碼登陸,不然再切換時會出錯,sshfence方式不能找到舊的Active NameNode,直接被reject掉。
第二,在切換的過程中我遇到了這個錯誤:
這個是權限問題,解決方法是直接在core-site.xml文件添加下面權限控制選項:
<property>
<name>hadoop.http.filter.initializers</name>
<value>org.apache.hadoop.security.AuthenticationFilterInitializer</value>
</property>
<property>
<name>hadoop.http.authentication.type</name>
<value>simple</value>
</property>
<property>
<name>hadoop.http.authentication.token.validity</name>
<value>36000</value>
</property>
<property>
<name>hadoop.http.authentication.signature.secret.file</name>
<value>/home/hadoop/hadoop-http-auth-signature-secret</value>
</property>
<property>
<name>hadoop.http.authentication.cookie.domain</name>
<value></value>
</property>
<property>
<name>hadoop.http.authentication.simple.anonymous.allowed</name>
<value>true</value>
</property>
然後建立/home/hadoop/hadoop-http-auth-signature-secret文件,並且在文件寫入訪問用戶,我寫入的是hadoop,然後將這個文件scp到各個節點,問題解決。
如果你將Active NameNode從nn1轉到nn2後,在各個DataNode日誌出現一個“錯誤”信息:
其實這個是我意料之中的“錯誤”信息,其實是沒有任何問題的。因爲,當你的Acive NameNode從nn1切換至nn2時,nn1就會被kill(即hadoop-msater中的NameNode進程會被kill掉),在上面切換日誌我標註紅方框的地方已經很清楚了。此時,各個DataNode還是會同時向Active NameNode和StandBy NameNode同時發送心跳信息的,因爲nn1已經被kill掉了,所有會報這個信息,對系統沒有任何影響,切換後正常使用,如果你重啓nn1則不會再報信息了,新啓動的nn1是處於StandBy模式的。
我們知道,StandByNameNode是不處理DataNode的RPC請求的,那麼各個DataNode爲什麼還會同時向Active NameNode和StandBy NameNode同時發送心跳呢?這是因爲這2個心跳的用途是不同的,各個DataNode向Active NameNode發送心跳主要是彙報數據塊的狀態信息,而向StandBy NameNode發心跳的主要目的是通知StandBy NameNode告訴它Active NameNode元數據發生了改變,要求StandBy NameNode去QJM區下載更改過的Edit Log信息。
3.5、配置自動切換模式
自動切換模式的實現需要下面兩個組建的額支持:
(1) Zookeeper實例
需要質數個Zookeeper實例,在本集羣我一個啓用了3個Zookeeper實例,分別部署在hadoop-slave02、hadoop-slave03、hadoop-slave04中。
(2) ZKFailoverController(簡稱“ZKFC”)
ZKFC 是一個 Zookeeper客戶端,負責監控和管理 NameNode 的狀態,每臺運行 NameNode的機器上也會運行一個 ZKFC 進程。
健康狀況監控:
ZKFC 週期性地與本地的NameNode 交互,執行一些健康狀況監測命令。
Zookeeper session 管理:
如果本地 NameNode 是健康的,則會持有Zookeeper 上一個 znode,如果它是 Active 的,會持有 zookeeper 的僅有的一個特殊 znode,該 znode 類型爲 ephemeral,一旦 namenode 掛掉後,會自動消失。
基於 zookeeper 的選舉:
如果本地 NameNode 是活的,而沒有其他 Namenode持有特殊的 znode,ZKFC 將嘗試獲取這個 znode,一旦獲取成功後,則認爲它“贏得了選舉”,進而隔離之前的Active namenode,自己轉換爲新的 Active namenode。其大概結構如下圖:
具體配置步驟:
步驟1:關閉集羣修改hdfs-site.xml配置文件,增加自動切換選項:
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
步驟2:編輯core-site.xml文件,添加Zookeeper實例:
<property>
<name>ha.zookeeper.quorum</name>
<value>hadoop-slave02:2181,hadoop-slave03:2181,hadoop-slave04:2181</value>
</property>
步驟3:啓動節點上的zookeeper實例:
分別進入hadoop-slave02、hadoop-slave03、hadoop-slave04節點執行:
$ZOOKEEPER_HOME/bin/zkServer.sh start
Zookeeper實例對應的進程名爲:
步驟4:初始化zookeeper。
注意:這個步驟是針對第一次啓動zookeeper實例用的,如果你的zookeeper實例不是第一次啓動則不需要執行此命令。
$HADOOP_HOME/bin/hdfs zkfc -formatZK
步驟5:啓動 JournalNode、NameNode 和 DataNode。
步驟6:啓動ZKFC。
分別進入hadoop-master和hadoop-slave1即在各個 NameNode 節點上執行:
$HADOOP_HOME/sbin/hadoop-daemon.sh startzkfc
ZKFC對應的進程名爲:
要注意的一點是:我們最先啓動的NameNode爲Active NameNode。現在爲止配置完畢,驗證請看下面一小節。
4、 HDFS HA機制的可用性驗證
4.1手動切換模式驗證
這裏我使用的驗證方法主要是模擬ActiveNameNode進程死掉的情況,另外Active NameNode所在節點發生宕機的情況也是一樣的。現在集羣中nn1爲Active NameNode,nn2爲StandBy NameNode,具體步驟:
步驟1:進入nn1所在節點即hadoop-master,運行kill -9 $NameNodePID將nn1殺死(此時集羣中只有一個StandByNameNode)。
步驟2:往集羣上傳文件,或者執行hadoop fs相關命令提示連接不到(此時,集羣中沒有Active NameNode來處理客戶端的RPC請求)。看錯誤信息:
步驟3:恢復集羣,將StandBy NameNode轉換爲Active NameNode。進入nn2所在節點即hadoop-slave01執行:
$HADOOP_HOME/bin/hdfshaadmin -transitionToActive nn2
此時,nn2已經變成爲Active NameNode,看50070:
步驟4:再次執行hadoop fs相關命令或者上傳文件,一切正常。
步驟5:另外,不要忘記集羣雖然是恢復了,但是此時已經沒有了StandBy NameNode了,這是直接進入nn1所在節點啓動NameNode進程,此時nn1爲Standby NameNode。
當目前爲止,一起驗證以及恢復已經完成。各個服務的日誌也恢復了正常。
4.2、自動切換模式驗證
自動切換模式的驗證和手動切換基本一樣,還是手動kill掉Active NameNode進程,觀察集羣是否會自動恢復,將備用節點轉換爲Active NameNode。經過測試,當手動kill掉Active NameNode後,Standby NameNode成功地自動轉換爲Active NameNode繼續服務於個個DataNode。
參考文獻:
[1] http://hadoop.apache.org/docs/r2.2.0/hadoop-yarn/hadoop-yarn-site/HDFSHighAvailabilityWithQJM.html
[2] http://www.sizeofvoid.net/hadoop-2-0-namenode-ha-federation-practice-zh/