Hadoop 1.0存在的問題:單點故障和內存受限
(1)NameNode單點故障(NameNode只有一個,一旦宕機了,則數據就會丟失,雖然有配置SecondaryNameNode,但是SecondardyNameNode
合並元數據和日志文件需要時間的,所有還是會有部分數據會丟失)
(2)NameNode壓力大(單節點只有一個NameNode,所有的請求都訪問一個NameNode)
Hadoop 2.0解決方案:
單點故障:HA(通過主備NameNode解決,如果主NameNode發生故障,則切換到備NameNode上)
內存受限問題:F(HDFS Federation 聯邦)
水平擴展,支持多個NameNode,每一個NameNode分管一部分目錄,並且所有NameNode共享DateNode存儲資源
現在只討論HA的實現:
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之間元數據不一致)所以這個想法也不可行。
想法三:當客戶端發送請求給主NameNode時,元數據寫到一個共享的磁盤中(兩個Namenode都可以訪問),這樣元數據就可以保持一致了。這種技術就叫做NFS技術。
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:在HDFS-HA搭建的過程中起着分布式協調作用
1、 zookeeper提供目錄結構樹機制,兩個ZKFC進行資源搶奪,誰搶奪上了,誰就可以在zookeeper上建立一個節點目錄,並且創建一把鎖,與此同時將與自身關聯的Namenode的狀態置為Active活躍狀態(主Namenode),另一個置為standBy(靜態的也叫備NameNode)。
2、 事件回調和監控,zkfc一旦監測到主NameNode發生宕機則,主Namenode節點上的zkfc會將zookeeper上創建的節點目錄進行刪除,此時zookeeper會回調之前備zkfc在zookeeper上注冊的事件,將備zkfc從standBy變成Active的狀態。
3、 Session機制:如果zkfc的進程掛了,那么tcp連接就會斷開,tcp斷開有個會話超時時間范圍,一旦超過這個范圍,zookeeper就會將主zkfc之前注冊的節點進行刪除事件的操作,此時zookeeper就會回調備zkfc注冊的節點事件,將備zkfc下的Namenode進行狀態轉換為Active,並且同時將主Namenode的狀態變成standBy,這樣的話就不會同時存在兩個Active的NameNode。
Zkfc(zookeeper Failover Controller)和namenode是在同一個節點上。
HDFS Fedration聯邦方式解決高可用問題:
兩個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
HDFS中免秘鑰應用場景有哪些?
(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。
HA配置文件:
(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文件中配置)
關於HDFS-HA方式部署大概有哪些步驟?
(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
模擬HA的高可用性(zookeeper實現故障自動轉移)
(1)下面模擬active的namenode發生故障 kill -9 ,看看效果
看下頁面,好像無法刷新了
再看下Namenode2,由standby變成了Active
好的,現在再看下zookeeper鎖的信息:變成了node2了
現在恢復namenode
現在啟動恢復下node1上的namenode的看下node2狀態有沒有被修改:
從上面看知道node2還是active ,node1就變成,standby
(2)現在來模擬zkfc掛了(在node2上模擬kill -9 zkfc的進程):
現在看看node2的狀態:變成了standby
看看node1:變成了active
從zookeeper客戶端看下是否是現在的鎖是node1的:
這就是zkfc的自動故障轉移功能,利用zookeeper來分布式協調。
搭建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>
core-site.xml文件里面的內容:
<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>