Keepalived的作用是检测web服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web服务器Layer3,4&7工作在IP/TCP协议栈的IP层,TCP层,及应用层,原理分别如下:

Layer3:Keepalived使用Layer3的方式工作式时,Keepalived会定期向服务器群中的服务器发送一个ICMP的数据包(既我们平时用的Ping程序),如果发现某台服务的IP地址没有激活,Keepalived便报告这台服务器失效,并将它从服务器群中剔除,这种情况的典型例子是某台服务器被非法关机。Layer3的方式是以服务器的IP地址是否有效作为服务器工作正常与否的标准。

Layer4:如果您理解了Layer3的方式,Layer4就容易了。Layer4主要以TCP端口的状态来决定服务器工作正常与否。如web server的服务端口一般是80,如果Keepalived检测到80端口没有启动,则Keepalived将把这台服务器从服务器群中剔除。

Layer7:Layer7就是工作在具体的应用层了,比Layer3,Layer4要复杂一点,在网络上占用的带宽也要大一些。Keepalived将根据用户的设定检查服务器程序的运行是否正常,如果与用户的设定不相符,则Keepalived将把服务器从服务器群中剔除。

主要用作RealServer的健康状态检查以及LoadBalance主机和BackUP主机之间failover的实现。

高可用web架构: LVS+keepalived+nginx+apache+php+eaccelerator(+nfs可选 可不选)

   keepalived的安装

使用源码先安装keepalived 

cd /usr/local/

wget  http://www.keepalived.org/software/keepalived-1.2.6.tar.gz

tar zxf keepalived-1.2.6.tar.gz 

cd keepalived-1.2.6

./configure --prefix=/usr/local/keepalived 

make

make install

建立服务启动脚本,以便使用service命令控制之 
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/keepalived

chmod +x /etc/init.d/keepalived

因为我们使用非默认路径(/usr/local)安装keepalived, 故需要修改几处路径,以保证keepalived能正常启动, 需要修改的文件如下:

2. 修改/etc/init.d/keepalived, 寻找大约15行左右的. /etc/sysconfig/keepalived, 修改为: 
.  /usr/local/keepalived/etc/sysconfig/keepalived, 即指向正确的文件位置

同时在上述行下添加以下内容(将keepavlied主程序所在路径导入到环境变量PATH中):

PATH="$PATH:/usr/local/keepalived/sbin"

export PATH

3. 修改/usr/local/keepalived/etc/sysconfig/keepalived文件,设置正确的服务启动参数 
KEEPALIVED_OPTIONS="-D -f /usr/local/keepalived/etc/keepalived/keepalived.conf"

4. 经过以上修改,keepalived基本安装即可完成,启动测试之: 
service keepalived restart

5. 切勿忘记将此服务设置为开机启动

chkconfig keepalived on

默认的配置文件中,指定了两个数个虚拟IP : 192.168.200.16  192.168.200.17  192.168.200.18

可使用ip addr命令验证之。

以上实验只需要一台主机,因为当前节点被指定为主节点,且没有收到其它节点的VRRP组播信息,故自动绑定了虚拟IP。

 

在这种模式下,虚拟IP在某时刻只能属于某一个节点,另一个节点作为备用节点存在。当主节点不可用时,备用节点接管虚拟IP,提供正常服务。

节点A 192.168.0.11 (主节点), 节点B 192.168.0.12(备用节点)  虚拟IP(对外提供服务的IP 192.168.0.200 

要求默认情况下由节点A提供服务,当节点A不可用时,由节点B提供服务(即虚拟IP漂移至节点B)。

节点A上的配置文件/usr/local/keepalived/etc/keepalived/keepalived.conf

global_defs {

   notification_email {

     root@localhost

   }

   notification_email_from root@local host

   smtp_server localhost

   smtp_connect_timeout 30

   router_id  NodeA

}

默认的配置文件中,使用第三方smtp服务器,但这在现实中几乎没有意义(需要验证的原因),我们将其指定为localhost, 将通知信息的发送交给本地sendmail服务处理。查阅说明文档得知route_id配置是为了标识当前节点,我将其设置为NodeA。当然两个节点的此项设置可相同,也可不相同。

vrrp_instance VI_1 {

    state MASTER   #指定A节点为主节点 备用节点上设置为BACKUP即可

    interface eth0   #绑定虚拟IP的网络接口

    virtual_router_id 100  #VRRP组名,两个节点的设置必须一样,以指明各个节点属于同一VRRP组(注意各节点要使用不同的id)

    priority 100   #主节点的优先级(1-254之间),备用节点必须比主节点优先级低

    advert_int 1   #组播信息发送间隔,两个节点设置必须一样

    authentication {   #设置验证信息,两个节点必须一致

        auth_type PASS

        auth_pass 1111

    }

    virtual_ipaddress {   #指定虚拟IP, 两个节点设置必须一样

     172.16.0.0/16

    }

}

默认的配置文件中,竟然没有子网掩码,从而导致使用了默认子网掩码255.255.255.255,如果导致无法从其它机器访问虚拟IP(keepalived虚拟IP无法ping通)。

按同样的方法配置节点B并修改配置文件,可将A节点的配置文件复制到B节点,并修改以下几项: 
router_id  NodeB

state   BACKUP

priority   99

其它项不必修改。

测试及验证:

这时就需要设置仲裁,即每个节点必须判断自身的状态(应用服务状态及自身网络状态),要实现这两点可使用自定义shell脚本实现,通过周期性地检查自身应用服务状态,并不断ping网关(或其它可靠的参考IP)均可。当自身服务异常、或无法ping通网关,则认为自身出现故障,就应该移除掉虚拟IP(停止keepalived服务即可)。主要借助keepalived提供的vrrp_script(要另起一行写,单独写)

及track_script(要在vrrp_server里面调用)实现:

在keepalived的配置文件最前面加入以下代码,定义一个跟踪脚本: 
vrrp_script check_local { #定义一个名称为check_local的检查脚本

    script "/usr/local/keepalived/bin/check_local.sh" #shell脚本的路径

    interval 5  #运行间隔

}

再在vrrp_instance配置中加入以下代码使用上面定义的检测脚本:

track_script {

check_local

}

我们在/usr/local/keepalived/bin/check_local.sh定义的检测规则是:

1.  自身web服务故障(超时,http返回状态不是200)

2.  无法ping通网关

3.  产生以上任何一个问题,均应该移除本机的虚拟IP(停止keepalived实例即可)

但这里有个小问题,如果本机或是网关偶尔出现一次故障,那么我们不能认为是服务故障。更好的做法是如果连续N次检测本机服务不正常或连接N次无法ping通网关,才认为是故障产生,才需要进行故障转移。另一方面,如果脚本检测到故障产生,并停止掉了keepalived服务,那么当故障恢复后,keepalived是无法自动恢复的。我觉得利用独立的脚本以秒级的间隔检查自身服务及网关连接性,再根据故障情况控制keepalived的运行或是停止。

这里提供一个思路,具体脚本内容请大家根据自己的需要编写即可。

 当我们要定义httpd的高可用是server1:

    vrrp_script chk_http_port { 
                script "killall -0 httpd"         ###监监控httpd是否在线
                interval 2                             ###监控时间 
                weight 2                                ###如果没哟检测到httpd开启,就修改权重

vrrp_instance VI_1 { 
        state MASTER                            ### 设置为 主 
        interface eth0                             ### 监控网卡    
        virtual_router_id 51                    ### 这个两台服务器必须一样 
        priority 100                               ### 权重值 MASTRE 一定要高于 BAUCKUP 
        authentication { 
                     auth_type PASS             ### 加密 
                     auth_pass eric                ### 加密的密码,两台服务器一定要一样,不然会出错 
        } 
        track_script { 
                chk_http_port                     ### 调用上面的定义的监控 
        } 
        virtual_ipaddress { 
             172.16.38.1                            ###    VIP 地址 
        } 

在server上定义:

     vrrp_script chk_http_port { 
                script "killall -0 httpd" 
                interval 2 
                weight 2 

vrrp_instance VI_1 { 
        state BACKUP                                ### 设置为 辅机 
        interface eth0 
        virtual_router_id 51                        ### 与 MASTRE 设置 值一样 
        priority 99                                    ### 比 MASTRE权重值 低 
        authentication { 
                     auth_type PASS 
                     auth_pass eric                    ### 密码 与 MASTRE 一样 
        } 
        track_script { 
                chk_http_port 
        } 
        virtual_ipaddress { 
                172.16.38.1
        } 
}

   server1

 这样我们就可以测试了,启用service keepalived start,发现在主节点上就启用了172.16.38.1这个ip,(使用ip addr show 查看),

 2:ss -untlp  就可以发现http的80端口已经起用了

      在你的浏览器上在去试验,输入172.16.38.1,就可以测试了,

  然后在server1把keeplived关闭,

      server2:

 

 这样我们就可以测试了,启用service keepalived start,发现在主节点上就启用了172.16.38.1这个ip,(使用ip addr show 查看),

 2:ss -untlp  就可以发现http的80端口已经起用了

      在你的浏览器上在去试验,输入172.16.38.1,就可以测试了,

  然后在server1把keeplived关闭,

可是当我们把httpd的进程killall掉时,发现资源没有被server1启用,这样我们就需要自己写一个脚本来监控了:

   我们在/etc/keepalived上定义一个脚本:

   做nginx的高可用

  

配置keepalived为实现nginx高可用的配置文件示例:


! Configuration File for keepalived  

  

global_defs {  

   notification_email {  

         linuxedu@foxmail.com

         mageedu@126.com  

   }  

   notification_email_from kanotify@magedu.com 

   smtp_connect_timeout 3  

   smtp_server 127.0.0.1  

   router_id LVS_DEVEL  

}  


vrrp_script chk_haproxy {  

    script "killall -0 nginx"  

    interval 1  

    weight 2  

}  


vrrp_script chk_mantaince_down {

   script "[[ -f /etc/keepalived/down ]] && exit 1 || exit 0"

   interval 1

   weight -2

}


vrrp_instance VI_1 {  

    interface eth0  

    state MASTER  # BACKUP for slave routers

    priority 101  # 100 for BACKUP

    virtual_router_id 51 

    garp_master_delay 1 

  

    authentication {  

        auth_type PASS  

        auth_pass password  

    }  

    track_interface {  

       eth0    

    }  

    virtual_ipaddress {  

        172.16.100.1/16 dev eth0 label eth0:0 

    }  

    track_script {  

        chk_haproxy  

        chk_mantaince_down

    }  

  

 

    notify_master "/etc/keepalived/notify.sh master"  

    notify_backup "/etc/keepalived/notify.sh backup"  

    notify_fault "/etc/keepalived/notify.sh fault"  



注意:

1、上面的state为当前节点的起始状态,通常在master/slave的双节点模型中,其一个默认为MASTER,而别一个默认为BACKUP。

2、priority为当关节点在当前虚拟路由器中的优先级,master的优先级应该大于slave的;



下面是一个notify.sh脚本的简单示例:

#!/bin/bash

# Author: MageEdu <linuxedu@foxmail.com>

# description: An example of notify script


vip=172.16.100.1

contact='root@localhost'


notify() {

    mailsubject="`hostname` to be $1: $vip floating"

    mailbody="`date '+%F %H:%M:%S'`: vrrp transition, `hostname` changed to be $1"

    echo $mailbody | mail -s "$mailsubject" $contact

}


case "$1" in

    master)

        notify master

        /etc/rc.d/init.d/nginx start

        exit 0

    ;;

    backup)

        notify backup

        /etc/rc.d/init.d/nginx stop

        exit 0

    ;;

    fault)

        notify fault

        /etc/rc.d/init.d/nginx stop

        exit 0

    ;;

    *)

        echo 'Usage: `basename $0` {master|backup|fault}'

        exit 1

    ;;

esac