==========
两个问题:
在继续讲keepalived之前让我们先思考两个问题:
1.如果我们只有一台调度器(lvs/Nginx),且它到后端real server集群的物理连接断了(如网卡、光模块、线缆等损坏),那该怎么应对?
2.如果我们只有一台调度器(Nginx等软件实现的),但是不巧的是Nginx进程被杀死了,那该怎么应对?
显然以上都是单点故障,遇到单点故障很棘手,我们应该避免。一般来讲网络中关键节点都应该有至少两个,所以我们的调度器也应该至少两台,组成高可用(HA)集群,如下图:
图中有两台调度器,正常情况下流量仅流经director1,也就是director1 作为MASTER,director2 作为BACKUP。我们期望的是当MASTER的 eth1或 eth3 接口挂掉后,director2 能够承接director1的所有流量。同时,如果direcotr1为Nginx反代(包括7层和伪34层),当Nginx进程挂掉后,也能完成流量切换。如何实现呢?用 keepalived。
在数通网络中,我们通常使用VRRP来做默认网关的冗余,keepalived也是靠实现VRRP协议来完成流量从 director1 到director2 的倒换的。如果不熟悉VRRP,建议去某度文库搜索下数通厂商的vrrp技术白皮书。理解了VRRP的工作原理后再来看 keepalived, so easy。(务必要先搞懂VRRP协议,厂商的vrrp技术白皮书已经写的很详细了,如华为。)
言归正传,使用keepalived,以下3种情况下会触发倒换(也就是图中流量从 director1 切到 director2):
场景描述:图中eth1和eth2起vrrp,且eth1被选举为MASTER(准确地说MASTER和BACKUP都是针对接口说的,不过我们在一定场合下也称路由器的状态为MASTER/BACKUP)
1. director1 的eth1接口down掉,这很容易理解,物理接口down掉了,无法发送vrrp advertisement,作为BACKUP的director2这时候就会接替原来director1的角色,成为MASTER。
2. 我们可以让 keepalived 进程在director1监控eth3接口的状态,如果该接口down掉,则停止在eth1接口上发送vrrp advertisement,把MASTER角色让个director2。这通过keepalived.conf中的track_interface命令配置来实现。
3. 我们可以让 keepalived 执行某脚本,该脚本周期性地检测诸如进程等的设备状态并返回code,keepalived根据脚本返回的code值调整(减小)director1发出的vrrp 通告中的priority至小于director2发出的通告中的priority,把MASTER角色让给director2。这通过keepalived.conf中的track_script命令配置来实现。
好,现在给出几个实例,我们通过如下两个拓扑完成实验验证。
Note:如中rs1,rs2,rs3是3台不同的物理机,只不过图中画在了一起。
实例一:
使用拓扑一,仅仅在 director1 和 director2 的左侧接口起 vrrp,VRIP为192.168.10.131,物理接口分别为 192.168.10.128 和 192.168.10.135。设置 192.168.10.128所在借口有 higher priority,以使之被选举为master,当master接口挂掉后,backup接口开始发送 vrrp advertisement,成为master。director 1 和 director2 的配置分别如下:
补充:组播地址 && VRID 要设置成一样才会被认为是同一个 vrrp_instance
===== director1 配置 =====
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id 6A //设置 router-id
vrrp_mcast_group4 224.0.0.18 //设置组播地址,同一个vrrp_instance中的组播地址一定要相同
}
vrrp_instance OUTSIDE_VI_1 { //创建一个instance,名字随意(本地有效,报文不含之),但是建议主备同名
state MASTER //预设置角色,其实没有大用,鹿死谁手还得比拼priority等
interface eth1 //指定加入instance的接口
virtual_router_id 1 //设置VRID,同一个vrrp_instance中的VRID一定要相同
priority 100 //本机在vrrp_instance中的优先级,准确说应该是接口的优先级
advert_int 1 //通告每隔1s发送一次
authentication { //通告验证使用简单验证,仅vrrp 2.0支持,而keepalived就是实现的vrrp 2.0
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //vrrp的VRIP
}
}
===== director2 配置 =====
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id 6B
vrrp_mcast_group4 224.0.0.18 //同一vrrp_instance的组播地址要一致
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
state BACKUP
interface eth6 //指定加入instance的接口
virtual_router_id 1 //同一vrrp_instance的VRID要一致
priority 99
advert_int 1
authentication {
auth_type PASS
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
192.168.10.131
}
}
配置完成后,重启keepalived。手动关闭 director1 的eth1,ip link set eth1 down,然后在vmnet1 上抓包,发现director2 的eth6 开始发送vrrp advertisement,也就是说director2的eth6已经是MASTER,切换完成。实验完成后再恢复eth1到up状态。
实例二:
使用拓扑一,在实验一的基础上,验证当MASTER监控的某借口down掉后,发生角色切换。也就是验证 track_interface的作用。
===== director1 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
192.168.10.131
}
track_interface { //仅在实验一的基础上添加这个配置段
eth0
}
}
===== director2 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
virtual_ipaddress {
192.168.10.131
}
track_interface { //仅在实验一的基础上添加这个配置段
eth2
}
}
配置完成后,重启keepalived。手动关闭 director1 的eth0,ip link set eth0 down,然后在vmnet1 上抓包,发现director2 的eth6 开始发送vrrp advertisement,也就是说director2的eth6已经是MASTER,切换完成。实验完毕后恢复eth0到up状态。
实例三:
使用拓扑一,在实验一的基础上,验证 track_script 的作用,即周期性地执行脚本,然后根据脚本的返回值调整weight,从而达到MASTER/BACKUP角色切换的目的。
===== director1 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_script my_test { //定义如何追踪脚本,及根据脚本返回代码执行何种动作
script "[ -f /tmp/hehe ]" //检查shell脚本表达式的值,这里双引号内可以是外置脚本(要有exec权限)
interval 1 //多长时间执行一次脚本并检查其返回值
weight -2 //如果脚本执行失败后的动作(weight-2,仅执行一次);如果后续检查发现脚本执行成功则执行反动作(把减去的2加回来)
fall 3 //返回几次非0,才认为失败,执行 weight-2的动作
rise 1 //返回几次0,才认为成功,执行weight-2的反动作,即当前weight+2
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script { //追踪my_test中指明的脚本,my_test要在vrrp_instance前定义好
my_test
}
}
}
===== director2 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_script my_test { //本次实验这一段不加也可,但是建议主备配置一致
script "[ -f /tmp/hehe ]"
interval 1
weight -2
fall 3
rise 1
interval 1
weight -2
fall 3
rise 1
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script { //本次实验这一段不加也可,但是建议主备配置一致
my_test
}
}
配置完成后,现在touch一个/tmp/hehe,重启keepalived,在vmnet1上抓包。这时候由于 [ -f /tmp/hehe ] 返回值为0,也就是 weight 不变化,所以director1还是MASTER。这时候 rm -f /tmp/hehe,当director1 检查脚本表达式时,发现3此都是非0,所以weight-2,由100变成了98,小于director2的priority99,让出了MASTER。观察抓到的报文,发现director2
确实已经再发 vrrp advertisement了。
这里只是拿 [ -f /tmp/hehe ]做个例子而已,生产环境中一般都是检查某个进程是否存活,如 [ killall -0 nginx ] 若 nginx 进程存在,则返回0,weight不变。若进程已死,则weight变化,一般是减小以让出MASTER。 当然,引用外部具有可执行权限的脚本也是ok的。
实例四:
使用如下拓扑二,我们实现一个 lvs-dr的高可用集群(集群这名总是感觉高达上,其实也就是director1和director2组成一个lvs-dr组)
===== director1 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //即做VRIP,也做lvs概念中的VIP
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
delay_loop 6
lb_algo rr
lb_kind DR //定义lvs类型为dr
persistence_timeout 0
protocol TCP
real_server 192.168.10.140 80 {
weight 1
HTTP_GET {
url {
path /
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.10.141 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.10.142 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
TCP_CHECK {
connect_timeout 1
}
}
}
===== director2 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind DR //定义lvs类型为dr
persistence_timeout 0
protocol TCP
real_server 192.168.10.140 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.10.141 80 {
weight 1
TCP_CHECK {
TCP_CHECK {
connect_timeout 1
}
}
}
real_server 192.168.10.142 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
TCP_CHECK {
connect_timeout 1
}
}
}
===== real server 的配置 =====
rs1:启动httpd,通过 ip add add 192.168.10.131 dev lo 给real server添加VIP
rs2:启动httpd,通过 ip add add 192.168.10.131 dev lo 给real server添加VIP
rs3:启动httpd,通过 ip add add 192.168.10.131 dev lo 给real server添加VIP
此时的高可用就体现在 director1 的master接口down掉后的流量切换。
实例五:
使用拓扑一,我们实现一个 lvs-nat 的高可用集群。为了方便理解配置,再贴一遍图,请对照拓扑理解配置。
先思考个问题,使用lvs-nat的话,real server把默认网关指向谁?在只有一台director的时候这个问题是不存在的,但是现在两台director,怎么办?还是用vrrp,把两台director右侧的interface加入另一个vrrp_instance,然后让real server把默认网关只想这个vrrp_instance的VRIP就可以了。这又引入了另一个问题,director上保存着nat表,所以同一条tcp流的上下行数据要经过同一台director(主director,也就是director1)。也就是两个vrrp_instance的master接口要在同一台director上,一个vrrp_instance发生了接口角色切换,另一个vrrp_instance
也要发生切换,如何实现?用vrrp同步组。
===== director1 配置 =====
lobal_defs {
//节约版面,这部分内容请自己补上
}
vrrp_sync_group VG_1 { //定义vrrp同步组,把两个vrrp_instance加入改组。当一个vrrp_instance接口切换时,另一个同步切换
group {
INSIDE_VI_1 //加入同步组的vrrp_instance
OUTSIDE_VI_1
}
}
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
state MASTER
interface eth1
virtual_router_id 1
priority 100 //左侧的vrrp_instance中,通过设置优先级让director1上的接口被选举为master
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //左侧vrrp_instance的VRIP
}
}
vrrp_instance INSIDE_VI_1 {
state MASTER
interface eth0
virtual_router_id 2
state MASTER
interface eth0
virtual_router_id 2
priority 100 //左侧的vrrp_instance中,通过设置优先级让director1上的接口被选举为master
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.20.135 //右侧vrrp_instance的VRIP
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind NAT //实现lvs-nat
persistence_timeout 0 # during which time, request is distributed to the same real server
protocol TCP
real_server 192.168.20.130 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
delay_loop 6
lb_algo rr
lb_kind NAT //实现lvs-nat
persistence_timeout 0 # during which time, request is distributed to the same real server
protocol TCP
real_server 192.168.20.130 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.20.131 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.20.132 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.20.132 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
}
===== director2 配置 =====
lobal_defs {
//节约版面,这部分内容请自己补上
}
vrrp_sync_group VG_1 {
group {
INSIDE_VI_1
OUTSIDE_VI_1
}
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
group {
INSIDE_VI_1
OUTSIDE_VI_1
}
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99 //director2作为backup
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131 //左侧vrrp_instance的VRIP
}
}
vrrp_instance INSIDE_VI_1 {
state BACKUP
interface eth2
virtual_router_id 2
state BACKUP
interface eth2
virtual_router_id 2
priority 99 //director2作为backup
advert_int 1
authentication {
authentication {
auth_type PASS
auth_pass 1111
}
}
virtual_ipaddress {
192.168.20.135 //右侧vrrp_instance的VRIP
}
}
virtual_server 192.168.10.131 80 {
delay_loop 6
lb_algo rr
lb_kind NAT
persistence_timeout 0 #dechao: during which time the request is distributed to same real server
protocol TCP
real_server 192.168.20.130 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.20.131 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.20.132 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
nb_get_retry 3
delay_before_retry 1
connect_timeout 1
}
}
real_server 192.168.20.131 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
real_server 192.168.20.132 80 {
weight 1
TCP_CHECK {
connect_timeout 1
}
}
}
2台director 上 echo 1 > /proc/sys/net/ipv4/ip_forward 设置 ip_forward=1
3台real server上添加默认路由 ip route add default via 192.168.20.135,同时启动httpd,建议给出不同页面。这时候通过client访问 192.168.10.131,请求就可以被轮询调度到后端不同的real server上了。
这个实例的高可用体现在vrrp的同步倒换上,我们手动关闭director1上右侧端口,发现右侧的vrrp_instance角色切换,同时左侧vrrp_instance的角色也发生切换。但是启用vrrp同步后,发现和track_script冲突,track_script不管用了。通过日志可以看出来,文章最后会讲到keepalived日志相关的东西。
实例六:
使用如下拓扑,我们实现一个Nginx的调度器高可用集群。director1平时作为master,当他上面的Nginx挂掉后,让出master角色给director2,同时执行一些动作,如发邮件给管理员。
说明:由于Nginx作反代调度器的时候,无论伪34层还是7层都可以认为是full-nat,也就是SNAT+DNAT,所以两台director的右侧接口没有起vrrp。
===== director1 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_script my_test {
script "killall -0 nginx"
interval 1
weight -2
fall 3
rise 1
script "killall -0 nginx"
interval 1
weight -2
fall 3
rise 1
}
vrrp_instance OUTSIDE_VI_1 {
state MASTER
interface eth1
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script {
my_test
}
notify_master "/tmp/2master.sh"
virtual_router_id 1
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script {
my_test
}
notify_master "/tmp/2master.sh"
notify_backup "/tmp/2backup.sh"
}
===== director2 配置 =====
global_defs {
//节约版面,这部分内容请自己补上
}
vrrp_script my_test {
script "killall -0 nginx"
interval 1
weight -2
fall 3
rise 1
}
vrrp_instance OUTSIDE_VI_1 {
state BACKUP
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
interface eth6
virtual_router_id 1
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.131
}
track_script {
my_test
}
notify_master "/tmp/2master.sh"
notify_backup "/tmp/2backup.sh"
}
director1 和 director2 上都运行Nginx,作为7层代理或者伪34层代理。此时director1被选举为master。
/tmp/2master.sh 和 /tmp/2backup.sh 要有执行权限,在本实验中脚本内容如下:
/tmp/2master.sh 内容:
#!/bin/bash
echo [email protected]`date` >> /tmp/keepalived.state
/tmp/2backup.sh 内容:
#!/bin/bash
echo [email protected]`date` >> /tmp/keepalived.state
这里只是举一个简单的例子,角色转换时执行的脚本可以是任意的。
现在我们手动停掉director1上的Nginx,这时候director1的vrrp weight由100-2变为98,让出了master,我们可以抓包看到此时director2开始发送vrrp advertisement。同时可以 cat /tmp/keepalived.state ,发现确实角色发生了转化。
===============
keepalived日志相关:
CentOS6上keepalived的日志默认记录在/var/log/messages里面,不好看,通过如下方式定向到一个文件。
/etc/sysconfig/keepalived
编辑:KEEPALIVED_OPTIONS="-D -d -S 3"
/etc/rsyslog.conf
编辑:local3.* /var/log/keepalived.log
service rsyslog restart
service keepalived restart
Oh,my god。总算说完了。