1. 程式人生 > >Hadoop高可用叢集

Hadoop高可用叢集

若HDFS叢集中只配置了一個NameNode,那麼當該NameNode所在的節點宕機,則整個HDFS就不能進行檔案的上傳和下載。

若YARN叢集中只配置了一個ResourceManager,那麼當該ResourceManager所在的節點宕機,則整個YARN就不能進行任務的計算。

*Hadoop依賴Zookeeper進行各個模組的HA配置,其中狀態為Active的節點對外提供服務,而狀態為StandBy的節點則只負責資料的同步,在必要時提供快速故障轉移。

2.HDFS HA叢集

2.1 模型

當有兩個NameNode時,提供哪個NameNode地址給客戶端?

 

1.Hadoop提供了NameService程序,其是NameNode的代理,維護NameNode列表並存儲NameNode的狀態,客戶端直接訪問的是NameService,NameService會將請求轉發給當前狀態為Active的NameNode。

2.當啟動HDFS時,DataNode將同時向兩個NameNode進行註冊。

怎樣發現NameNode無法提供服務以及如何進行NameNode間狀態的切換?

 

1.Hadoop提供了FailoverControllerActive和FailoverControllerStandBy兩個程序用於NameNode的生命監控。

2.FailoverControllerActive和FailoverControllerStandBy會分別監控對應狀態的NameNode,若NameNode無異常則定期向Zookeeper叢集傳送心跳,若在一定時間內Zookeeper叢集沒收到FailoverControllerActive傳送的心跳,則認為此時狀態為Active的NameNode已經無法對外提供服務,因此將狀態為StandBy的NameNode切換為Active狀態。

NameNode之間的資料如何進行同步和共享?

1.Hadoop提供了JournalNode用於存放NameNode中的編輯日誌。

2.當啟用的NameNode執行任何名稱空間上的修改時,它將修改的記錄儲存到JournalNode叢集中,備用的NameNode能夠實時監控JournalNode叢集中日誌的變化,當監控到日誌發生改變時會將其同步到本地。

*當狀態為Active的NameNode無法對外提供服務時,Zookeeper將會自動的將處於StandBy狀態的NameNode切換成Active。

2.2 HDFS HA高可用叢集搭建

1.配置HDFS(hdfs-site.xml)

<configuration>

<!-- 指定NameService的名稱 -->

<property>

<name>dfs.nameservices</name>

<value>mycluster</value>

</property>

<!-- 指定NameService下兩個NameNode的名稱 -->

<property>

<name>dfs.ha.namenodes.mycluster</name>

<value>nn1,nn2</value>

</property>

<!-- 分別指定NameNode的RPC通訊地址 -->

<property>

<name>dfs.namenode.rpc-address.mycluster.nn1</name>

<value>192.168.1.80:8020</value>

</property>

<property>

<name>dfs.namenode.rpc-address.mycluster.nn2</name>

<value>192.168.1.81:8020</value>

</property>

<!-- 分別指定NameNode的Web監控頁面地址 -->

<property>

<name>dfs.namenode.http-address.mycluster.nn1</name>

<value>192.168.1.80:50070</value>

</property>

<property>

<name>dfs.namenode.http-address.mycluster.nn2</name>

<value>192.168.1.81:50070</value>

</property>

<!-- 指定NameNode編輯日誌儲存在JournalNode叢集中的目錄-->

<property>

<name>dfs.namenode.shared.edits.dir</name>

<value>qjournal://192.168.1.80:8485;192.168.1.81:8485;192.168.1.82:8485/mycluster</value>

</property>

<!-- 指定JournalNode叢集存放日誌的目錄-->

<property>

<name>dfs.journalnode.edits.dir</name>

<value>/usr/hadoop/hadoop-2.9.0/journalnode</value>

</property>

<!-- 配置NameNode失敗自動切換的方式-->

<property>

<name>dfs.client.failover.proxy.provider.mycluster</name>

<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>

</property>

<!-- 配置隔離機制-->

<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.automatic-failover.enabled</name>

<value>true</value>

</property>

<!-- 配置Zookeeper地址-->

<property>

<name>ha.zookeeper.quorum</name>

<value>192.168.1.80:2181,192.168.1.81:2181,192.168.1.82:2181</value>

</property>

<!-- 檔案在HDFS中的備份數(小於等於NameNode) -->

<property>

<name>dfs.replication</name>

<value>3</value>

</property>

<!-- 關閉HDFS的訪問許可權 -->

<property>

<name>dfs.permissions.enabled</name>

<value>false</value>

</property>

<!-- 指定一個配置檔案,使NameNode過濾配置檔案中指定的host -->

<property>

<name>dfs.hosts.exclude</name>

<value>/usr/hadoop/hadoop-2.9.0/etc/hadoop/hdfs.exclude</value>

</property>

</configuration>

*指定NameNode的RPC通訊地址是為了接收FailoverControllerActive和FailoverControllerStandBy以及DataNode傳送的心跳。

2.配置Hadoop公共屬性(core-site.xml)

<configuration>

<!-- Hadoop工作目錄,用於存放Hadoop執行時NameNode、DataNode產生的資料 -->

<property>

<name>hadoop.tmp.dir</name>

<value>/usr/hadoop/hadoop-2.9.0/data</value>

</property>

<!-- 預設NameNode,使用NameService的名稱 -->

<property>

<name>fs.defaultFS</name>

<value>hdfs://mycluster</value>

</property>

<!-- 開啟Hadoop的回收站機制,當刪除HDFS中的檔案時,檔案將會被移動到回收站(/usr/<username>/.Trash),在指定的時間過後再對其進行刪除,此機制可以防止檔案被誤刪除 -->

<property>

<name>fs.trash.interval</name>

<!-- 單位是分鐘 -->

<value>1440</value>

</property>

</configuration>

*在HDFS HA叢集中,StandBy的NameNode會對namespace進行checkpoint操作,因此就不需要在HA叢集中執行SecondaryNameNode、CheckpintNode、BackupNode。

2.啟動HDFS HA高可用叢集

1.分別啟動JournalNode

 

 

 

2.格式化第一個NameNode並啟動

 

 

3.第二個NameNode同步第一個NameNode的資訊

 

4.啟動第二個NameNode

 

5.啟動Zookeeper叢集

 

 

 

6.格式化Zookeeper

 

*當格式化ZK後,ZK中將會多了hadoop-ha節點。

7.重啟HDFS叢集

 

 

當HDFS HA叢集啟動完畢後,可以分別訪問NameNode管理頁面檢視當前NameNode的狀態

 

 

*可以檢視到主機名為hadoop1的NamNode其狀態為StandBy,而主機名為hadoop2的NameNode其狀態為Active。

8.模擬NameNode宕機,手動殺死程序。

 

此時訪問NameNode管理頁面,可見主機名為hadoop1的NameNode其狀態從原本的StandBy切換成Active。

 

2.3 JAVA操作HDFS HA叢集

*由於在HDFS HA叢集中存在兩個NameNode,且服務端暴露的是NameService,因此在通過JAVA連線HDFS HA叢集時需要使用Configuration例項進行相關的配置。

/**

* @Auther: ZHUANGHAOTANG

* @Date: 2018/11/6 11:49

* @Description:

*/

public class HDFSUtils {

/**

* HDFS NamenNode URL

*/

private static final String NAMENODE_URL = "hdfs://mycluster:8020";

/**

* 配置項

*/

private static Configuration conf = null;

static {

conf = new Configuration();

//指定預設連線的NameNode,使用NameService的名稱

conf.set("fs.defaultFS", "hdfs://mycluster");

//指定NameService的名稱

conf.set("dfs.nameservices", "mycluster");

//指定NameService下的NameNode列表

conf.set("dfs.ha.namenodes.mycluster", "nn1,nn2");

//分別指定NameNode的RPC通訊地址

conf.set("dfs.namenode.rpc-address.mycluster.nn1", "hadoop1:8020");

conf.set("dfs.namenode.rpc-address.mycluster.nn2", "hadoop2:8020");

//配置NameNode失敗自動切換的方式

conf.set("dfs.client.failover.proxy.provider.mycluster", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");

}

/**

* 建立目錄

*/

public static void mkdir(String dir) throws Exception {

if (StringUtils.isBlank(dir)) {

throw new Exception("Parameter Is NULL");

}

dir = NAMENODE_URL + dir;

FileSystem fs = FileSystem.get(URI.create(NAMENODE_URL), conf);

if (!fs.exists(new Path(dir))) {

fs.mkdirs(new Path(dir));

}

fs.close();

}

/**

* 刪除目錄或檔案

*/

public static void delete(String dir) throws Exception {

if (StringUtils.isBlank(dir)) {

throw new Exception("Parameter Is NULL");

}

dir = NAMENODE_URL + dir;

FileSystem fs = FileSystem.get(URI.create(NAMENODE_URL), conf);

fs.delete(new Path(dir), true);

fs.close();

}

/**

* 遍歷指定路徑下的目錄和檔案

*/

public static List<String> listAll(String dir) throws Exception {

List<String> names = new ArrayList<>();

if (StringUtils.isBlank(dir)) {

throw new Exception("Parameter Is NULL");

}

dir = NAMENODE_URL + dir;

FileSystem fs = FileSystem.get(URI.create(dir), conf);

FileStatus[] files = fs.listStatus(new Path(dir));

for (int i = 0, len = files.length; i < len; i++) {

if (files[i].isFile()) { //檔案

names.add(files[i].getPath().toString());

} else if (files[i].isDirectory()) { //目錄

names.add(files[i].getPath().toString());

} else if (files[i].isSymlink()) { //軟或硬連結

names.add(files[i].getPath().toString());

}

}

fs.close();

return names;

}

/**

* 上傳當前伺服器的檔案到HDFS中

*/

public static void uploadLocalFileToHDFS(String localFile, String hdfsFile) throws Exception {

if (StringUtils.isBlank(localFile) || StringUtils.isBlank(hdfsFile)) {

throw new Exception("Parameter Is NULL");

}

hdfsFile = NAMENODE_URL + hdfsFile;

FileSystem fs = FileSystem.get(URI.create(NAMENODE_URL), conf);

Path src = new Path(localFile);

Path dst = new Path(hdfsFile);

fs.copyFromLocalFile(src, dst);

fs.close();

}

/**

* 通過流上傳檔案

*/

public static void uploadFile(String hdfsPath, InputStream inputStream) throws Exception {

if (StringUtils.isBlank(hdfsPath)) {

throw new Exception("Parameter Is NULL");

}

hdfsPath = NAMENODE_URL + hdfsPath;

FileSystem fs = FileSystem.get(URI.create(NAMENODE_URL), conf);

FSDataOutputStream os = fs.create(new Path(hdfsPath));

BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

byte[] data = new byte[1024];

while (bufferedInputStream.read(data) != -1) {

os.write(data);

}

os.close();

fs.close();

}

/**

* 從HDFS中下載檔案

*/

public static byte[] readFile(String hdfsFile) throws Exception {

if (StringUtils.isBlank(hdfsFile)) {

throw new Exception("Parameter Is NULL");

}

hdfsFile = NAMENODE_URL + hdfsFile;

FileSystem fs = FileSystem.get(URI.create(NAMENODE_URL), conf);

Path path = new Path(hdfsFile);

if (fs.exists(path)) {

FSDataInputStream is = fs.open(path);

FileStatus stat = fs.getFileStatus(path);

byte[] data = new byte[(int) stat.getLen()];

is.readFully(0, data);

is.close();

fs.close();

return data;

} else {

throw new Exception("File Not Found In HDFS");

}

}

}

2.YARN HA叢集

2.1 模型

 

*啟動兩個ResourceManager後分別向Zookeeper註冊,通過Zookeeper管理他們的狀態,一旦狀態為Active的ResourceManager無法正常提供服務,Zookeeper將會立即將狀態為StandBy的ResourceManager切換為Active。

2.2 YARN HA高可用叢集搭建

1.配置YARN(yarn-site.xml)

<configuration>

<!-- 配置Reduce取資料的方式是shuffle(隨機) -->

<property>

<name>yarn.nodemanager.aux-services</name>

<value>mapreduce_shuffle</value>

</property>

<!-- 開啟日誌 -->

<property>

<name>yarn.log-aggregation-enable</name>

<value>true</value>

</property>

<!-- 設定日誌的刪除時間 -1:禁用,單位為秒 -->

<property>

<name>yarn.log-aggregation。retain-seconds</name>

<value>864000</value>

</property>

<!-- 設定yarn的記憶體大小,單位是MB -->

<property>

<name>yarn.nodemanager.resource.memory-mb</name>

<value>8192</value>

</property>

<!-- 設定yarn的CPU核數 -->

<property>

<name>yarn.nodemanager.resource.cpu-vcores</name>

<value>8</value>

</property>

<!-- YARN HA配置 -->

<!-- 開啟yarn ha -->

<property>

<name>yarn.resourcemanager.ha.enabled</name>

<value>true</value>

</property>

<!-- 指定yarn ha的名稱 -->

<property>

<name>yarn.resourcemanager.cluster-id</name>

<value>cluster1</value>

</property>

<!-- 分別指定兩個ResourceManager的名稱 -->

<property>

<name>yarn.resourcemanager.ha.rm-ids</name>

<value>rm1,rm2</value>

</property>

<!-- 分別指定兩個ResourceManager的地址 -->

<property>

<name>yarn.resourcemanager.hostname.rm1</name>

<value>192.168.1.80</value>

</property>

<property>

<name>yarn.resourcemanager.hostname.rm2</name>

<value>192.168.1.81</value>

</property>

<!-- 分別指定兩個ResourceManager的Web訪問地址 -->

<property>

<name>yarn.resourcemanager.webapp.address.rm1</name>

<value>192.168.1.80:8088</value>

</property>

<property>

<name>yarn.resourcemanager.webapp.address.rm2</name>

<value>192.168.1.81:8088</value>

</property>

<!-- 配置使用的Zookeeper叢集 -->

<property>

<name>yarn.resourcemanager.zk-address</name>

<value>192.168.1.80:2181,192.168.1.81:2181,192.168.1.82:2181</value>

</property>

<!-- ResourceManager Restart配置 -->

<!-- 啟用ResourceManager的restart功能,當ResourceManager重啟時將會儲存執行時資訊到指定的位置,重啟成功後再進行讀取 -->

<property>

<name>yarn.resourcemanager.recovery.enabled</name>

<value>true</value>

</property>

<!-- ResourceManager Restart使用的儲存方式(實現類) -->

<property>

<name>yarn.resourcemanager.store.class</name>

<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>

</property>

<!-- ResourceManager重啟時資料儲存在Zookeeper中的目錄 -->

<property>

<name>yarn.resourcemanager.zk-state-store.parent-path</name>

<value>/rmstore</value>

</property>

<!-- NodeManager Restart配置 -->

<!-- 啟用NodeManager的restart功能,當NodeManager重啟時將會儲存執行時資訊到指定的位置,重啟成功後再進行讀取 -->

<property>

<name>yarn.nodemanager.recovery.enabled</name>

<value>true</value>

</property>

<!-- NodeManager重啟時資料儲存在本地的目錄 -->

<property>

<name>yarn.nodemanager.recovery.dir</name>

<value>/usr/hadoop/hadoop-2.9.0/data/rsnodemanager</value>

</property>

<!-- 配置NodeManager的RPC通訊埠 -->

<property>

<name>yarn.nodemanager.address</name>

<value>0.0.0.0:45454</value>

</property>

</configuration>

ResourceManager Restart使用的儲存方式(實現類)

1.ResourceManager執行時的資料儲存在ZK中:org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore

2.ResourceManager執行時的資料儲存在HDFS中:org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore

3.ResourceManager執行時的資料儲存在本地:org.apache.hadoop.yarn.server.resourcemanager.recovery.LeveldbRMStateStore

*使用不同的儲存方式將需要額外的配置項,可參考官網,http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/ResourceManagerRestart.html

2.啟動YARN HA高可用叢集

1.在ResourceManager所在節點中啟動YARN叢集

 

2.手動啟動另一個ResourceManager

 

*當啟動YARN HA集群后,可以分別訪問ResourceManager管理頁面,http://192.168.1.80:8088、http://192.168.1.81:8088。

訪問狀態為StandBy的ResourceManager時,會將請求重定向到狀態為Active的ResourceManager的管理頁面。

3.模擬ResourceManager宕機,手動殺死程序

 

*Zookeeper在一定時間內無法接收到狀態為Active的ResourceManager傳送的心跳時,將會立即將狀態為StandBy的ResourceManager切換為Active。