天天看點

完全解析大資料的高可用叢集部署

作者:好程式員IT教育

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的狀态

優秀的你與大廠之間隻差一個“好程式員”!

繼續閱讀