Hadoop高可用集羣

1.簡介

 

 

若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服務器

 

 

2.HDFS HA集羣

 

 

2.1 模型


當有兩個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。

 

 

2.2 HDFS HA高可用集羣搭建

 

1.安裝並配置Zookeeper集羣

https://www.cnblogs.com/funyoung/p/8778106.html 

 

2.配置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發送的心跳。

 

3.配置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。

 

4.啓動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的狀態,http://192.168.1.80:50070http://192.168.1.81:50070

 

 

*能夠查看到主機名爲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 {

    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");
        }
    }

}

 

 

3.YARN HA集羣

 

 

3.1 模型

 

 

 

 

*啓動兩個ResourceManager後分別向Zookeeper註冊,經過Zookeeper管理他們的狀態,一旦狀態爲Active的ResourceManager沒法正常提供服務,Zookeeper將會當即將狀態爲StandBy的ResourceManager切換爲Active。

 

 

3.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:8088http://192.168.1.81:8088

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

 

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

 

*Zookeeper在必定時間內沒法接收到狀態爲Active的ResourceManager發送的心跳時,將會當即將狀態爲StandBy的ResourceManager切換爲Active。

相關文章
相關標籤/搜索