若HDFS集群中只配置了一個NameNode混埠,那么當(dāng)該NameNode所在的節(jié)點宕機,則整個HDFS就不能進行文件的上傳和下載赘风。
若YARN集群中只配置了一個ResourceManager时鸵,那么當(dāng)該ResourceManager所在的節(jié)點宕機,則整個YARN就不能進行任務(wù)的計算。
*Hadoop依賴Zookeeper進行各個模塊的HA配置红碑,其中狀態(tài)為Active的節(jié)點對外提供服務(wù)赶站,而狀態(tài)為StandBy的節(jié)點則只負(fù)責(zé)數(shù)據(jù)的同步,在必要時提供快速故障轉(zhuǎn)移木蹬。
2.HDFS HA集群
2.1 模型
當(dāng)有兩個NameNode時至耻,提供哪個NameNode地址給客戶端?
1.Hadoop提供了NameService進程镊叁,其是NameNode的代理尘颓,維護NameNode列表并存儲NameNode的狀態(tài),客戶端直接訪問的是NameService晦譬,NameService會將請求轉(zhuǎn)發(fā)給當(dāng)前狀態(tài)為Active的NameNode疤苹。
2.當(dāng)啟動HDFS時,DataNode將同時向兩個NameNode進行注冊敛腌。
怎樣發(fā)現(xiàn)NameNode無法提供服務(wù)以及如何進行NameNode間狀態(tài)的切換卧土?
1.Hadoop提供了FailoverControllerActive和FailoverControllerStandBy兩個進程用于NameNode的生命監(jiān)控惫皱。
2.FailoverControllerActive和FailoverControllerStandBy會分別監(jiān)控對應(yīng)狀態(tài)的NameNode,若NameNode無異常則定期向Zookeeper集群發(fā)送心跳尤莺,若在一定時間內(nèi)Zookeeper集群沒收到FailoverControllerActive發(fā)送的心跳旅敷,則認(rèn)為此時狀態(tài)為Active的NameNode已經(jīng)無法對外提供服務(wù),因此將狀態(tài)為StandBy的NameNode切換為Active狀態(tài)颤霎。
NameNode之間的數(shù)據(jù)如何進行同步和共享媳谁?
1.Hadoop提供了JournalNode用于存放NameNode中的編輯日志。
2.當(dāng)激活的NameNode執(zhí)行任何名稱空間上的修改時友酱,它將修改的記錄保存到JournalNode集群中晴音,備用的NameNode能夠?qū)崟r監(jiān)控JournalNode集群中日志的變化,當(dāng)監(jiān)控到日志發(fā)生改變時會將其同步到本地缔杉。
*當(dāng)狀態(tài)為Active的NameNode無法對外提供服務(wù)時锤躁,Zookeeper將會自動的將處于StandBy狀態(tài)的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監(jiān)控頁面地址 -->
<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>
<!-- 開啟失敗故障自動轉(zhuǎn)移-->
<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中的備份數(shù)(小于等于NameNode) -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 關(guān)閉HDFS的訪問權(quán)限 -->
<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發(fā)送的心跳或详。
2.配置Hadoop公共屬性(core-site.xml)
<configuration>
<!-- Hadoop工作目錄,用于存放Hadoop運行時NameNode系羞、DataNode產(chǎn)生的數(shù)據(jù) -->
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/hadoop/hadoop-2.9.0/data</value>
</property>
<!-- 默認(rèn)NameNode,使用NameService的名稱 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<!-- 開啟Hadoop的回收站機制,當(dāng)刪除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
*當(dāng)格式化ZK后杠人,ZK中將會多了hadoop-ha節(jié)點。
7.重啟HDFS集群
當(dāng)HDFS HA集群啟動完畢后宋下,可以分別訪問NameNode管理頁面查看當(dāng)前NameNode的狀態(tài)
*可以查看到主機名為hadoop1的NamNode其狀態(tài)為StandBy嗡善,而主機名為hadoop2的NameNode其狀態(tài)為Active。
8.模擬NameNode宕機学歧,手動殺死進程罩引。
此時訪問NameNode管理頁面,可見主機名為hadoop1的NameNode其狀態(tài)從原本的StandBy切換成Active枝笨。
2.3 JAVA操作HDFS HA集群
*由于在HDFS HA集群中存在兩個NameNode袁铐,且服務(wù)端暴露的是NameService,因此在通過JAVA連接HDFS HA集群時需要使用Configuration實例進行相關(guān)的配置横浑。
/**
* @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();
//指定默認(rèn)連接的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");
}
/**
* 創(chuàng)建目錄
*/
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;
}
/**
* 上傳當(dāng)前服務(wù)器的文件到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管理他們的狀態(tài),一旦狀態(tài)為Active的ResourceManager無法正常提供服務(wù)徙融,Zookeeper將會立即將狀態(tài)為StandBy的ResourceManager切換為Active洒缀。
2.2 YARN HA高可用集群搭建
1.配置YARN(yarn-site.xml)
<configuration>
<!-- 配置Reduce取數(shù)據(jù)的方式是shuffle(隨機) -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!-- 開啟日志 -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<!-- 設(shè)置日志的刪除時間 -1:禁用,單位為秒 -->
<property>
<name>yarn.log-aggregation。retain-seconds</name>
<value>864000</value>
</property>
<!-- 設(shè)置yarn的內(nèi)存大小,單位是MB -->
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>8192</value>
</property>
<!-- 設(shè)置yarn的CPU核數(shù) -->
<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功能,當(dāng)ResourceManager重啟時將會保存運行時信息到指定的位置,重啟成功后再進行讀取 -->
<property>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>true</value>
</property>
<!-- ResourceManager Restart使用的存儲方式(實現(xiàn)類) -->
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
</property>
<!-- ResourceManager重啟時數(shù)據(jù)保存在Zookeeper中的目錄 -->
<property>
<name>yarn.resourcemanager.zk-state-store.parent-path</name>
<value>/rmstore</value>
</property>
<!-- NodeManager Restart配置 -->
<!-- 啟用NodeManager的restart功能,當(dāng)NodeManager重啟時將會保存運行時信息到指定的位置,重啟成功后再進行讀取 -->
<property>
<name>yarn.nodemanager.recovery.enabled</name>
<value>true</value>
</property>
<!-- NodeManager重啟時數(shù)據(jù)保存在本地的目錄 -->
<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使用的存儲方式(實現(xiàn)類)
1.ResourceManager運行時的數(shù)據(jù)保存在ZK中:org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore
2.ResourceManager運行時的數(shù)據(jù)保存在HDFS中:org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore
3.ResourceManager運行時的數(shù)據(jù)保存在本地:org.apache.hadoop.yarn.server.resourcemanager.recovery.LeveldbRMStateStore
*使用不同的存儲方式將需要額外的配置項,可參考官網(wǎng)树绩,http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/ResourceManagerRestart.html
2.啟動YARN HA高可用集群
1.在ResourceManager所在節(jié)點中啟動YARN集群
2.手動啟動另一個ResourceManager
*當(dāng)啟動YARN HA集群后萨脑,可以分別訪問ResourceManager管理頁面,http://192.168.1.80:8088饺饭、http://192.168.1.81:8088渤早。
訪問狀態(tài)為StandBy的ResourceManager時,會將請求重定向到狀態(tài)為Active的ResourceManager的管理頁面瘫俊。
3.模擬ResourceManager宕機蛛芥,手動殺死進程
*Zookeeper在一定時間內(nèi)無法接收到狀態(tài)為Active的ResourceManager發(fā)送的心跳時,將會立即將狀態(tài)為StandBy的ResourceManager切換為Active军援。