1. 程式人生 > >大資料學習之路91-Hadoop的高可用

大資料學習之路91-Hadoop的高可用

我們之前一直沒有配置過hadoop的高可用,今天我們就來配置一下

之前我們的namenode只要一掛,則整個hdfs叢集就完蛋。雖然我們可以通過重啟的方式來恢復,可是我們重啟好之前,我們的hdfs叢集就不能提供服務了。所以它存在單點故障問題。

我們可以設定兩臺namenode ,一臺為active,另一臺為standby

active對外提供服務,而standby則不斷的和active同步元資料。但這個元資料在hadoop中不是直接讓他們就同步,而是在一開始的時候,讓兩個namenode保持相同的fsimage。當叢集執行起來之後,active的記憶體中會不斷的產生元資料。而且不光是產生元資料,還會將引起元資料變化的操作記錄成日誌。

namenode在高可用的模式下,不光會將日誌記錄在本地,還會將日誌記錄在日誌管理系統裡面,這個分散式的日誌管理系統,叫Qjournal分散式日誌管理系統。Qjournal是一個叢集,它裡面會有很多臺機器,可以實現資料的可靠儲存,他會將日誌檔案記錄在很多臺伺服器上,當然這些伺服器肯定有本地目錄,而且它還有一個特點,就是隻要有半數以上的機器還在,就可以正常的提供對外服務,這個就和Zookeeper有點像了,為什麼說它和Zookeeper有點像呢?因為他們所用的資料同步的策略相同。Zookeeper及資料時候也是記錄在很多臺,也需要進行同步的。他們是通過paxos演算法做資料一致性的同步。所以一般有奇數臺節點比較合適,並且存活半數以上,這個叢集就能正常執行。所以它是很可靠的,他一般不會掛。而這個系統是基於Zookeeper開發的。沒有Zookeeper是執行不起來的。所以我們還要引入Zookeeper叢集。

反正這麼一來active就把這些日誌記錄到Qjournal叢集中的伺服器中了。然後standby就可以不斷的從Qjournal中將這些日誌讀下來。讀下來之後就可以做一些解析運算,然後更新到記憶體元資料,同時就把fsimage也順便更新了。當然,standby不會只更新自己的fsimage,他會將自己的fsimage上傳到active中,覆蓋掉原來的fsimage。這壓根就相當於也承擔了secondary namenode的一些功能。(把日誌和fsimage合併)

這樣一來,這兩個namenode的元資料基本上就能保持同步。當然,這樣的同步也不可能做到完全同步,因為不可能active增加一條日誌,standby就下載一條。

但是那些還沒有來的及同步的資料是存在在Qjournal中的。萬一active掛了,standby也頂多將還沒來的及同步的資料拿來解析一下,再放入記憶體,這樣記憶體中的元資料就恢復了。恢復之後就可以立馬對外服務。

有兩個namenode的話還需要有一個狀態協調,兩個namenode不可以起衝突。需要知道active是誰,那麼另一臺就是standby。

還有比如active死了,那麼standby要不要切換狀態。這些事情都是需要寫邏輯的。

而namenode本身的程式碼就已經很複雜了,如果再把這些邏輯加進去,就會更加複雜。所以hadoop在這方面做了一些不一樣的處理。

他把這些新增的狀態管理的邏輯寫在另外一個程式中了。

這個程式就叫做:ZKFC ------>基於Zookeeper的failover controller(失敗切換  控制器)

這個ZKFC就負責不斷的查詢namenode的狀態,看他正常不正常。

那麼它是怎麼查的呢?他是通過本地程序的方式去監測的namenode的,namenode其實也是一個程序。

每個namenode都需要有一個ZKFC來監測各自的namenode程序。這樣就有兩個ZKFC了。而且這兩個ZKFC還會通過Zookeeper交換一些資訊。比如說現在誰是active。

其實ZKFC做的最主要的事主要體現在切換功能上,假如說ZCFC發現自己監測的namenode死了,他就會告訴另一個ZKFC,我這邊的namenode掛了。然後另一臺ZKFC就會控制自己所監測的namenode去切換狀態,但是他也不會立刻就這麼做,因為立刻這麼做會有風險。

這裡所說的風險就是,這個active不見得就真的死了。因為判斷active死亡是通過ZKFC的程序向namenode的程序傳送請求來判斷的。而namenode又是一個java程式,java程式是執行在jvm中的。而jvm是會對程式執行過程中產生的記憶體垃圾進行回收的。

而jvm的垃圾回收有一個有意思的小例子,假如在家裡,你扔一點垃圾,你媽就給你掃一點垃圾,可是你媽的清理速度跟不上你的扔垃圾的速度,這個時候他就會說,你停下來,我要進行大掃除,這個時候程序就會停住。而namenode就相當於死了。

如果這個時候就立馬將standby切換成active,那麼當之前的active甦醒之後,它的狀態依然為active。這樣就會有兩個active。就會出問題。

這種現象叫做腦裂,防止腦裂的策略有兩種。

第一種:SSH kill通過ssh將判斷為死亡的active殺掉

第二種:shell指令碼

操作之後就可以放心大膽的進行切換了。但是事情並不是那麼完美。ssh有可能一直請求無響應,這個時候就有可能使網路不通造成的。當然ZKFC沒有那麼傻,他不會一直等,如果一直連線不通,他就會轉而去調我們提供給他的shell指令碼。

我們可以在shell指令碼中想辦法,將要殺死的那臺namenode的網路斷掉,電停掉,或者告警等。之後就可以放心的切換狀態了。

 

-----------------------------------------------------------------------------------------------------------------------------------------------------

namenode的單點故障解決起來之所以麻煩是因為我們需要找一個替代者,那麼這兩個namenode程式之間就需要進行狀態同步。

yarn中的resource manager也存在單點故障,但是處理起來就沒有這麼麻煩。

resource manager的作用只是用來讓客戶端提交程式的,客戶端提交一個程式,resource manager就把任務分配給若干個node manager去執行。

萬一在某一段時間,resource manager突然掛了,大不了就是客戶端無法提交job了。我們只需要重新來一臺resource manager 就又可以重新提交job了,而這兩個resource manager之間不存在狀態的繼承。所以自然就沒有這麼麻煩

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下來我們就要來配置hadoop的高可用了

還記得我們之前配置的core-site.xml嗎?

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://marshal:9000/</value>
    </property>
</configuration>

我們之前通過這個配置可以指定namenode的埠號。

但是現在不能這樣了,因為我們有兩個namenode,如果我們寫死了這個值,那麼客戶端肯定要出問題的。因為兩個namenode隨時可能會所換。所以這個namenode肯定不能是一個具體的值。

我們取而代之的是填入nameservice的名字。這個nameservice就代表一對namenode,只不過這個nameservice的名字是可以自己定的。但是光憑一個名字是不足以說明是哪兩個namenode的,所以後面還有其他的配置檔案去說明。

修改之後的core-site.xml:

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://marshalservice:9000/</value>
    </property>
	
	<property>
	    <name>ha.zookeeper.quorum</name>
		<value>marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181</value>
	</property>
</configuration>

接下來的重點就是修改hdfs-site.xml,在這個檔案中我們就要指定那個名字代表哪些namenode了。
 

dfs.nameservices就是我們之前定義的那個名稱。

dfs.ha.namenodes.marshalservice,就是指定有哪些namenode

接著還要指定這兩個namenode的rpc通訊地址,和http通訊地址。

dfs.namenode.shared.edits.dir指定了namenode的共享edits元資料在JournalNode上的存放位置。

那麼qjournal拿到日誌之後還是要放在磁碟上的,dfs.journalnode.edits.dir,就指定了存放的位置。

我們還需要配置ZKFC的相關內容

dfs.ha.automatic-failover.enabled這個開啟了namenode的失敗自動切換

dfs.client.failover.proxy.provider.marshalservice配置了失敗自動切換的實現方式。

dfs.ha.fencing.methods配置了隔離機制方法,多個機制用換行分割,即每個機制佔用一行,我們這裡並沒有配置真正的shell指令碼,而是配置了個假的。有真正的需求的時候,可以真正的配置一個。

由於使用sshfence隔離機制的時候還需要ssh免登陸,所以我們還需要配置ssh

dfs.ha.fencing.ssh.connect-timeout配置了隔離機制的超時時間
而且配置了這些之後,我們之前配置的secondary namenode就不需要了。

配置好的hdfs-site.xml

<configuration>
    <property>
	    <name>dfs.nameservices</name>
		<value>marshalservice</value>
	</property>
	<property>
	    <name>dfs.ha.namenodes.marshalservice</name>
		<value>marshal,marshal01</value>
	</property>
	<property>
	    <name>dfs.namenode.rpc-address.marshalservice.marshal</name>
		<value>marshal:9000</value>
	</property>
	<property>
	    <name>dfs.namenode.http-address.marshalservice.marshal</name>
		<value>marshal:50070</value>
	</property>
	<property>
	    <name>dfs.namenode.rpc-address.marshalservice.marshal01</name>
		<value>marshal01:9000</value>
	</property>
	<property>
	    <name>dfs.namenode.http-address.marshalservice.marshal01</name>
		<value>marshal01:50070</value>
	</property>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>/root/hadoopdata/name/</value>
    </property>
   <property>
        <name>dfs.datanode.data.dir</name>
        <value>/root/hadoopdata/data/</value>
   </property>
  <!-- <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>marshal01:50090</value>
   </property> -->
   <property>
       <name>dfs.namenode.shared.edits.dir</name>
	   <value>qjournal://marshal03:8485;marshal04:8485;marshal05:8485/marshalservice</value>
   </property>
   <property>
       <name>dfs.journalnode.edits.dir</name>
	   <value>/root/hadoopdata/journaldata</value>
   </property>
   <property>
       <name>dfs.ha.automatic-failover.enabled</name>
	   <value>true</value>
   </property>
   <property>
       <name>dfs.client.failover.proxy.provider.marshalservice</name>
	   <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
   </property>
   <property>
       <name>dfs.ha.fencing.methods</name>
	   <value>
	         sshfence
			 shell(/bin/true)
	   </value>
   </property>
   <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>
   
</configuration>

然後就是要配置mapred-site.xml:其實這個檔案可以不用配置

這個檔案主要是用來指定我們提交job的時候是提交到yarn上還是執行在本地。

下面是具體配置:

<configuration>
<!--指定mr框架為yarn方式 -->
    <property>
	    <name>mapreduce.framework.name</name>
		<value>yarn</value>
	</property>
</configuration>

------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下來我們要配置的就是yarn的高可用

首先來修改yarn-site.xml

<configuration>

<!-- Site specific YARN configuration properties -->
    <!-- 啟動高可用-->
     <property>
        <name>yarn.resourcemanager.ha.enabled</name>
        <value>true</value>    
     </property>
	 <!-- 指定resource manager的叢集id-->
	 <property>
        <name>yarn.resourcemanager.cluster-id</name>
        <value>yrc</value>    
     </property>
	 <!-- 指定resource manager的邏輯名字,具體是哪臺機器可以在下面的配置指定-->
	 <property>
        <name>yarn.resourcemanager.ha.rm-ids</name>
        <value>rm1,rm2</value>    
     </property>
	 <!-- 分別指定rm的地址-->
     <property>
        <name>yarn.resourcemanager.hostname.rm1</name>
        <value>marshal</value>    
     </property>
	 <property>
        <name>yarn.resourcemanager.hostname.rm2</name>
        <value>marshal01</value>    
     </property>
	 <!-- 指定Zookeeper叢集地址-->
	 <property>
        <name>yarn.resourcemanager.zk-address</name>
        <value>marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181</value>    
     </property>
	 
     <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
     </property>
     <property>
        <name>yarn.nodemanager.resource.memory-mb</name>
        <value>2048</value>
     </property>
     <property>
        <name>yarn.nodemanager.resource.cpu-vcores</name>
        <value>2</value>
     </property>
     <property>
        <name>yarn.scheduler.maximum-allocation-mb</name>
        <value>2048</value>
     </property>



</configuration>

配置檔案配置好之後需要進行分發:

接著我們要先將Zookeeper叢集啟動起來,因為journalnode要向啟動起來必須先啟動Zookeeper

接下來我們還需要在(marshal03,marshal04,marshal05)上手動啟動journalnode(之前在hdfs-site.xml中配置過)

啟動命令為:

sbin/hadoop-daemon.sh start journalnode

我們可以執行jps命令檢檢視marshal03,marshal04,marshal05上是否多了JournalNode程序。

啟動了JournalNode程序之後我們就可以開始格式化namenode了。

那麼為什麼我們格式化Journalnode之前還要啟動Journalnode呢?

因為現在namenode記錄日誌除了在本地記錄還會在Journalnode記錄。所以我們先把Journalnode啟動起來,這樣格式化的時候,就會在JournalNode中也進行相應的初始化工作。

現在我們有兩個namenode,那麼我們格式化誰?我們只選擇格式化一個,另一個不能格式化,因為如果另一個也格式化的話,就會生成新的叢集id,這樣這兩個namenode就不在一塊了。

所以我們的選擇是,先格式化一臺,然後將格式化之後的目錄複製給另一個namenode,這樣這兩個namenode就完全一樣了。

我們在marshal這臺機器執行命令:

hadoop namenode -format

然後將生成的hadoopdata目錄複製給marshal01

scp -r hadoopdata/ marshal01:$PWD

接著我們還要格式化ZKFC(在marshal上執行即可):

ZKFC是用來做狀態切換的,它還會在zookeeper上記錄一些資訊。既然要在zookeeper上記錄資訊,就肯定要在zookeeper上先把父節點建立起來。

我們來看,在格式化之前zookeeper節點的目錄如下:

我們執行命令進行格式化:

hdfs zkfc -formatZK

執行命令之後可以看到:

然後我們再看Zookeeper的目錄的時候就可以看到:

多了hadoop-ha的目錄:

此時這裡還沒有資料

此時我們啟動hdfs:

start-dfs.sh

這下我們再執行jps就可以看到marshal和marshal01上都有namenode,datanode,ZKFC的程序存在。

然後我們訪問http就可以看到:

我們之前也配置了yarn的高可用但是當我們在marshal執行:

start-yarn.sh

之後發現,只有marshal是resource manager,其他機器都是node manager,所以在這裡我們還要手動在marshal01上啟動另一個resource manager

我們在marshal01上執行:

yarn-daemon.sh start resourcemanager

在HA的模式下,客戶端要想知道叢集中的情況,必須將叢集上的配置檔案放入工程中的src