該文主要記錄了自己用雲服務器搭建集群的過程,也分享一些自己遇到的問題和解決方法。里面可能提及一些自己的理解,可能不夠准確,希望大家能夠指正我,謝謝。
1.什么是HA集群
HA :High Available
問題:對於只有一個namenode的集群,如果namenode的集群出現故障,集群將無法使用直到重新啟動。
方法:開啟HDFS的HA功能,通過在不同節點上設置Active/Standby兩個namenode,當其中一個出現故障,可以很快的把namenode切換到另外一台機器(同時只有一個namenode處於Active狀態)。為了能夠實時同步 Active 和 Standby 兩個 NameNode 的元數據信息(實際上 editlog),需提 供一個共享存儲系統,可以是 NFS、QJM(Quorum Journal Manager)或者 Zookeeper,Active Namenode 將數據寫入共享存儲系統,而 Standby 監聽該系統,一旦發現有新數據寫入,則 讀取這些數據,並加載到自己內存中,以保證自己內存狀態與 Active NameNode 保持基本一 致,如此這般,在緊急情況下 standby 便可快速切為 active namenode。為了實現快速切換,Standby 節點獲取集群的最新文件塊信息也是很有必要的。為了實現這一目標,DataNode 需要配置 NameNodes 的位置,並同時給他們發送文件塊信息以及心跳檢測。
2.集群規划
三台DigitalOcean的1G內存的服務器,系統是Ubuntu 16.04.4(內存只有1G,所以配置文件中內存分配必須根據實際設置,不能使用默認值),三台組成一個Zookeeper集群(奇數且最少三台)。hadoop1和hadoop2作為namenode的主備切換,hadoop3運行resourcemanager,因為機器性能限制,namenode和resourcemanager都要占用大量資源,所以把他們分開了。而且在這里,沒有啟用RM的高性能模式。
3.服務器准備(注意每台機器使用的用戶名必須相同,在這里我全是root用戶)
3.0 修改主機名和ip-hostname映射關系
主要是修改 /etc/hostname 和 /etc/hosts 兩個文件。關於修改hostname是不是必要這個問題,我剛開始是直接用的Ip地址,但是會發現HDFS里面有很多函數方法都會首先默認取主機名,從而會有很多Error出現。況且通過配置ip和主機名的映射關系,配置文件的修改會更加的方便。
a.分別在三台機器的/etc/hostname文件修改主機名,分別取名為hadoop1,hadoop2,hadoop3 。
b.在每台機器的 /etc/hosts 文件中中添加Ip和主機名的映射關系。關於這里的Ip是公網ip還是內網ip的問題,這個問題我覺得不能一刀切。一般情況下使用的是內網Ip,但是DigitalOcean的服務器自己 ping 自己的內網Ip是ping不通的,但是公網卻能夠ping 通。所以我在這里的ip使用的是公網Ip。Ip是公網的還是內網的涉及到不同的zookeeper的配置,所以配置時候得根據實際情況。
Ip1 hadoop1
Ip2 hadoop2
Ip3 hadoop3
3.1 ssh免登陸配置
在每台服務器的/root/.ssh/ 目錄中,執行如下命令生成一對密鑰,並將本地主機的公鑰添加到遠程主機的authorized_keys文件上。三台機器兩兩雙向進行配置,注意包括自己對自已。
ssh-keygen -t rsa ssh-copy-id ip
3.2 Java環境安裝
apt-get install openjdk-8-jre
3.3 Hadoop(每台服務器Hadoop的安裝目錄必須一致,安裝包的配置信息也必須一致)
在 /root/目錄中,執行以下命令下載Hadoop安裝包,然后解壓到當前目錄 tar -zxvf hadoop-3.8.4.tar.gz -C /root/ 。我安裝的版本是2.8.4。也可以只現在一台服務器上安裝並配置好,然后復制到其他服務器上。
wget http://www.apache.org/dyn/closer.cgi/hadoop/common/hadoop-2.8.4/hadoop-2.8.4.tar.gz
3.4 Zookeeper
在每台服務器的 /root/目錄中,執行以下命令下載Zookeeper安裝包,然后解壓到當前目錄tar -zxvf zookeeper-3.4.12tar.gz -C /root/ 。也可以在一台服務器上安裝並配置好,然后復制到其他服務器上。但是zookeeper的配置文件每台服務器不同注意修改。
wget https://zookeeper.apache.org/releases.html#download
3.5 添加環境變量
在每台服務器的 /etc/profile 文件最后添加hadoop,java和zookeeper的路徑。然后,執行 source /etc/profile 命令后生效。
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre export HADOOP_HOME=/root/hadoop-2.8.4 export ZOOKEEPER_HOME=/root/zookeeper-3.4.12 export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$ZOOKEEPER_HOME/bin
4.配置文件的修改(配置文件分別位於root/hadoop-2.8.4/etc/hadoop和root/zookeeper-3.4.12/conf)
4.1 hadoop-env.sh
將25行左右的那一行替換成如下語句
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre
4.2 core-site.xml
<configuration> <!-- 指定hdfs的nameservice為ns(任取)--> <property> <name>fs.defaultFS</name> <value>hdfs://ns</value> </property> <!-- 指定hadoop臨時目錄 --> <property> <name>hadoop.tmp.dir</name> <value>/root/hadoop-2.8.4/tmp</value> </property>
<!--流文件的緩沖區單位KB> <property> <name>io.file.buffer.size</name> <value>4096</value> </property> <!-- 指定zookeeper集群的地址 --> <property> <name>ha.zookeeper.quorum</name> <value>hadoop1:2181,hadoop2:2181,hadoop3:2181</value> </property> </configuration>
4.3 hdfs-site.xml
<configuration>
<!--指定hdfs的nameservice為ns,需要和core-site.xml中的保持一致 --> <property> <name>dfs.nameservices</name> <value>ns</value> </property> <!-- ns下面有兩個NameNode,分別是nn1,nn2 --> <property> <name>dfs.ha.namenodes.ns</name> <value>nn1, nn2</value> </property> <!-- nn1的RPC通信地址 --> <property> <name>dfs.namenode.rpc-address.ns.nn1</name> <value>hadoop1:9000</value> </property> <!-- nn1的http通信地址 --> <property> <name>dfs.namenode.http-address.ns.nn1</name> <value>hadoop1:50070</value> </property> <!-- nn2的RPC通信地址 --> <property> <name>dfs.namenode.rpc-address.ns.nn2</name> <value>hadoop2:9000</value> </property> <!-- nn2的http通信地址 --> <property> <name>dfs.namenode.http-address.ns.nn2</name> <value>hadoop2:50070</value> </property> <!-- 指定NameNode的元數據在JournalNode上的存放位置 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://hadoop1:8485;hadoop2:8485;hadoop3:8485/ns</value> </property> <!-- 指定JournalNode在本地磁盤存放數據的位置 --> <property> <name>dfs.journalnode.edits.dir</name> <value>/root/hadoop-2.8.4/journal</value> </property> <!-- 開啟NameNode失敗自動切換 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- 配置失敗自動切換實現方式 --> <property> <name>dfs.client.failover.proxy.provider.ns</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> <!-- 使用sshfence隔離機制時需要ssh免登陸 --> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/root/.ssh/id_rsa</value> </property> <!-- 配置sshfence隔離機制超時時間 --> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property>
<!--設置副本數為2> <property> <name>dfs.replication</name> <value>2</value> </property> </configuration>
4.4 mapred-site.xml
<configuration> <!-- 指定mr框架為yarn方式 --> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property>
<!--map任務內存大小,默認1G--> <property> <name>mapreduce.map.memory.mb</name> <value>230</value> </property> <!--reduce任務內存大小,默認1G--> <property> <name>mapreduce.reduce.memory.mb</name> <value>460</value> </property> <!--map任務運行的JVM進程內存大小,默認-Xmx200M--> <property> <name>mapreduce.map.java.opts</name> <value>-Xmx184m</value> </property> <!--reduce任務運行的JVM進程內存,默認-Xmx200M--> <property> <name>mapreduce.reduce.java.opts</name> <value>-Xmx368m</value> </property> <!--MR AppMaster運行需要內存,默認1536M--> <property> <name>yarn.app.mapreduce.am.resource.mb</name> <value>460</value> </property> <!--MR AppMaster運行的JVM進程內存,默認-Xmx1024m--> <property> <name>yarn.app.mapreduce.am.command-opts</name> <value>-Xmx368m</value> </property> </configuration>
4.5 yarn-site.xml
<configuration> <!-- 分別指定RM的地址 --> <property> <name>yarn.resourcemanager.hostname</name> <value>hadoop3</value> </property> <!-- 指定zk集群地址 --> <property> <name>yarn.resourcemanager.zk-address</name> <value>hadoop1:2181,hadoop2:2181,hadoop3:2181</value> </property>
<> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property>
<!--RM中分配容器的內存最小值,默認1G--> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>230</value> </property>
<!--RM中分配容器的內存最大值,默認8G--> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>700</value> </property>
<!--可用物理內存大小,默認8G--> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>700</value> </property>
<!--虛擬內存檢查是否開始> <property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> </property> </configuration>
4.6 slaves
在每台服務器的slaves文件中配置集群Datanode的主機名,只有這個文件里面的主機名才能被Namenode識別。
hadoop1
hadoop2
hadoop3
4.7 zoo.cfg 和 myid
首先重命名zoo_sample.cfg文件為zoo.cfg,並按如下修改文件。
#修改 dataDir=/root/zookeeper-3.4.12/data dataLogDir=/root/zookeeper-3.4.12/datalog #末尾添加(內網IP) server.1=hadoop1:2888:3888
server.2=hadoop2:2888:3888 server.3=hadoop3:2888:3888
但是如果使用的是公網IP的時候,注意把本機配置為0.0.0.0。假設我們現在在hadoop1節點上,且使用的是公網IP,則應該如下配置
#末尾添加
server.1=0.0.0.0:2888:3888 server.2=165.227.133.86:2888:3888 server.3=159.65.120.185:2888:3888
保存退出后。在/root/zookeeper-3.4.12/目錄下,創建data和datalog兩個目錄,並在data目錄下面創建myid文件並添加內容。在三台服務器中的myid的內容分別是1,2,3(對應server.xx)。
例如,在Hadoop1服務器上執行命令 echo 1 > myid 。
5.集群啟動(嚴格按照下面步驟)
5.1 在三個節點上執行如下命令啟動zookeeper,並分別查看節點狀態,正常情況下一個leader和兩個follower。
root@hadoop1:~# zkServer.sh start ZooKeeper JMX enabled by default Using config: /root/zookeeper-3.4.12/bin/../conf/zoo.cfg Starting zookeeper ... STARTED
root@hadoop1:~/zookeeper-3.4.12# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /root/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: leader
5.2 在hadoop1上啟動journalnode集群 hadoop-daemons.sh start journalnode (注意是復數版本的)。用jps檢驗三個節點多了JournalNode進程。
5.3 在hadoop1上格式化HDFS hdfs namenode -format 。格式化后會在根據core-site.xml中的hadoop.tmp.dir配置生成個文件,在這里是存在 /root/hadoop-2.8.4/tmp 的目錄下。然后將這個文件拷貝到hadoop2的 /root/hadoop-2.8.4/ 目錄下 scp -r tmp/ root@hadoop2:/root/hadoop-2.8.4/ 。
5.4 在hadoop1上格式化ZK hdfs zkfc -formatZK 。
5.5 在hadoop1上啟動HDFS start-dfs.sh 。
5.6 在hadoop3上啟動Yarn start-yarn.sh 。
5.7 用jps查看進程
#hadoop1和hadoop2 root@hadoop1:~/hadoop-2.8.4# jps 15297 NodeManager 14340 JournalNode 741 Jps 14119 DataNode 14538 DFSZKFailoverController 15884 NameNode 13773 QuorumPeerMain #hadoop3 root@hadoop3:~# jps 9937 QuorumPeerMain 11107 ResourceManager 10836 DataNode 28037 Jps 10957 JournalNode 10637 NodeManager
6.集群測試
查看namenode狀態,目前hadoop1上的namenode處於active的狀態,嘗試kill掉namenode進程,發現hadoop2上的nn2由standby變成了active。
root@hadoop1:~# hdfs haadmin -getServiceState nn1 active root@hadoop1:~# hdfs haadmin -getServiceState nn2 standby
也可以通過瀏覽器訪問:http://Ip1:50070;http://Ip2:50070
運行一下hadoop提供的demo中的WordCount程序:
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.4.1.jar wordcount input out