大数据学习之路91-Hadoop的高可用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37050372/article/details/82989198

我们之前一直没有配置过hadoop的高可用,今天我们就来配置一下

之前我们的namenode只要一挂,则整个hdfs集群就完蛋。虽然我们可以通过重启的方式来恢复,可是我们重启好之前,我们的hdfs集群就不能提供服务了。所以它存在单点故障问题。

我们可以设置两台namenode ,一台为active,另一台为standby

active对外提供服务,而standby则不断的和active同步元数据。但这个元数据在hadoop中不是直接让他们就同步,而是在一开始的时候,让两个namenode保持相同的fsimage。当集群运行起来之后,active的内存中会不断的产生元数据。而且不光是产生元数据,还会将引起元数据变化的操作记录成日志。

namenode在高可用的模式下,不光会将日志记录在本地,还会将日志记录在日志管理系统里面,这个分布式的日志管理系统,叫Qjournal分布式日志管理系统。Qjournal是一个集群,它里面会有很多台机器,可以实现数据的可靠保存,他会将日志文件记录在很多台服务器上,当然这些服务器肯定有本地目录,而且它还有一个特点,就是只要有半数以上的机器还在,就可以正常的提供对外服务,这个就和Zookeeper有点像了,为什么说它和Zookeeper有点像呢?因为他们所用的数据同步的策略相同。Zookeeper及数据时候也是记录在很多台,也需要进行同步的。他们是通过paxos算法做数据一致性的同步。所以一般有奇数台节点比较合适,并且存活半数以上,这个集群就能正常运行。所以它是很可靠的,他一般不会挂。而这个系统是基于Zookeeper开发的。没有Zookeeper是运行不起来的。所以我们还要引入Zookeeper集群。

反正这么一来active就把这些日志记录到Qjournal集群中的服务器中了。然后standby就可以不断的从Qjournal中将这些日志读下来。读下来之后就可以做一些解析运算,然后更新到内存元数据,同时就把fsimage也顺便更新了。当然,standby不会只更新自己的fsimage,他会将自己的fsimage上传到active中,覆盖掉原来的fsimage。这压根就相当于也承担了secondary namenode的一些功能。(把日志和fsimage合并)

这样一来,这两个namenode的元数据基本上就能保持同步。当然,这样的同步也不可能做到完全同步,因为不可能active增加一条日志,standby就下载一条。

但是那些还没有来的及同步的数据是存在在Qjournal中的。万一active挂了,standby也顶多将还没来的及同步的数据拿来解析一下,再放入内存,这样内存中的元数据就恢复了。恢复之后就可以立马对外服务。

有两个namenode的话还需要有一个状态协调,两个namenode不可以起冲突。需要知道active是谁,那么另一台就是standby。

还有比如active死了,那么standby要不要切换状态。这些事情都是需要写逻辑的。

而namenode本身的代码就已经很复杂了,如果再把这些逻辑加进去,就会更加复杂。所以hadoop在这方面做了一些不一样的处理。

他把这些新增的状态管理的逻辑写在另外一个程序中了。

这个程序就叫做:ZKFC ------>基于Zookeeper的failover controller(失败切换  控制器)

这个ZKFC就负责不断的查询namenode的状态,看他正常不正常。

那么它是怎么查的呢?他是通过本地进程的方式去监测的namenode的,namenode其实也是一个进程。

每个namenode都需要有一个ZKFC来监测各自的namenode进程。这样就有两个ZKFC了。而且这两个ZKFC还会通过Zookeeper交换一些信息。比如说现在谁是active。

其实ZKFC做的最主要的事主要体现在切换功能上,假如说ZCFC发现自己监测的namenode死了,他就会告诉另一个ZKFC,我这边的namenode挂了。然后另一台ZKFC就会控制自己所监测的namenode去切换状态,但是他也不会立刻就这么做,因为立刻这么做会有风险。

这里所说的风险就是,这个active不见得就真的死了。因为判断active死亡是通过ZKFC的进程向namenode的进程发送请求来判断的。而namenode又是一个java程序,java程序是运行在jvm中的。而jvm是会对程序运行过程中产生的内存垃圾进行回收的。

而jvm的垃圾回收有一个有意思的小例子,假如在家里,你扔一点垃圾,你妈就给你扫一点垃圾,可是你妈的清理速度跟不上你的扔垃圾的速度,这个时候他就会说,你停下来,我要进行大扫除,这个时候进程就会停住。而namenode就相当于死了。

如果这个时候就立马将standby切换成active,那么当之前的active苏醒之后,它的状态依然为active。这样就会有两个active。就会出问题。

这种现象叫做脑裂,防止脑裂的策略有两种。

第一种:SSH kill通过ssh将判断为死亡的active杀掉

第二种:shell脚本

操作之后就可以放心大胆的进行切换了。但是事情并不是那么完美。ssh有可能一直请求无响应,这个时候就有可能使网络不通造成的。当然ZKFC没有那么傻,他不会一直等,如果一直连接不通,他就会转而去调我们提供给他的shell脚本。

我们可以在shell脚本中想办法,将要杀死的那台namenode的网络断掉,电停掉,或者告警等。之后就可以放心的切换状态了。

-----------------------------------------------------------------------------------------------------------------------------------------------------

namenode的单点故障解决起来之所以麻烦是因为我们需要找一个替代者,那么这两个namenode程序之间就需要进行状态同步。

yarn中的resource manager也存在单点故障,但是处理起来就没有这么麻烦。

resource manager的作用只是用来让客户端提交程序的,客户端提交一个程序,resource manager就把任务分配给若干个node manager去执行。

万一在某一段时间,resource manager突然挂了,大不了就是客户端无法提交job了。我们只需要重新来一台resource manager 就又可以重新提交job了,而这两个resource manager之间不存在状态的继承。所以自然就没有这么麻烦

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来我们就要来配置hadoop的高可用了

还记得我们之前配置的core-site.xml吗?

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://marshal:9000/</value>
    </property>
</configuration>

我们之前通过这个配置可以指定namenode的端口号。

但是现在不能这样了,因为我们有两个namenode,如果我们写死了这个值,那么客户端肯定要出问题的。因为两个namenode随时可能会所换。所以这个namenode肯定不能是一个具体的值。

我们取而代之的是填入nameservice的名字。这个nameservice就代表一对namenode,只不过这个nameservice的名字是可以自己定的。但是光凭一个名字是不足以说明是哪两个namenode的,所以后面还有其他的配置文件去说明。

修改之后的core-site.xml:

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://marshalservice:9000/</value>
    </property>
	
	<property>
	    <name>ha.zookeeper.quorum</name>
		<value>marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181</value>
	</property>
</configuration>

接下来的重点就是修改hdfs-site.xml,在这个文件中我们就要指定那个名字代表哪些namenode了。
 

dfs.nameservices就是我们之前定义的那个名称。

dfs.ha.namenodes.marshalservice,就是指定有哪些namenode

接着还要指定这两个namenode的rpc通信地址,和http通信地址。

dfs.namenode.shared.edits.dir指定了namenode的共享edits元数据在JournalNode上的存放位置。

那么qjournal拿到日志之后还是要放在磁盘上的,dfs.journalnode.edits.dir,就指定了存放的位置。

我们还需要配置ZKFC的相关内容

dfs.ha.automatic-failover.enabled这个开启了namenode的失败自动切换

dfs.client.failover.proxy.provider.marshalservice配置了失败自动切换的实现方式。

dfs.ha.fencing.methods配置了隔离机制方法,多个机制用换行分割,即每个机制占用一行,我们这里并没有配置真正的shell脚本,而是配置了个假的。有真正的需求的时候,可以真正的配置一个。

由于使用sshfence隔离机制的时候还需要ssh免登陆,所以我们还需要配置ssh

dfs.ha.fencing.ssh.connect-timeout配置了隔离机制的超时时间
而且配置了这些之后,我们之前配置的secondary namenode就不需要了。

配置好的hdfs-site.xml

<configuration>
    <property>
	    <name>dfs.nameservices</name>
		<value>marshalservice</value>
	</property>
	<property>
	    <name>dfs.ha.namenodes.marshalservice</name>
		<value>marshal,marshal01</value>
	</property>
	<property>
	    <name>dfs.namenode.rpc-address.marshalservice.marshal</name>
		<value>marshal:9000</value>
	</property>
	<property>
	    <name>dfs.namenode.http-address.marshalservice.marshal</name>
		<value>marshal:50070</value>
	</property>
	<property>
	    <name>dfs.namenode.rpc-address.marshalservice.marshal01</name>
		<value>marshal01:9000</value>
	</property>
	<property>
	    <name>dfs.namenode.http-address.marshalservice.marshal01</name>
		<value>marshal01:50070</value>
	</property>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>/root/hadoopdata/name/</value>
    </property>
   <property>
        <name>dfs.datanode.data.dir</name>
        <value>/root/hadoopdata/data/</value>
   </property>
  <!-- <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>marshal01:50090</value>
   </property> -->
   <property>
       <name>dfs.namenode.shared.edits.dir</name>
	   <value>qjournal://marshal03:8485;marshal04:8485;marshal05:8485/marshalservice</value>
   </property>
   <property>
       <name>dfs.journalnode.edits.dir</name>
	   <value>/root/hadoopdata/journaldata</value>
   </property>
   <property>
       <name>dfs.ha.automatic-failover.enabled</name>
	   <value>true</value>
   </property>
   <property>
       <name>dfs.client.failover.proxy.provider.marshalservice</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>
   <property>
       <name>dfs.ha.fencing.ssh.private-key-files</name>
	   <value>/root/.ssh/id_rsa</value>
   </property>
   <property>
       <name>dfs.ha.fencing.ssh.connect-timeout</name>
	   <value>30000</value>
   </property>
   
</configuration>

然后就是要配置mapred-site.xml:其实这个文件可以不用配置

这个文件主要是用来指定我们提交job的时候是提交到yarn上还是运行在本地。

下面是具体配置:

<configuration>
<!--指定mr框架为yarn方式 -->
    <property>
	    <name>mapreduce.framework.name</name>
		<value>yarn</value>
	</property>
</configuration>

------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来我们要配置的就是yarn的高可用

首先来修改yarn-site.xml

<configuration>

<!-- Site specific YARN configuration properties -->
    <!-- 启动高可用-->
     <property>
        <name>yarn.resourcemanager.ha.enabled</name>
        <value>true</value>    
     </property>
	 <!-- 指定resource manager的集群id-->
	 <property>
        <name>yarn.resourcemanager.cluster-id</name>
        <value>yrc</value>    
     </property>
	 <!-- 指定resource manager的逻辑名字,具体是哪台机器可以在下面的配置指定-->
	 <property>
        <name>yarn.resourcemanager.ha.rm-ids</name>
        <value>rm1,rm2</value>    
     </property>
	 <!-- 分别指定rm的地址-->
     <property>
        <name>yarn.resourcemanager.hostname.rm1</name>
        <value>marshal</value>    
     </property>
	 <property>
        <name>yarn.resourcemanager.hostname.rm2</name>
        <value>marshal01</value>    
     </property>
	 <!-- 指定Zookeeper集群地址-->
	 <property>
        <name>yarn.resourcemanager.zk-address</name>
        <value>marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181</value>    
     </property>
	 
     <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
     </property>
     <property>
        <name>yarn.nodemanager.resource.memory-mb</name>
        <value>2048</value>
     </property>
     <property>
        <name>yarn.nodemanager.resource.cpu-vcores</name>
        <value>2</value>
     </property>
     <property>
        <name>yarn.scheduler.maximum-allocation-mb</name>
        <value>2048</value>
     </property>



</configuration>

配置文件配置好之后需要进行分发:

接着我们要先将Zookeeper集群启动起来,因为journalnode要向启动起来必须先启动Zookeeper

接下来我们还需要在(marshal03,marshal04,marshal05)上手动启动journalnode(之前在hdfs-site.xml中配置过)

启动命令为:

sbin/hadoop-daemon.sh start journalnode

我们可以运行jps命令检查看marshal03,marshal04,marshal05上是否多了JournalNode进程。

启动了JournalNode进程之后我们就可以开始格式化namenode了。

那么为什么我们格式化Journalnode之前还要启动Journalnode呢?

因为现在namenode记录日志除了在本地记录还会在Journalnode记录。所以我们先把Journalnode启动起来,这样格式化的时候,就会在JournalNode中也进行相应的初始化工作。

现在我们有两个namenode,那么我们格式化谁?我们只选择格式化一个,另一个不能格式化,因为如果另一个也格式化的话,就会生成新的集群id,这样这两个namenode就不在一块了。

所以我们的选择是,先格式化一台,然后将格式化之后的目录复制给另一个namenode,这样这两个namenode就完全一样了。

我们在marshal这台机器执行命令:

hadoop namenode -format

然后将生成的hadoopdata目录复制给marshal01

scp -r hadoopdata/ marshal01:$PWD

接着我们还要格式化ZKFC(在marshal上执行即可):

ZKFC是用来做状态切换的,它还会在zookeeper上记录一些信息。既然要在zookeeper上记录信息,就肯定要在zookeeper上先把父节点建立起来。

我们来看,在格式化之前zookeeper节点的目录如下:

我们执行命令进行格式化:

hdfs zkfc -formatZK

执行命令之后可以看到:

然后我们再看Zookeeper的目录的时候就可以看到:

多了hadoop-ha的目录:

此时这里还没有数据

此时我们启动hdfs:

start-dfs.sh

这下我们再执行jps就可以看到marshal和marshal01上都有namenode,datanode,ZKFC的进程存在。

然后我们访问http就可以看到:

我们之前也配置了yarn的高可用但是当我们在marshal执行:

start-yarn.sh

之后发现,只有marshal是resource manager,其他机器都是node manager,所以在这里我们还要手动在marshal01上启动另一个resource manager

我们在marshal01上执行:

yarn-daemon.sh start resourcemanager

在HA的模式下,客户端要想知道集群中的情况,必须将集群上的配置文件放入工程中的src

猜你喜欢

转载自blog.csdn.net/qq_37050372/article/details/82989198