若HDFS集羣中只配置了一個NameNode,那麼當該NameNode所在的節點宕機,則整個HDFS就不能進行文件的上傳和下載。html
若YARN集羣中只配置了一個ResourceManager,那麼當該ResourceManager所在的節點宕機,則整個YARN就不能進行任務的計算。node
*Hadoop依賴Zookeeper進行各個模塊的HA配置,其中狀態爲Active的節點對外提供服務,而狀態爲StandBy的節點則只負責數據的同步,在必要時提供快速故障轉移。web
Hadoop各個模塊剖析:http://www.javashuo.com/article/p-sjrfnywc-hc.htmlapache
Hadoop集羣管理:http://www.javashuo.com/article/p-vietrqco-hc.html服務器
當有兩個NameNode時,提供哪一個NameNode地址給客戶端?app
1.Hadoop提供了NameService進程,其是NameNode的代理,維護NameNode列表並存儲NameNode的狀態,客戶端直接訪問的是NameService,NameService會將請求轉發給當前狀態爲Active的NameNode。ssh
2.當啓動HDFS時,DataNode將同時向兩個NameNode進行註冊。webapp
怎樣發現NameNode沒法提供服務以及如何進行NameNode間狀態的切換?ide
1.Hadoop提供了FailoverControllerActive和FailoverControllerStandBy兩個進程用於NameNode的生命監控。oop
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。
https://www.cnblogs.com/funyoung/p/8778106.html
<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發送的心跳。
<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。
1.分別啓動JournalNode
2.格式化第一個NameNode並啓動
3.第二個NameNode同步第一個NameNode的信息
4.啓動第二個NameNode
5.啓動Zookeeper集羣
6.格式化Zookeeper
*當格式化ZK後,ZK中將會多了hadoop-ha節點。
7.重啓HDFS集羣
當HDFS HA集羣啓動完畢後,能夠分別訪問NameNode管理頁面查看當前NameNode的狀態,http://192.168.1.80:50070、http://192.168.1.81:50070。
*能夠查看到主機名爲hadoop1的NamNode其狀態爲StandBy,而主機名爲hadoop2的NameNode其狀態爲Active。
8.模擬NameNode宕機,手動殺死進程。
此時訪問NameNode管理頁面,可見主機名爲hadoop1的NameNode其狀態從本來的StandBy切換成Active。
*因爲在HDFS HA集羣中存在兩個NameNode,且服務端暴露的是NameService,所以在經過JAVA鏈接HDFS HA集羣時須要使用Configuration實例進行相關的配置。
/** * @Auther: ZHUANGHAOTANG * @Date: 2018/11/6 11:49 * @Description: */ public class HDFSUtils { private static Logger logger = LoggerFactory.getLogger(HDFSUtils.class); /** * NameNode Service */ private static final String NAMESERVER_URL = "hdfs://mycluster:8020"; /** * NameNode服務列表 */ private static final String[] NAMENODE_URLS = {"192.168.1.80:8020", "192.168.1.81:8020"}; /** * HDFS文件系統鏈接對象 */ private static FileSystem fs = null; static { Configuration conf = new Configuration(); //指定默認鏈接的NameNode,使用NameService的地址 conf.set("fs.defaultFS", NAMESERVER_URL); //指定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", NAMENODE_URLS[0]); conf.set("dfs.namenode.rpc-address.mycluster.nn2", NAMENODE_URLS[1]); //配置NameNode失敗自動切換的方式 conf.set("dfs.client.failover.proxy.provider.mycluster", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider"); try { fs = FileSystem.get(URI.create(NAMESERVER_URL), conf); } catch (IOException e) { logger.info("初始化HDFS鏈接失敗:{}", e); } } /** * 建立目錄 */ public static void mkdir(String dir) throws Exception { dir = NAMESERVER_URL + dir; if (!fs.exists(new Path(dir))) { fs.mkdirs(new Path(dir)); } } /** * 刪除目錄或文件 */ public static void delete(String dir) throws Exception { dir = NAMESERVER_URL + dir; fs.delete(new Path(dir), true); } /** * 遍歷指定路徑下的目錄和文件 */ public static List<String> listAll(String dir) throws Exception { List<String> names = new ArrayList<>(); dir = NAMESERVER_URL + dir; FileStatus[] files = fs.listStatus(new Path(dir)); for (FileStatus file : files) { if (file.isFile()) { //文件 names.add(file.getPath().toString()); } else if (file.isDirectory()) { //目錄 names.add(file.getPath().toString()); } else if (file.isSymlink()) { //軟或硬連接 names.add(file.getPath().toString()); } } return names; } /** * 上傳當前服務器的文件到HDFS中 */ public static void uploadLocalFileToHDFS(String localFile, String hdfsFile) throws Exception { hdfsFile = NAMESERVER_URL + hdfsFile; Path src = new Path(localFile); Path dst = new Path(hdfsFile); fs.copyFromLocalFile(src, dst); } /** * 經過流上傳文件 */ public static void uploadFile(String hdfsPath, InputStream inputStream) throws Exception { hdfsPath = NAMESERVER_URL + hdfsPath; FSDataOutputStream os = fs.create(new Path(hdfsPath)); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); byte[] data = new byte[1024]; int len; while ((len = bufferedInputStream.read(data)) != -1) { if (len == data.length) { os.write(data); } else { //最後一次讀取 byte[] lastData = new byte[len]; System.arraycopy(data, 0, lastData, 0, len); os.write(lastData); } } inputStream.close(); bufferedInputStream.close(); os.close(); } /** * 從HDFS中下載文件 */ public static byte[] readFile(String hdfsFile) throws Exception { hdfsFile = NAMESERVER_URL + hdfsFile; 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(); return data; } else { throw new Exception("File Not Found In HDFS"); } } }
*啓動兩個ResourceManager後分別向Zookeeper註冊,經過Zookeeper管理他們的狀態,一旦狀態爲Active的ResourceManager沒法正常提供服務,Zookeeper將會當即將狀態爲StandBy的ResourceManager切換爲Active。
<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
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。