(1)NameNode單點故障(NameNode只有一個,一旦宕機了,則數據就會丟失,雖然有配置SecondaryNameNode,可是SecondardyNameNodejava
合併元數據和日誌文件須要時間的,全部仍是會有部分數據會丟失)node
(2)NameNode壓力大(單節點只有一個NameNode,全部的請求都訪問一個NameNode)apache
單點故障:HA(經過主備NameNode解決,若是主NameNode發生故障,則切換到備NameNode上)bootstrap
內存受限問題:F(HDFS Federation 聯邦)服務器
水平擴展,支持多個NameNode,每個NameNode分管一部分目錄,而且全部NameNode共享DateNode存儲資源網絡
HA:客戶端只有和一個NameNode(主)進行通訊,而元數據部分是如何和NameNode(備)進行共享的?(首先dataNode 的信息是共享的,主NameNode和備NameNode這部分信息是一致的,而元數據不同,不是實時的)負載均衡
想法一:主Namenode和備Namenode之間創建一個socket通訊(阻塞型通訊),這樣兩個NameNode和DataNode的元數據就是一致的了(同時會引起一個問題,IO網絡通訊的問題,若是之間網絡一旦出現問題,則客戶端會認爲主Namenode出現了問題,由於整個流程是這樣的,客戶端發送一個請求給主NameNode,而後主Namenode再發送給備Namenode,而此時網絡發生波動的話,請求就會一直阻塞在那裏直到備NameNode返回成功的狀態,因此客戶端會認爲是主Namenode有問題),因此這個想法不可行。運維
想法二:主NameNode和備NameNode之間創建一個非阻塞的通訊(就是客戶端發送請求給主Namenode,而後主NameNode再發送給備NameNode,不須要等待備NameNode的返回狀態,這樣的話若是備NameNode發生問題,就會致使兩個NameNode之間元數據不一致)因此這個想法也不可行。dom
想法三:當客戶端發送請求給主NameNode時,元數據寫到一個共享的磁盤中(兩個Namenode均可以訪問),這樣元數據就能夠保持一致了。這種技術就叫作NFS技術。ssh
NFS:
可是NFS運維成本過高,因此Hadoop自己開發了一種技術,JNN(JournalNode)仍是集羣部署的,(保證了NameNode的高可用性)
ZookeeperFailOverController :Hadoop 配置ZKFC來實現自動故障轉移,這兩個都是在namenode上的JVM進程,用來監測主NameNode是否發生宕機的,若是發送宕機則向zookeeper彙報,zookeeper將原先註冊的鎖事件進行刪除,而後zookeeper在鎖事件刪除後會回調備用NameNode發送的鎖請求,將自動將備用NameNode變成主Namenode,而且備Namenode的狀態由standby變成了Active
Zookeeper:在HDFS-HA搭建的過程當中起着分佈式協調做用
一、 zookeeper提供目錄結構樹機制,兩個ZKFC進行資源搶奪,誰搶奪上了,誰就能夠在zookeeper上創建一個節點目錄,而且建立一把鎖,與此同時將與自身關聯的Namenode的狀態置爲Active活躍狀態(主Namenode),另外一個置爲standBy(靜態的也叫備NameNode)。
二、 事件回調和監控,zkfc一旦監測到主NameNode發生宕機則,主Namenode節點上的zkfc會將zookeeper上建立的節點目錄進行刪除,此時zookeeper會回調以前備zkfc在zookeeper上註冊的事件,將備zkfc從standBy變成Active的狀態。
三、 Session機制:若是zkfc的進程掛了,那麼tcp鏈接就會斷開,tcp斷開有個會話超時時間範圍,一旦超過這個範圍,zookeeper就會將主zkfc以前註冊的節點進行刪除事件的操做,此時zookeeper就會回調備zkfc註冊的節點事件,將備zkfc下的Namenode進行狀態轉換爲Active,而且同時將主Namenode的狀態變成standBy,這樣的話就不會同時存在兩個Active的NameNode。
Zkfc(zookeeper Failover Controller)和namenode是在同一個節點上。
兩個NameNode的狀態都是活躍的,可是兩個NameNode的元數據是不同的,也就是說存儲的目錄機構是不同的。可是接受客戶端訪問的請求量上去了,但與此同時NameNode仍是有單點故障的,因此仍是要加上HA,就能夠解決單點故障的問題。至於客戶端訪問那個NameNode這就交給代理來處理就能夠了,就好比說Ngix,搭建一個ngix來負載均衡。
這裏邦聯不細究了。
如今來搭建一下HDFS-HA來實現HDFS的高可用。(解決單點故障問題),下面是結構圖:
由於資源有限因此這麼搭建:
原本zookeeper集羣搭建就應該放三臺服務器
JNN應該放三臺服務器上
NameNode放在兩臺服務器上
Node1節點上有NameNode 和一個zkfc(JVM進程負責監測NameNode而且和zookeeper進行通訊的)和JNN(主NameNode 和從Namenode元數據同步的技術,通常都是集羣搭建,至少3個)
Node2節點上有NameNode 和一個secondaryNameNode(在搭建HA以前是有SecondaryNamenodede,可是搭建HA時,是不須要SecondaryNameNode的,這是衝突的,hdfs-site.xml中須要取消這個配置) 和一個DataNode 和一個zookeeper
和一個zkfc 和一個JNN
(1) 啓動腳本控制節點起停,就是node1(Namenode)啓動時順便把node2,node3,node4上的dataNode啓動
(2) 對方Zkfc(A對象的zkfc)控制對方namenode(B對象的namenode)狀態時須要通訊,須要免祕鑰,當其中一個zkfc(A對象的zkfc)掛掉時,對方的zkfc(B對象的zkfc)將會把對方的namenode(A對象的namenode)的狀態變成standby,而後將自身的namenode(B對象的Namenode)的狀態置爲Active。
(1) 邏輯位置到物理位置的映射(由於有兩個Namenode,客戶端沒法直接同時訪問兩個Namenode,因此要配置一個邏輯位置,而後邏輯位置映射到實際的物理位置)
(2) JouaryNode的配置,寫入到磁盤文件的路徑(就是兩個Namenode共享的元數據的信息Edits.Log中的信息。)還有journalNode的位置信息
(3) 免祕鑰配置,failover zkfc發生故障時,須要通訊將對方的namenode狀態修改
同一個zookeepr集羣能夠爲多個hdfs集羣服務(只要hdfs集羣取不一樣的名稱就能夠具體的在hdfs-site.xml文件中配置)
同一個JouaryNode集羣能夠爲多個hdfs集羣服務(只要hdfs集羣取不一樣的名稱就能夠具體的在hdfs-site.xml文件中配置)
(1)配置好HA的配置文件(看文章最後面有貼出配置)
(3) 啓動JNN(jouaryNode集羣)(hadoop-daemon.sh start journalnode)hadoop-daemon.sh start journalnode
(4) Hdfs namenode –format(格式化namenode前啓動jouaryNode是由於namenode格式化fsimage和edits.log文件,若是兩個namenode都格式化,會形成fsimage中的數據不一致,因此要先啓動jounaryNode,而後再格式化Namenode,而後將fsimage共享給另外一個namenode)hdfs namenode -format
(5) 啓動第一個namenode(hadoop-daemon.sh start namenode)
(6) 而後以-bootstrapStandby啓動第二個namenode (將這個namenode設置爲備用namenode) hdfs namenode -bootstrapStandby 若是這步報錯了,看下面注意部分
(7) 啓動zookeeper集羣,每一個zookeeper節點上執行: zkServer.sh start
(8) 格式化zkfc:hdfs zkfc -formatZK(由於zkfc依賴於zookeeper,由於格式化後會加一把鎖在zookeeper上,會在zookeeper集羣上建立一個節點,若是此時zookeeper未啓動的話,會報錯)
(9) 啓動zookeeper客戶端查看zkfc是否生成對應的目錄結構 zkCli.sh 回車
(10) 啓動hadoop集羣 start-dfs.sh
(11) 在zookeeper客戶端能夠查看到生成了鎖,使用get獲取鎖的信息:鎖是node1的
特別要注意的是(我已經踩了這個坑):/etc/hosts文件 127.0.0.1 localhost node1 localhost4 localhost4.localdomain4必定要寫成192.168.234.11 localhost node1 localhost4 localhost4.localdomain4,由於在主namenode格式化而後啓動以後,將第二個namenode置爲standby狀態時,會報錯(FATAL ha.BootstrapStandby: Unable to fetch namespace information from active NN at node1/192.168.234.11:8020: Call From node2/127.0.0.1 to node1:8020 failed on connection exception: java.net.ConnectException: Connection refused;),其實報錯的時候能夠看下主namenode下監聽8020端口時的ip地址(命令:netstat -antp | fgrep 8020),這時你會發現是127.0.0.1因此備namenode在置爲standby時會報錯,主namenode拒接連接備namenode.
若是發生了作以下操做:(1)修改/etc/hosts文件將127.0.0.1修改爲實際的ip (2)重啓sshd服務 service sshd restart (3)重啓網絡 service network restart
看下在zookeeper 那裏註冊的鎖信息:
Node1爲active
Node2爲standby
看下頁面,好像沒法刷新了
再看下Namenode2,由standby變成了Active
好的,如今再看下zookeeper鎖的信息:變成了node2了
如今恢復namenode
如今啓動恢復下node1上的namenode的看下node2狀態有沒有被修改:
從上面看知道node2仍是active ,node1就變成,standby
如今看看node2的狀態:變成了standby
看看node1:變成了active
從zookeeper客戶端看下是不是如今的鎖是node1的:
這就是zkfc的自動故障轉移功能,利用zookeeper來分佈式協調。
(1) /usr/zookeeper/zookeeper-3.4.6/conf : cp zoo_samp.conf zoo.conf
(2) 修改data.dir zookeeper數據文件的目錄: dataDir=/usr/zookeeper/tmp/data
(3) 添加3個節點的地址(
server.1=192.168.234.12:2888:3888
server.2=192.168.234.13:2888:3888
server.3=192.168.234.14:2888:3888
)
(4) 手動建立數據文件的目錄
(5) Echo 1 >> 數據文件目錄/myid 注:而後分發到各個節點,修改myid 若是(zoo.conf文件中)當前節點的ip地址對應server.2 那麼myid就修改成2
(6) 修改各個節點的profile文件,要保持一致
(7) . /etc/profile (使得修改後的profile文件生效)
(8) 啓動zookeeper zkService.sh start (只要超過一半的zookeeper就能夠認爲zookeeper啓動,不然認爲未啓動,能夠啓動一個zookeeper時查看狀態)
zookeeper配置文件: /usr/zookeeper/zookeeper-3.4.6/confzoo.cfg文件的最低端加上以下的內容
server.1=192.168.234.12:2888:3888
server.2=192.168.234.13:2888:3888
server.3=192.168.234.14:2888:3888
hdts-site.xml
<configuration>
<!-- namenode HA集羣的別名 -->
<property>
<name>dfs.nameservices</name>
<value>hacluster</value>
</property>
<!-- HA集羣下的兩個namenode的別名nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.hacluster</name>
<value>nn1,nn2</value>
</property>
<!-- namenode1監聽的地址 -->
<property>
<name>dfs.namenode.rpc-address.hacluster.nn1</name>
<value>node1:8020</value>
</property>
<!-- namenode2監聽的地址 -->
<property>
<name>dfs.namenode.rpc-address.hacluster.nn2</name>
<value>node2:8020</value>
</property>
<!-- namenode1客戶端訪問的物理地址以及端口 -->
<property>
<name>dfs.namenode.http-address.hacluster.nn1</name>
<value>node1:50070</value>
</property>
<!-- namenode2客戶端訪問的物理地址以及端口 -->
<property>
<name>dfs.namenode.http-address.hacluster.nn2</name>
<value>node2:50070</value>
</property>
<!-- journalNode的地址和端口:jnncluster 別名能夠隨便取 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node1:8485;node2:8485;node3:8485/hacluster</value>
</property>
<!-- journalNode 存放edit.log文件的路徑 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/usr/hadoop/journal/node/local/data</value>
</property>
<!-- namenode故障監測 -->
<property>
<name>dfs.client.failover.proxy.provider.hacluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- zkfc進程進行回調須要免祕鑰 -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<!-- zkfc進程進行回調須要免祕鑰,私鑰路徑 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<!-- 開啓zookeeper的自動故障轉移功能 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- the secondary namenode address -->
<!-- <property> -->
<!-- <name>dfs.namenode.secondary.http-address</name> -->
<!-- <value>node2:9001</value> -->
<!-- </property> -->
<!-- the directory of namenode data -->
<property>
<name>dfs.namenode.name.dir</name>
<value>${hadoop.tmp.dir}/name</value>
</property>
<!-- the directory of datanode data -->
<property>
<name>dfs.datanode.data.dir</name>
<value>${hadoop.tmp.dir}/data</value>
</property>
<!-- the number of replication of datanode data -->
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
</configuration>
<configuration><!-- namenode address--><!-- <property> --><!-- <name>fs.defaultFS</name> --><!-- <value>hdfs://node1:9000</value> --><!-- </property> --><!-- namenode集羣入口 --><property> <name>fs.defaultFS</name> <value>hdfs://hacluster</value></property><!-- zookeeper集羣信息 --> <property> <name>ha.zookeeper.quorum</name> <value>node2:2181,node3:2181,node4:2181</value> </property><!-- the hadoop temporary directory --> <property> <name>hadoop.tmp.dir</name> <value>file:/usr/hadoop/tmp</value> </property><!-- upload file IO cache size --> <property> <name>io.file.buffer.size</name> <value>131072</value> </property></configuration>