HA介紹
什麼是HA
HA: High Availability,高可用叢集,指的是叢集7*24小時不間斷服務。
為什麼需要HA
在HDFS中,有NameNode、DataNode和SecondaryNameNode角色的分布,用戶端所有的操作都是要與NameNode互動的,同時整個叢集的命名空間資訊也都儲存在NameNode節點。但是,現在的叢集配置中隻有一個NameNode,于是就有一個問題: 單點故障
那麼,什麼是單點故障呢?現在叢集中隻有一個NameNode,那麼假如這個NameNode意外當機、更新硬體等,導緻NameNode不可用了,整個叢集是不是也就不可用了?這就是單點故障的問題。
為了解決這樣的問題,就需要高可用叢集了。
高可用的備份方式
- 主從模式(冷備)
準備兩台伺服器, 準備相同的程式。 一台伺服器對外提供服務, 稱為主節點(Active節點); 另外一台伺服器平時不對外提供服務, 主要負責和Active節點之間進行資料的同步, 稱為備份節點(Standby節點). 當主節點出現故障, Standby節點可以自動提升為Active節點, 對外提供服務。
ZooKeeper實作的叢集高可用, 采用的就是這種模式。
我作為一個班級的講師,在班級負責授課的工作。如果我有一天生病請假了,是不是咱們班級就得自習一天了?為了解決這樣的問題,教學部安排另外一個講師,每天跟着我。我上課講課,他就在旁邊聽着;我上課提問問題,他就在旁邊看着;我去吃飯,他也在旁邊跟着;我去上個廁所,他也在跟着!做為我的一個影子存在着。由于這個同時每天都會跟着我,是以我的一言一行,講了什麼内容,留了什麼作業,吃了什麼飯,抽了幾根煙,他都知道!那麼,如果有一天我生病請假了,他是不是就可以直接替代我為班級上課呢?
- 雙主互備(熱備)(了解)
準備兩台伺服器, 準備相同的程式. 同時對外提供服務(此時, 這兩台伺服器彼此為對方的備份), 這樣, 當一台節點當機的時候, 另外一台節點還可以繼續提供服務.
小明到肯德基吃飯,找服務員點餐,這是正常的流程。但是,如果服務員隻有一個,并且恰好生病了,那麼小明是不是将沒有辦法正常點餐了。為了解決這個問題,肯德基雇了兩個服務員,同時提供服務,這樣一個服務員出問題了,另外一個服務員依然可以提供服務。
- 叢集多備(了解)
基本上等同于雙主互備, 差別就在于同時對外提供服務的節點數量更多, 備份數量更多 肯德基覺得兩個服務員也不保險,有兩個同時生病的可能性,于是又多雇了幾個服務員。
高可用的實作
我們在這裡采用的是主從模式的備份方式,也就是準備兩個NameNode,一個對外提供服務,稱為Active節點;另外一個不對外提供服務,隻是實時的同步Active節點的資料,稱為Standby的節點。
為了提供快速的故障轉移,Standby節點還必須具有叢集中塊位置的最新資訊。為了實作這一點,DataNodes被配置了兩個NameNodes的位置,并向兩者發送塊位置資訊和心跳信号。也就是說,DataNode同時向兩個NameNode心跳回報。
高可用架構圖
編輯
JournalNode
- JournalNode的功能
Hadoop2.x版本之後, Clouera提出了QJM/QuromJournal Manager, 這是一個基于Paxos算法實作的HA的實作方案
1. 基本的原理就是使用2N+1台JN存儲EditLog, 每次寫入資料的時候, 有半數以上的JN傳回成功的資訊, 就表示本次的操作已經同步到了JN
2. 在HA中, SecondaryNameNode這個角色已經不存在了, 保證Standby節點的中繼資料資訊與Active節點的中繼資料資訊一緻, 需要通過若幹個JN
3. 當有任何的操作發生在Active節點上的時候, JN會記錄這些操作到半數以上的節點中. Standby節點檢測JN中的log日志檔案發生了變化, 會讀取JN中的資料到自己的記憶體中, 維護最新的目錄樹結構與中繼資料資訊
4. 當發生故障的時候, Active節點挂掉, 此時Standby節點在成為新的Active節點之前, 會将讀取到的EditLog檔案在自己的記憶體中進行推演, 得到最新的目錄樹結構. 此時再升為Active節點, 可以無縫地繼續對外提供服務.
- 防止腦裂的發生
對于HA群集的正确操作至關重要,一次隻能有一個NameNode處于Active狀态。否則,名稱空間狀态将在兩者之間迅速分散,進而有資料丢失或其他不正确結果的風險。為了確定該屬性并防止所謂的“裂腦情況”,JournalNode将一次僅允許單個NameNode成為作者。在故障轉移期間,變為活動狀态的NameNode将僅承擔寫入JournalNodes的角色,這将有效地防止另一個NameNode繼續處于活動狀态,進而使新的Active可以安全地進行故障轉移。
- 怎麼了解腦裂?
就是Active節點處于網絡震蕩狀态,假死狀态,Standby就轉為Active。等網絡震蕩過後,就有兩個Active了,這就是腦裂。
- JournalNode叢集正常工作的條件
- 至少3個Journalnode節點
- 運作個數建議奇數個(3,5,7等)
- 滿足(n+1)/2個以上,才能正常服務。即能容忍(n-1)/2個故障。
- JournalNode的缺點
在這種模式下,即使活動節點發生故障,系統也不會自動觸發從活動NameNode到備用NameNode的故障轉移,必須需要人為的操作才行。要是有一個能監視Active節點的服務功能就好了。
這個時候,我們就可以使用zookeeper叢集服務,來幫助我們進行自動容災了。
自動容災原理
如果想進行HA的自動故障轉移,那麼需要為HDFS部署兩個新元件:ZooKeeper quorum和ZKFailoverController程序(縮寫為ZKFC)
Zookeeper quorum
Apache ZooKeeper是一項高可用性服務,用于維護少量的協調資料,将資料中的更改通知用戶端并監視用戶端的故障。HDFS自動故障轉移的實作依賴ZooKeeper進行以下操作:
- 故障檢測
叢集中的每個NameNode計算機都在ZooKeeper中維護一個持久性會話。如果計算機崩潰,則ZooKeeper會話将終止,通知另一個NameNode應觸發故障轉移。
- 活動的NameNode選舉(HA的第一次啟動)
ZooKeeper提供了一種簡單的機制來專門選舉一個節點為活動的節點。如果目前活動的NameNode崩潰,則另一個節點可能會在ZooKeeper中采取特殊的排他鎖,訓示它應成為下一個活動的NameNode。
ZKFC
ZKFailoverController(ZKFC)是一個新元件,它是一個ZooKeeper用戶端,它監視和管理namenode的狀态。運作namenode的每台機器都會運作一個ZKFC,該ZKFC負責以下内容:
- 運作狀況監視
ZKFC使用運作狀況檢查指令定期ping其本地NameNode。隻要NameNode以健康狀态及時響應,ZKFC就會認為該節點是健康的。如果節點崩潰,當機或以其他方式進入不正常狀态,則運作狀況螢幕将其标記為不正常。
- ZooKeeper會話管理
當本地NameNode運作狀況良好時,ZKFC會在ZooKeeper中保持打開的會話。如果本地NameNode處于活動狀态,則它還将持有一個特殊的“鎖定” znode。該鎖使用ZooKeeper對“臨時”節點的支援。如果會話到期,則鎖定節點将被自動删除。
- 基于ZooKeeper的選舉
如果本地NameNode運作狀況良好,并且ZKFC看到目前沒有其他節點持有鎖znode,則它本身将嘗試擷取該鎖。如果成功,則它“赢得了選舉”,并負責運作故障轉移以使其本地NameNode處于活動狀态。故障轉移過程類似于上述的手動故障轉移:首先,如有必要,将先前的活動節點隔離,然後将本地NameNode轉換為活動狀态。
自動容災的過程描述
ZKFC(是一個程序,和NN在同一個實體節點上)有兩隻手,分别拽着NN和Zookeeper。(監控NameNode健康狀态,并向Zookeeper注冊NameNode);叢集一啟動,2個NN誰是Active?誰又是Standby呢?
2個ZKFC先判斷自己的NN是否健康,如果健康,2個ZKFC會向zoopkeeper叢集搶着建立一個節點,結果就是隻有1個會最終建立成功,進而決定active地位和standby位置。如果ZKFC1搶到了節點,ZKFC2沒有搶到,ZKFC2也會監控watch這個節點。如果ZKFC1的Active NN異常退出,ZKFC1最先知道,就通路ZK,ZK就會把曾經建立的節點删掉。删除節點就是一個事件,誰監控這個節點,就會調用callback回調,ZKFC2就會把自己的地位上升到active,但在此之前要先确認ZKFC1的節點是否真的挂掉?這就引入了第三隻手的概念。
ZKFC2通過ssh遠端連接配接NN1嘗試對方降級,判斷對方是否挂了。确認真的不健康,才會真的 上升地位之active。是以ZKFC2的步驟是:
1.建立新節點。
2.第三隻手把對方降級。
3.把自己更新
那如果NN都沒毛病,ZKFC挂掉了呢?Zoopkeeper有一個用戶端session機制,叢集啟動之後,2個ZKFC除了監控自己的NN,還要和Zoopkeeper建立一個tcp長連接配接,并各自擷取自己的session。隻要一方的session失效,Zoopkeeper 就會删除該方建立的節點,同時另一方建立節點,上升地位。
HA的配置
守護程序布局
qianfeng01: NameNode、DataNode、JournalNode、QuorumPeerMain、ZKFC
qianfeng02: NameNode、DataNode、JournalNode、QuorumPeerMain、ZKFC
qianfeng03: DataNode、JournalNode、QuorumPeerMain
現在,先停止HDFS的程序,修改如下的配置檔案吧!
hdfs-site.xml
<!-- 注意: 高可用的叢集,沒有SecondaryNameNode的存在,是以在這個檔案中之前存在的SecondaryNameNode的配置需要删除 -->
<configuration>
<!-- 配置NameNode的邏輯名稱 -->
<!-- 注意: 後面的很多參數配置都是需要使用到這個名稱的 -->
<property>
<name>dfs.nameservices</name>
<value>supercluster</value>
</property>
<!-- 配置兩個NameNode的唯一辨別符 -->
<property>
<name>dfs.ha.namenodes.supercluster</name>
<value>nn1,nn2</value>
</property>
<!-- 針對每一個NameNode,配置自己的RPC通信位址和端口 -->
<property>
<name>dfs.namenode.rpc-address.supercluster.nn1</name>
<value>qianfeng01:9820</value>
</property>
<property>
<name>dfs.namenode.rpc-address.supercluster.nn2</name>
<value>qianfeng02:9820</value>
</property>
<!-- 針對每一個NameNode,配置WebUI的位址和端口 -->
<property>
<name>dfs.namenode.http-address.supercluster.nn1</name>
<value>qianfeng01:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.supercluster.nn2</name>
<value>qianfeng02:9870</value>
</property>
<!-- 定義journalnode程序的資料存儲的父路徑, 目錄在上面已經定義好了的:journalData -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/usr/local/hadoop-3.3.1/tmp</value>
</property>
<!-- 配置journalnode的伺服器位址和存儲目錄(數目為奇數個) -->
<!-- 伺服器位址使用分号“;”作為分隔符-->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://qianfeng01:8485;qianfeng02:8485;qianfeng03:8485/journalData</value>
</property>
<!-- 指定用戶端連接配接Active的namenode節點的java類型 -->
<property>
<name>dfs.client.failover.proxy.provider.supercluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!--為了保證系統的正确性,在任何時間隻有一個NameNode處于Active狀态,需要配置一個防護機制 -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<!--為了使該防護選項起作用,它必須能夠在不提供密碼的情況下SSH到目标節點。是以,還必須配置以下屬性-->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<!-- 免密登陸逾時時間,超過此時間未連接配接上,則登陸失敗,此配置可選-->
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
<!-- 支援自動容災屬性 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- 塊的副本數量 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
</configuration>
core-site.xml
<configuration>
<!--注意:使用到的是在hdfs-site.xml中配置的邏輯名稱 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://supercluster</value>
</property>
<!-- hdfs的資料儲存的路徑,被其他屬性所依賴的一個基礎路徑 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/local/hadoop-3.3.1/tmp</value>
</property>
<!-- ZooKeeper服務的位址 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>qianfeng01:2181,qianfeng02:2181,qianfeng03:2181</value>
</property>
</configuration>
hadoop-env.sh
# 添加兩行
export HDFS_JOURNALNODE_USER=root
export HDFS_ZKFC_USER=root
分發配置檔案到其他節點
[root@qianfeng01 ~]# cd $HADOOP_HOME/etc
[root@qianfeng01 hadoop]# scp -r hadoop qianfeng02:$PWD
[root@qianfeng01 hadoop]# scp -r hadoop qianfeng03:$PWD
啟動叢集
現在,叢集已經搭建成為了高可用的叢集了。在啟動叢集之前,我們需要先明确一件事情: 叢集現在的狀态有兩種:
- 這個叢集我之前使用過,NameNode已經存儲有資料了(fsimage和edits已生成)
- 這個叢集是我新搭建的,我直接搭建叢集的時候就搭建的高可用的叢集,之前從來沒有啟動過
如果你是第一種情況,請跳轉到 普通叢集轉HA
如果你是第二種情況,請跳轉到 直接搭建HA
啟動: 普通叢集轉HA
# 1. 啟動叢集的JournalNode服務。
# 注意事項: 如果之前叢集還在運作,需要先将其停止!使用指令 stop-dfs.sh
[root@qianfeng01 ~]# hdfs --daemon start journalnode
[root@qianfeng02 ~]# hdfs --daemon start journalnode
[root@qianfeng03 ~]# hdfs --daemon start journalnode
# 2. 啟動以前節點上的namenode程序
[root@qianfeng01 ~]# hdfs --daemon start namenode
# 3. 在新的namenode節點上拉取鏡像檔案
[root@qianfeng02 ~]# hdfs namenode -bootstrapStandby
# 4. 同步日志到journalnode叢集上,再啟動叢集
# 先關namenode
[root@qianfeng01 ~]# hdfs --daemon stop namenode
# 再同步日志
[root@qianfeng01 ~]# hdfs namenode -initializeSharedEdits
# 5. 格式化zkfc
# 5.1. 前提QuorumPeerMain服務必須處于開啟狀态,用戶端zkfc才能格式化成功
[root@qianfeng01 ~]# zkServer.sh start
[root@qianfeng02 ~]# zkServer.sh start
[root@qianfeng03 ~]# zkServer.sh start
# 5.2. 選擇其中一個namenode節點進行格式化zkfc
[root@qianfeng01 ~]# hdfs zkfc -formatZK
# 6. 你就可以快樂的開啟HA叢集進行測試了
[root@qianfeng01 ~]# start-all.sh
# 檢視NameNode的狀态
[root@qianfeng01 ~]# hdfs haadmin -getServiceState nn1
# 注意: 以後開HA叢集時,要先開zookeeper服務,再開HDFS。
啟動: 直接搭建HA
# 1. 啟動三個節點上的journalnode服務
[root@qianfeng01 ~]# hdfs --daemon start journalnode
[root@qianfeng02 ~]# hdfs --daemon start journalnode
[root@qianfeng03 ~]# hdfs --daemon start journalnode
# 2. 格式化namenode
# - 先删除所有節點的${hadoop.tmp.dir}/tmp/的資料(可選,這一步表示棄用fsimage.)
# - 選擇其中一個namenode進行格式化
[root@qianfeng01 ~]# hdfs namenode -format
# - 并啟動namenode程序
[root@qianfeng01 ~]# hdfs --daemon start namenode
# 3. 在另一台namenode上拉取已格式化的那台機器的鏡像檔案(資料的一緻性)
[root@qianfeng02 ~]# hdfs namenode -bootstrapStandby
# 4. 然後關閉已經啟動的namenode
[root@qianfeng01 ~]# hdfs --daemon stop namenode
# 5. 格式化zkfc
# 5.1. 前提QuorumPeerMain服務必須處于開啟狀态,用戶端zkfc才能格式化成功
[root@qianfeng01 ~]# zkServer.sh start
[root@qianfeng02 ~]# zkServer.sh start
[root@qianfeng03 ~]# zkServer.sh start
# 5.2. 選擇其中一個namenode節點進行格式化zkfc
[root@qianfeng01 ~]# hdfs zkfc -formatZK
# 6. 你就可以快樂的開啟HA叢集進行測試了
[root@qianfeng01 ~]# start-all.sh
# 注意:以後開HA叢集時,要先開zookeeper服務,再開HDFS。
自動容災測試
由于CentOS7的minimal版本缺少容災切換ActiveNameNode節點時所需要的元件,是以需要手動安裝一下:
yum install -y psmisc
1. 首先檢視目前活躍的Active節點是誰
2. Kill掉活躍節點上的NameNode程序,模拟當機
3. 觀察另外一個節點,是否已經變成Active的狀态
優秀的你與大廠之間隻差一個“好程式員”!