Keepalived和LVS

安装

yum install keepalived ipvsadm psmisc

防火墙

firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" destination address="224.0.0.18" protocol value="vrrp" accept' --permanent
firewall-cmd --reload

LVS相关术语:

1. DS:Director Server。指的是前端负载均衡器节点。
2. RS:Real Server。后端真实的工作服务器。
3. VIP:向外部直接面向用户请求,作为用户请求的目标的IP地址。
4. DIP:Director Server IP,主要用于和内部主机通讯的IP地址。
5. RIP:Real Server IP,后端服务器的IP地址。
6. CIP:Client IP,访问客户端的IP地址。

LVS集群方式:

lvs-nat:修改请求报文的目标IP;多目标IP的DNAT
lvs-dr:操纵封装新的MAC地址
lvs-tun:在原请求IP报文之外新加一个IP首部
lvs-fullnat:修改请求报文的源和目标IP

IPVS的调度算法

根据其调度时是否考虑各RS当前的负载状态,可分为静态方法和动态方法两种:

静态方法:仅根据算法本身进行调度

    RR:roundrobin,轮询
    WRR:Weighted RR,加权轮询
    SH:Source Hashing,实现session sticy,源IP地址hash;将来自于同一个IP地址的请求始终发往第一次挑中的RS,从而实现会话绑定
    DH:Destination Hashing;目标地址哈希,将发往同一个目标地址的请求始终转发至第一次挑中的RS,典型使用场景是正向代理缓存场景中的负载均衡         

动态方法:主要根据每个RS当前的负载状态及调度算法进行调度

    LC:least connections 最小连接调度
    Overhead=activeconns*256+inactiveconns

    WLC:Weighted LC 加权最小连接调度
    Overhead=(activeconns*256+inactiveconns)/weight

    SED:Shortest Expection Delay
    Overhead=(activeconns+1)*256/weight

    NQ:Never Queue    

    LBLC:Locality-Based LC,动态的DH算法;基于局部性的最少链接

    LBLCR:LBLC with Replication,带复制功能的LBLC,带复制的基于局部性最少链接

lvs-dr说明

    Direct Routing,直接路由,通过为请求报文重新封装一个MAC首部进行转发,源MAC是DIP所在的接口的MAC,目标MAC是某挑选出的RS的RIP所在接口的MAC地址;源IP/PORT,以及目标IP/PORT均保持不变。  
    Director和各RS都得配置使用VIP; 
    (1) 确保前端路由器将目标IP为VIP的请求报文发往Director;
    (a) 在前端网关做静态绑定;
    (b) 在RS上使用arptables;
    (c) 在RS上修改内核参数以限制arp通告及应答级别;
    arp_announce
    arp_ignore
    (2) RS的RIP可以使用私网地址,也可以是公网地址;RIP与DIP在同一IP网络;RIP的网关不能指向DIP,以确保响应报文不会经由Director; 
    (3) RS跟Director要在同一个物理网络;
    (4) 请求报文要经由Director,但响应不能经由Director,而是由RS直接发往Client;
    (5) 不支持端口映射。


ip地址配置示例:

Director:      eht0    --DIP:  192.168.1.101
                eth0:0  --VIP:192.168.1.100

Real Server1:   eth0    --RIP:  192.168.1.102
                lo:0    --VIP:  192.168.1.100

Real Server2:   eth0    --RIP:  192.168.1.103
                lo:0    --VIP: 192.168.1.100

需要解决两个问题:

    1) Real Server上的VIP对外不可见:

        即对网络内的VIP的ARP请求,Real Server不响应,只有Director响应,这样Client的请求就能首先路由到Director,再由Director转发到Real Server;

        echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
        echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
        echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
        echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

        说明:

        arp_ignore和arp_announce 默认值都为0

        arp_ignore: 定义接收到ARP请求时的响应级别;

            0:只要本地配置的有相应地址,就给予响应;默认;

            1:仅在请求的目标地址配置在到达的接口上的时候,才给予响应;

        arp_announce:定义将自己地址向外通告时的通告级别;

            0:将本地任何接口上的任何地址向外通告;默认;

            1:试图仅向目标网络通告与其网络匹配的地址;

            2:仅向与本地接口上地址匹配的网络进行通告;


    2) Real Server直接返回Client

         Director转发client的请求到Real Server后,Real Server通过eth0--RIP接收处理,而响应回去的源地址需要为VIP,即通过lo:0--VIP接口返回,而不是RIP接口;

         route add -host 192.168.1.100 dev lo:0

         注意:假如VIP与RIP不在同一网段(RIP为私有网络IP),Real Server转发Client的需要经由另外一个私有网关接口出去;


Director 和 real server 配置的虚拟VIP的广播地址必须相同
    ifconfig eth0:0 192.168.1.100 broadcast 192.168.1.100 netmask 255.255.255.255 up
## Real Server服务器的lvs_real_dr.sh脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#!/bin/bash
#chkconfig: 2345 79 20
#description:realserver

#定义虚拟IP变量
Vip=192.168.1.100

source /etc/rc.d/init.d/functions
case "$1" in

start)
echo "Start Real Server..."
# 在Real服务器上基于回环虚拟网卡lo建立一个虚拟网络接口
/sbin/ifconfig lo:1 $Vip broadcast $Vip netmask 255.255.255.255 up
# 将虚拟地址添加到路由表
/sbin/route add -host $Vip dev lo:1
# 定义对目标地址为本地IP的ARP询问的应答模式(1表示只应答目标Ip是接收请求的网络接口(非网卡,双接口可共用一个网卡)本地地址的ARP查询请求)
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
# 控制系统在对外发送arp请求时,如何选择arp请求数据包的源IP地址(2表示忽略Ip数据包源地址,选择可能接收到该ARP回应的网络接口来进行发送)
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
;;

stop)
echo "Close Real Server..."
# 关闭这个网卡
ifconfig lo:1 down
# 恢复arp_ignore和arp_announce的值
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
;;

*)
echo "Usage: "$1" {start|stop}"
;;

esac
exit 0
## 调度服务器的lvs_serv_dr.sh脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#! /bin/bash

#定义虚拟IP和实服务IP变量
Vip=192.168.1.100
Rs1=192.168.1.102
Rs2=192.168.1.103

source /etc/rc.d/init.d/functions
case "$1" in
start)
echo "Start LVS of Server..."
# 在Director服务器上基于物理网卡eth0建立一个虚拟网络接口(新增一个子网卡,设置它的虚拟IP和广播地址以及子网掩码并启动)
/sbin/ifconfig eth0:1 $Vip broadcast $Vip netmask 255.255.255.255 up
# 打开Director服务器上开启路由转发功能(多网卡下的网卡间数据包转发)
#echo 1 > /proc/sys/net/ipv4/ip_forward
# 将虚拟地址添加到路由表
/sbin/route add -host $Vip dev eth0:1
# 清除所有的虚拟服务器记录
/sbin/ipvsadm -C
# 在内核虚拟服务器表中添加一台虚拟服务器(指定为tcp协议,指定虚拟ip和prot,指定调度算法)
/sbin/ipvsadm -A -t $Vip:6500 -s rr # rr 表示轮询调度
# 在一台虚拟服务器中增加一台新的真实服务器(执行虚拟服务对应真实服务的关系,指定负载均衡模式)
/sbin/ipvsadm -a -t $Vip:6500 -r $Rs1:6500 -g # -g 表示DR模式
/sbin/ipvsadm -a -t $Vip:6500 -r $Rs2:6500 -g
# 启动LVS
/sbin/ipvsadm
;;

stop)
echo "Close LVS of Server..."
# 清除所有的虚拟服务器记录
/sbin/ipvsadm -C
# 关闭这个子网卡
/sbin/ifconfig eth0:1 down
;;

*)
echo "Usage: $0 {start|stop}"
;;

esac
exit 0

lvs-nat说明

NAT方式配置

这种方式需要Director和各Real Server在同一IP网络内(172.16.100.0/24),具体配置如下示例:

    Director:          eht0    --VIP:192.168.0.146     gateway:局域网网关(路由器地址)
                        eth1    --DIP:172.16.100.10     gateway:为空,不需要填

    Real Server1(RS1):  eth0    --RIP: 172.16.100.2      gateway:172.16.100.10

    Real Server2(RS2):  eth0    --RIP: 172.16.100.3      gateway:172.16.100.10


配置director打开网卡间的转发功能,echo '1' >/proc/sys/net/ipv4/ip_forward
## LVS - 地址转换(NAT)模式示例,Director节点的lvs_serv_nat.sh脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

#!/bin/bash

# 配置实服务主机IP,调度器虚拟IP(调度器节点需要双网卡,对外地址为VIP,内网地址需要设置为RS网关)
Vip=192.168.1.100
Rs1=192.168.1.101
Rs2=192.168.1.102

source /etc/rc.d/init.d/functions

case "$1" in

start)

echo "Start LVS of Server..."

# 打开Director服务器上开启路由转发功能(多网卡下的网卡间数据包转发)
echo 1 > /proc/sys/net/ipv4/ip_forward

# 清空防火墙nat表的所有链
#iptables -t nat -F
# 删除防火墙nat自定义链
#iptables -t nat -X

# 新增一个子网卡
/sbin/ifconfig eth0:0 $Vip netmask 255.255.240.0 up
# 设置Director的ipvs
/sbin/ipvsadm -C
# 在内核虚拟服务器表中添加一台虚拟服务器
/sbin/ipvsadm -A -t $Vip:6500 -s rr # rr 表示轮询调度
# 在一台虚拟服务器中增加一台新的真实服务器(指定虚拟服务对应真实服务的关系,指定负载均衡模式)
/sbin/ipvsadm -a -t $Vip:6500 -r $Rs1:6500 -m # -m 表示NAT模式
/sbin/ipvsadm -a -t $Vip:6500 -r $Rs2:6500 -m
# 启动LVS
/sbin/ipvsadm
;;
stop)

echo "Close LVS of Server..."
echo "0" >/proc/sys/net/ipv4/ip_forward
/sbin/ipvsadm -C
/sbin/ifconfig eth0:0 down
;;

*)
echo "Usage: $0 {start|stop}"
;;

esac
exit 0

修改系统参数

echo 'net.ipv4.ip_nonlocal_bind = 1' >> /etc/sysctl.conf
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf

sysctl -p

架构

"keepalived_lvs"

IP分配:

VIP1:172.18.67.66
VIP2:172.18.67.88
DIP1:172.18.67.13
DIP2:172.18.67.14
RIP1:172.18.67.11
RIP2:172.18.67.12
CIP:172.18.67.3

keepalived

## 主备不同之处
    1,router_id        不能一致
    2,state            MASTER/BACKUP
    3, priority         权重不能一致
    4, interface ens33  网络接口注意和本机对应

keepalived master

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
IT@service.com
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id lvs01
vrrp_garp_master_refresh 60
vrrp_garp_master_delay 5

vrrp_mcast_group4 224.0.0.18
}

vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 50
priority 100
advert_int 1
#garp_master_delay 10
#garp_master_refresh 60
#nopreempt
smtp_alert

authentication {
auth_type PASS
auth_pass DFwx4893Gh60
}

virtual_ipaddress {
## <IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPE> label <LABEL>
172.18.67.66/24 dev ens33 label ens33:1
}

## mcast_src_ip <IPADDR>
#unicast_src_ip 172.18.67.13
#unicast_peer {
# 172.18.67.14
#}

track_interface {
ens33
}


## 可以不用执行脚本,除非需要执行某些操作。keepalived 1.5 + postfix 能够发送邮件。

#notify_master "/etc/keepalived/notify.sh master"
#notify_backup "/etc/keepalived/notify.sh backup"
#notify_fault "/etc/keepalived/notify.sh fault"

}

## 设置vip地址和端口,DR架构WEB端口要和虚拟端口监听一致。否则将无法访问
virtual_server 172.18.67.66 80 {

delay_loop 6 ## service polling的delay时间,即服务轮询的时间间隔,单位为妙
lb_algo rr ## 调度策略,支持rr|wrr|lc|wlc|lblc|sh|dh
lb_kind DR ## LVS集群模式:NAT|DR|TUN
persistence_timeout 0 ## 会话保持时间(持久连接,秒),同一IP在这段时间内的请求都发到同个real server,如果是mysql之类的长连接可以设置长点,比如7200
protocol TCP ## 使用TCP协议,keepalived只支持TCP协议

#net_mask 255.255.255.0
sorry_server 127.0.0.1 80 ## 所有realserver都down机,指定的备用服务器

## 后端真实WEB服务器地址与端口
real_server 172.18.67.11 80 {
weight 1    ## 调度权重

#inhibit_on_failure ##表示在节点失败后,把他权重设置成0,而不是冲IPVS中删除
#notify_up <STRING> | <QUOTED-STRING> ##检查服务器正常(UP)后,要执行的脚本
#notify_down <STRING> | <QUOTED-STRING> ##检查服务器失败(down)后,要执行的脚本
#uthreshold <INTEGER> ## maximum number of connections to server
#lthreshold <INTEGER> ## minimum number of connections to server

## 健康检查方式,一共HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK
## HTTP_GET检查url,SSL_GET检查https url,TCP_CHECK检查tcp连接,可用在mysql检查
HTTP_GET {

## 要检查的URL,可以有多个
url {
path /
status_code 200 ## 返回成功状态码,也可以用另外一个选项配置返回字符串
}

connect_timeout 2 ## 连接超时时间
nb_get_retry 3 ## 重连次数
delay_before_retry 3 ## 重连间隔
}

## 通过检查TCP端口来检测
#TCP_CHECK {
# connect_timeout 10 ##连接超时时间
# nb_get_retry 3
# delay_before_retry 3
# connect_port 80 ## 连接端口为80,要和上面的保持一致
#}
}

real_server 172.18.67.12 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
connect_timeout 2
nb_get_retry 3
delay_before_retry 3
}
}

}

##virtual_server 172.18.67.33 80 {
##....
##}


EOF

keepalived backup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
IT@service.com
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id lvs02
vrrp_garp_master_refresh 60
vrrp_garp_master_delay 5

vrrp_mcast_group4 224.0.0.18
}

vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 50
priority 90
advert_int 1
#garp_master_delay 10
#garp_master_refresh 60
#nopreempt
smtp_alert

authentication {
auth_type PASS
auth_pass DFwx4893Gh60
}

virtual_ipaddress {
## <IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPE> label <LABEL>
172.18.67.66/24 dev ens33 label ens33:1
}

## mcast_src_ip <IPADDR>
#unicast_src_ip 172.18.67.13
#unicast_peer {
# 172.18.67.14
#}

track_interface {
ens33
}

#notify_master "/etc/keepalived/notify.sh master"
#notify_backup "/etc/keepalived/notify.sh backup"
#notify_fault "/etc/keepalived/notify.sh fault"

}

virtual_server 172.18.67.66 80 {

delay_loop 6
lb_algo rr
lb_kind DR
persistence_timeout 0
protocol TCP

#net_mask 255.255.255.0
sorry_server 127.0.0.1 80

real_server 172.18.67.11 80 {
weight 1
HTTP_GET {

url {
path /
status_code 200
}

connect_timeout 2
nb_get_retry 3
delay_before_retry 3
}
}

real_server 172.18.67.12 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
connect_timeout 2
nb_get_retry 3
delay_before_retry 3
}
}

}

EOF

http 检测

## 健康检查方式,一共HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK
## HTTP_GET检查url,SSL_GET检查https url,TCP_CHECK检查tcp连接,可用在mysql检查
HTTP_GET {

    ## 要检查的URL,可以有多个
    url {
        path /
        ## 返回成功状态码,也可以用另外一个选项配置返回字符串
        status_code 200
        ## 用keepalived自带的genhash生成,/usr/bin/genhash -s rsIP -p port -u uri
        #digest 1362a91278f0806aa1d33e1e26d67763
    }

    connect_timeout 2           ## 连接超时时间
    nb_get_retry 3              ## 重连次数
    delay_before_retry 3        ## 重连间隔
}

tcp 检测

TCP_CHECK {

    connect_timeout 10
    nb_get_retry 3
    delay_before_retry 3

    #检测的端口为80
    connect_port 80

}

MISC_CHECK

MISC_CHECK {
    misc_path "/etc/keepalived/misc_check.sh http://192.168.2.222:6500/check/200.jsp"    # 外部程序或者脚本的路径和参数
    misc_timeout 10   # 脚本执行的超时时间
    misc_dynamic    #动态权重标志。脚本返回0则检测成功权重不变,返回1表示失败权重设置为0
}

misc_check.sh脚本示例:

#!/bin/bash
# ./misc_check.sh http://192.168.2.222:6500/check/200.jsp

if [ $# -ne 1 ]; then
    echo "Warning: command param error."
    exit 1
else
    CHECK_URL=$1
    CMD=`/usr/bin/curl -I ${CHECK_URL} 2>/dev/null | grep "200 OK" | wc -l`

    if [ ${CMD} -eq 1 ]; then
            echo "Succ: Check proxy ${CHECK_URL} is succeed."
        exit 0

    else
        echo "Fail: check proxy ${CHECK_URL} is failed."
        exit 1
    fi
fi

lvs 进程检查脚本

1
2
3
4
5
6
7
8

cat > /etc/keepalived/chk_lvs.sh << EOF
#!/bin/bash
[[ \$(ps -C 'lvs' --no-heading -o stat,ppid,pid,cmd | grep -ve '^[Zz]' | grep -iv 'grep' | wc -l) -ge 2 ]] && exit 0 || exit 1

EOF

chmod u+x /etc/keepalived/chk_lvs.sh

维护脚本

1
2
3
4
5
6
7
8

cat > /etc/keepalived/chk_down.sh << EOF
#!/bin/bash
[[ -f /etc/keepalived/down ]] && exit 1 || exit 0

EOF

chmod u+x /etc/keepalived/chk_down.sh

ipvs 超时和监控

ipvs默认超时时间

## 查看默认超时时间
ipvsadm -L --timeout
Timeout (tcp tcpfin udp): 900 120 300

## 调整默认超时时间
# ipvsadm --set 1 2 1

监测连接保持状态(2s刷新一次状态)

watch ipvsadm -L -n -c