Heartbeat简介和作用/Linux-HA工程组件教程Server

印迹发布于:2020-3-22 996

简介

Heartbeat是Linux-HA工程的一个组件,自1999年开始到现在,发布了众多版本,是目前开源Linux-HA项目最成功的一个例子,在行业内得到了广泛的应用,Heartbeat 项目是 Linux-HA 工程的一个组成部分,它实现了一个高可用集群系统。心跳服务和集群通信是高可用集群的两个关键组件,在 Heartbeat 项目里,由 heartbeat 模块实现了这两个功能。下面描述了 heartbeat 模块的可靠消息通信机制,并对其实现原理做了一些介绍。

项目地址:http://www.linux-ha.org/wiki/Heartbeat

HeartBeat作用

通过HeartBeat,可以将资源(IP以及程序服务等资源)从一台已经故障的计算机快速转移到另一台正常运转的机器上继续提供服务,一般称之为高可用的服务。在实际的生产应用场景中,heartbeat的功能和另一个高可用的开源软件keepalived有很多的相同之处,在我们实际的生产业务中也是有区别的。

HeartBeat工作原理

heartbeat (Linux-HA)的工作原理:heartbeat最核心的包括两个部分,心跳监测部分和资源接管部分,心跳监测可以通过网络链路和串口进行,而且支持冗 余链路,它们之间相互发送报文来告诉对方自己当前的状态,如果在指定的时间内未收到对方发送的报文,那么就认为对方失效,这时需启动资源接管模块来接管运 行在对方主机上的资源或者服务。

通过修改Heartbeat的软件的配置文件,可以制定那一台Heartbeat服务器作为主服务器,则另一台将自动成为热备服务器。然后在热备服务器上配置Heartbeat守护程序来监听来自主服务器的心跳消息。如果热备服务器在指定时间内为监听到来自主服务器的心跳,就会启动故障转义程序,并取得主服务器上的相关资源服务的所有权,接替主服务器继续不间断的提供服务,从而达到资源以及服务高可用的目的。

以上描述heartbeat的主备模式,heartbeat还支持主主模式,即两台服务器互为主备,这是他们之间还会互相发送报文来告诉对方自己的当前的状态,如果在指定的时间内未收到对方发送的心跳报文,那么,一方就会认为对方失效或者是已经宕机了,这时每个运行正常的主机就会启动自身的资源接管模块来接管运行在对方主机上的资源或者是服务,继续为用户提供服务。一般情况下,可以较好的实现一台主机故障后,企业业务能够不间断的持续的提供服务。注意:所谓的业务不间断,在故障转移期间也是需要切换时间的,heartbeat的切换时间是5-20秒。

切换的常见条件:

1)服务器宕机

2)Heartbeat服务本故障

3)中间的连接线路故障

应用服务故障则不会产生切换,可以通过服务宕机把heartbeat服务停掉。

heartbeat的心跳连接:

讲过上面的描述,要部署heartbeat服务,至少需要两台主机才能完成。那么,要实现高可用服务,这两台主机之间,是如何做到互相通信互相监控的呢?

下面是两台heartbeat主机之间通信的一些常用的可行的方法:

1)串行电缆,即所谓的串口(首选,缺点是距离不能太远)

2)一根以太网电缆量网口直连(生产环境中常用的方式)

3)以太网电缆,通过交换机等网络设备连接(次选,原因是增加了故障点,不好排查故障,同时,线路不是专用的心跳线,容易受其他数据传输的影响,导致心跳报文发送问题)

高可用集群

高可用集群是指一组通过硬件和软件连接起来的独立计算机,它们在用户面前表现为一个单一系统,在这样的一组计算机系统内部的一个或者多个节点停止工作,服务会从故障节点切换到正常工作的节点上运行,不会引起服务中断。从这个定义可以看出,集群必须检测节点和服务何时失效,何时恢复为可用。这个任务通常由一组被称为“心跳”的代码完成。在Linux-HA里这个功能由一个叫做heartbeat的程序完成。

消息通信模型

Heartbeat包括以下几个组件:

heartbeat – 节点间通信校验模块
CRM - 集群资源管理模块
CCM - 维护集群成员的一致性
LRM - 本地资源管理模块
StonithDaemon - 提供节点重启服务
logd - 非阻塞的日志记录
apphbd - 提供应用程序级的看门狗计时器
Recovery Manager - 应用故障恢复
底层结构–包括插件接口、进程间通信等
CTS – 集群测试系统,集群压力测试
这里主要分析的是Heartbeat的集群通信机制,所以这里主要关注的是heartbeat模块。
heartbeat模块由以下几个进程构成:
master进程(masterprocess)
FIFO子进程(fifochild)
read子进程(readchild)
write子进程(writechild)

在heartbeat里每一条通信通道对应于一个write子进程和一个read子进程,假设n是通信通道数,p为heartbeat模块的进程数,则p、n有以下关系:

p=2*n+2

在heartbeat里,master进程把自己的数据或者是客户端发送来的数据,通过IPC发送到write子进程,write子进程把数据发送到网络;同时read子进程从网络读取数据,通过IPC发送到master进程,由master进程处理或者由master进程转发给其客户端处理。

Heartbeat启动的时候,由master进程来启动FIFO子进程、write子进程和read子进程,最后再启动client进程。

实现

一般集群通信有两类消息包,一类是心跳消息包,这类消息包通告集群内节点的存活情况;另一类是控制消息包,这类消息包负责集群的节点和资源管理。heartbeat把心跳消息包看成是控制消息包的一个特例,采用相同的通信通道进行发送,这使得协议的实现简单化,而且很有效,并把相应的代码限制在几百行之内。

在heartbeat里,一切流向网络的数据都由master进程发送到write子进程进行发送。master进程调用send_cluster_msg()函数把消息发送到所有的write子进程。下面通过一些代码片段看看heartbeat是怎么发送消息的。在介绍代码之前先介绍相关的重要数据结构.

Heartbeat的消息包数据结构structha_msg{intnfields;/*消息包数据域的个数*/intnalloc;/*己分配的内存块个数*/char**names;/*消息包数据域的名称*/size_t*nlens;/*各个数据域称的长度*/void**values;/*与数据域名称对应的数据值*/size_t*vlens;/*各个数据域对应的数据值的长度*/int*types;/*消息包的类型*/};

Heartbeat的历史消息队列structmsg_xmit_hist{structha_msg*msgq[MAXMSGHIST];/*历史消息队列*/seqno_tseqnos[MAXMSGHIST];/*历史消息序列号*/longclock_tlastrexmit[MAXMSGHIST];/*上一次重传的时间*/intlastmsg;/*上一次重传到的消息序列号*/seqno_thiseq;/*最大消息序列号*/seqno_tlowseq;/*最小消息序列号*/seqno_tackseq;/*确认了的消息序列号*/structnode_info*lowest_acknode;/*确认的节点*/};

代码所属文件heartbeat/heartbeat.c

intsend_cluster_msg(structha_msg*msg){...pid_tourpid=getpid();...
if(ourpid==processes[0]){/*来自master进程的消息*//*添加控制信息,包括源节点名,源节点全局标识符,序列号,代数,时间等*/if((msg=add_control_msg_fields(msg))!=NULL){/*可靠的多播消息包传递*/rc=process_outbound_packet(&msghist,msg);}}else{/*来自client进程的消息*/intffd=-1;char*smsg=NULL;

...

/*发送到FIFO进程*/

if((smsg=msg2wirefmt_noac(msg,&len))==NULL){...}elseif((ffd=open(FIFONAME,O_WRONLY|O_APPEND))nodename)==0);
/*把消息转换成字符串*/smsg=msg2wirefmt(msg,&len);

...

if(cseq!=NULL){/*存放到历史消息队列里,通过序列号记录,如果需要,则进行重传*/add2_xmit_hist(hist,msg,seqno);}

...
/*通过write子进程发送到所有的网络接口上*/send_to_all_media(smsg,len);
...
returnHA_OK;}
add2_xmit_hist()函数把发送的消息发到一个历史消息队列里去,队列的最大长度为200。如果接收者请求重传消息,发送者通过序列号在该队列里查找要重传的消息,如果找到则进行重传。下面是相关代码。

staticvoidadd2_xmit_hist(structmsg_xmit_hist*hist,structha_msg*msg,seqno_tseq){intslot;structha_msg*slotmsg;
...
/*查找队列里消息存放的位置*/slot=hist->lastmsg+1;if(slot>=MAXMSGHIST){/*到达队尾,从头开始。在这里实现循环队列*/slot=0;}
hist->hiseq=seq;slotmsg=hist->msgq[slot];
/*删除队列中找到的位置上的旧消息*/if(slotmsg!=NULL){hist->lowseq=hist->seqnos[slot];hist->msgq[slot]=NULL;if(!ha_is_allocated(slotmsg)){...}else{ha_msg_del(slotmsg);}}
hist->msgq[slot]=msg;hist->seqnos[slot]=seq;hist->lastrexmit[slot]=0L;hist->lastmsg=slot;
if(enable_flow_control&&live_node_count>1&&(hist->hiseq–hist->lowseq)>((MAXMSGHIST*3)/4)){/*消息队列长度大于告警长度,记录日志*/...}if(enable_flow_control&&hist->hiseq–hist->ackseq>FLOWCONTROL_LIMIT){/*消息队列的长度大于流控限制长度*/if(live_node_counthiseq–(FLOWCONTROL_LIMIT–1));all_clients_resume();}else{/*client进程发送消息过快,暂停所有的client进程*/all_clients_pause();hist_display(hist);}}
}

当发送者收到接收者的重传请求后,通过回调函数HBDoMsg_T_REXMIT()函数调用process_rexmit()函数进行消息重传。

#defineMAX_REXMIT_BATCH50/*每次最多重传的消息包数*/
staticvoidprocess_rexmit(structmsg_xmit_hist*hist,structha_msg*msg){constchar*cfseq;constchar*clseq;seqno_tfseq=0;seqno_tlseq=0;seqno_tthisseq;intfirstslot=hist->lastmsg–1;intrexmit_pkt_count=0;constchar*fromnodename=ha_msg_value(msg,F_ORIG);structnode_info*fromnode=NULL;
...
/*取得要重传的消息包的起始序列号*/if((cfseq=ha_msg_value(msg,F_FIRSTSEQ))==NULL||(clseq=ha_msg_value(msg,F_LASTSEQ))==NULL||(fseq=atoi(cfseq))lseq){/*无效序列号,记录日志信息*/...}
...
/*重传丢失的消息包*/for(thisseq=fseq;thisseqtrack.ackseq){/*该消息包已经被确认过,可以忽略掉*/continue;}if(thisseqlowseq){/*序列号小于消息队列里的最小序列号,该消息己不存在于历史消息队列中*//*告知对方,不重传该消息*/nak_rexmit(hist,thisseq,fromnodename,“seqnotoolow”);continue;}if(thisseq>hist->hiseq){/*序列号大于消息队列中最大序列号*/...continue;}
for(msgslot=firstslot;!foundit&&msgslot!=(firstslot+1);--msgslot){char*smsg;longclock_tnow=time_longclock();longclock_tlast_rexmit;size_tlen;
...
/*重传上一次重传剩下的消息包*/last_rexmit=hist->lastrexmit[msgslot];
if(cmp_longclock(last_rexmit,zero_longclock)!=0&&longclockto_ms(sub_longclock(now,last_rexmit))<(ACCEPT_REXMIT_REQ_MS)){gotoNextReXmit;}
/*一次不能发送太多数据包,如果数据包太多的话,可能会引起串口溢出*/++rexm


Heartbeat裂脑:

什么是裂脑?

由于两台高可用服务器之间在指定的时间内,无法互相检测到对方心跳而各自启动故障转移功能,取得了资源以及服务的所有权,而此时的两台高可用服务器对都还活着并作正常运行,这样就会导致同一个IP湖综合服务在两端同时启动而发生冲突的严重问题,最严重的就是两台主机同时占用一个VIP的地址,当用户写入数据的时候可能会分别写入到两端,这样可能会导致服务器两端的数据不一致或造成数据的丢失,这种情况就本成为裂脑,也有的人称之为分区集群或者大脑垂直分隔。

导致裂脑发生的原因:  

一般来说,裂脑的发生,主要是由以下的几个原因导致的:

1)高可用服务器对之间心跳线路故障,导致无法正常的通信。原因比如:

(1).心跳线本身就坏了(包括断了,老化)

(2).网卡以及相关驱动坏了,IP配置及冲突问题

(3).心跳线间连接的设备故障(交换机的故障或者是网卡的故障)

(4).仲裁的服务器出现问题

2)高可用服务器对上开启了防火墙阻挡了心跳消息的传输

3)高可用服务器对上的心跳网卡地址等信息配置的不正确,导致发送心跳失败。

4)其他服务配置不当等原因,如心跳的方式不同,心跳广播冲突,软件出现了BUG等

防止脑裂发生的方法总结:

发生脑裂的时候,对业务的影响是及其严重的,有的时候甚至是致命的。如:两台高可用的服务器对之间发生脑裂,导致互相竞争同一个IP资源,就如同我们局域网内常见的IP地址冲突一样,两个机器就会有一个或者两个不正常,影响用户正常访问服务器。如果是应用在数据库或者是存储服务这种极重要的高可用上,那就导致用户发布的数据间断的写在两台服务器上的恶果,最终数据恢复及困难或者是难已恢复。

实际的生产环境中,我们可以从以下几个方面来防止裂脑的发生:

1)同时使用串行电缆和以太网电缆连接,同时用两条心跳线路,这样一条线路坏了,另一个线路还是好的,依然能传送消息(推荐)

2)检测到裂脑的时候强行的关闭一个心跳节点(需要特殊的节点支持,如stonith,fence),相当于程序上备节点发现心跳线故障,发送关机命令到主节点。

3)做好对裂脑的监控报警(如邮件以及手机短信等),在问题发生的时候能够人为的介入到仲裁,降低损失。当然,在实施高可用方案的时候,要根据业务的实际需求确定是否能够容忍这样的损失。对于一般的网站业务,这个损失是可控的(公司使用)

4)启用磁盘锁。正在服务一方锁住共享磁盘,脑裂发生的时候,让对方完全抢不走共享的磁盘资源。但使用锁磁盘也会有一个不小的问题,如果占用共享盘的乙方不主动解锁,另一方就永远得不到共享磁盘。现实中介入服务节点突然死机或者崩溃,另一方就永远不可能执行解锁命令。后备节点也就截关不了共享的资源和应用服务。于是有人在HA中涉及了“智能”锁,正在服务的一方只在发现心跳线全部断开时才启用磁盘锁,平时就不上锁了。

5)报警报在服务器接管之前,给人员处理留足够的时间

  就是1分钟内报警了,但是服务器不接管,而是5分钟之后接管,接管的时间较长。

  数据不会丢失,但就是会导致用户无法写数据。

6)报警后,不直接自动服务器接管,而是由人员接管。

7)增加仲裁的机制,确定谁该获得资源,这里面有几个参考的思路:

  1)增加一个仲裁机制。例如设置参考的IP,当心跳完全断开的时候,2个节点各自都ping一下参考的IP,不同则表明断点就出现在本段,这样就主动放弃竞争,让能够ping通参考IP的一端去接管服务。

  2)通过第三方软件仲裁谁该获得资源,这个在阿里有类似的软件应用

HeartBeat的消息类型:

heartBeat高可用软件在工作的过程中,一般来说,有三种消息的类型,具体为:

1)心跳消息

 心跳消息为约150字节的数据包,可能为单播,广播或者多播的方式,控制心跳频率以及出现故障要等待多久进行故障转换。

2)集群转换消息

 当主服务器恢复在线状态时,通过ip-request消息是要求备机释放主服务器失败时被服务器取得的的资源,然后被服务器关闭是仿主服务器失败时取得的资源以及服务。

 备服务器释放主服务器失败时取得的资源以及服务后,就会通过ip-request-resp消息通知主服务器它不在拥有该资源以及服务,主服务器收到来自备节点的ip-request-resp消息通知后,启动失败时释放的资源以及服务,并开始提供正常的访问服务。

3)重传消息请求

 rexmit-request控制重传心跳请求。此消息不太重要,细节就不多介绍了

提示:以上的心跳控制消息都使用的是UDP协议发送到/etc/ha.d/ha.cf文件指定到任意的端口,或者指定到多播地址。


Heartbeat ip地址接管和故障转移:

Heartbeat是通过IP地址接管和ARP广播进行故障转移的。

ARP广播:在主服务器故障的时候,备用节点接管资源后,会强制更新所有的客户端本地的ARP表(即清除客户端本地缓存的失败服务器的VIP地址和mac地址的解析记录)。确保客户端和新的主服务器进行对话。

(这提到的客户端机器是和Heartbeat高可用服务器对在同一个网络中的客户机,并不是最终的互联网用户,这里的客户端及其是相对Heartbeat高可用服务器对说的,这点,请注意)

VIP/IP 别名/辅助IP:

真实IP,又被称为管理IP,一般是配置在物理网卡上的实际IP,这可以看做是你本人的真实姓名,如:张三。在负载均衡以及高可用环境中,管理IP是不会对外提供用户的访问服务的,而是仅作管理服务器使用,如ssh可以通过这个管理IP连接服务器。

VIP是虚拟的IP,只是个概念而已,可能会误导,实际上就是Heartbeat临时绑在物理网卡上的别名IP,如eth0:x,x为0-255的任意数字,可以在一块网卡上绑定多个别名,这样做的好处是当提供服务的服务器宕机之后,在接管的服务器上会直接会自动配置上同样的VIP提供服务。如果使用管理IP的话,来回迁移就难以做到,而且,管理IP迁移过去了我们就不能够登录到这台机器上,这就需要到机房登陆了。VIP的实质就是确保两台服务器有一个管理IP不懂,就是随时可以连上机器,然后,增加绑定其他的VIP,这样就算VIP转移走了,也不至于服务器本身连不上,因为还有管理的IP。


手工配置VIP的方法:

ifconfig eth0:1 124.42.61.109 netmask 255.255.255.224 up(ip alias) –》heartbeat2软件默认是使用这个命令来添加VIP的
ip addr add 10.0.15.1/24 broadcast 10.0.15.255 dev eth1(辅助Ip)–》keepalived以及heartbeat3采用的方案添加VIP的

注意:使用ip addr能够查看到包括别名和辅助IP,用ifconfig无法查到辅助IP的配置情况

手工删除VIP的方法:

ip addr del 10.0.15.1/24 broadcast 10.0.15.255 dev eth1(辅助IP)
ifconfig eth0:1 124.42.61.109 netmask 255.255.255.244 down(ip alias)

HeartBeat配置文件:

 heartbeat的默认配置文件的目录为/etc/ha.d heartbeat的常用配置文件有三个,分别为ha.cf,authkey,haresource.

 ha.cf heartbeat参数配置文件 在这里配置一些基本的参数

 authkey heartbeat认证文件 高可用服务器对之间根据对端的authkey,对对端的进行认证

 haresource heartbeat的资源文件 如配置资源以及一些脚本程序

重要资源目录:/etc/ha.d/resource.d/,如果以后自己开发程序,就放到这个地方即可,然后在haresource文件里直接调用

参考资料

1. heartbeat主页: http://www.linux-ha.org/

2. heartbeat文档: http://www.linux-ha.org/doc/users-guide/users-guide.html

3. heartbeat配置示例: https://github.com/chenzhiwei/configure/tree/master/heartbeat



http://www.virplus.com/thread-1244.htm
转载请注明:2020-3-22 于 VirPlus 发表

推荐阅读
最新回复 (0)

    ( 登录 ) 后,可以发表评论!

    返回