先申明,本文现在已经在我公司的测试环境和生产测试环境使用。正式环境请用keepalived+lvs.
安装ipvsadm不多说了,先说说脚本的功能,脚本分为redirect server 端和realserver 端,脚本分别为 lvs_redirector.sh 和realserver.sh脚本。另外加一个监控脚本lvs_monitor.sh(此脚本来源网友,做了一点修改,算是 取之于网络,共享给网络吧)。
执行 lvs_redirector.sh nat|dr|tun|stop,中的一个选项可以启动或关闭相应的lvs模式,并调用lvs_monitor.sh 监控realserver。当realserver故障,或者重新启动时,自动删除,添加相应的realserver.当realserver 全故障时,自动添加redirector server 本地127.0.0.1的web页面的故障提示。当realserver只要有一台恢复时,自动添加相应的realserver,并删除127.0.0.1。
Lvs具体原理可以看我的博客:Lvs通俗易懂的总结(http://qiaomiao.blog.51cto.com/484197/1729587) 。
本文脚本的使用如下图的场景:
lvs_redirector.sh 脚本如下:
#!/bin/bash
# blog:http://qiaomiao.blog.51cto.com
#
# 20151223 v2.8 20160115
# home:http://www.qmlab.cn
#
# description:this script is use start lvs (nat/dr/tun),and stop lvs.
#define the variable
NAT_VIP="172.16.8.11"
DR_VIP="10.0.8.20"
TUN_VIP="10.0.8.20"
RIP1="10.0.8.21"
RIP2="10.0.8.22"
DPORT=80
RPORT1=80
RPORT2=80
GATEWAY01=172.16.8.254 #做nat模式时,redirector 服务器的外网卡的路由器网关地址。
GATEWAY02=10.0.8.254 #路由器网关地址,做dr 和 tun 模式时用到。
#两个GATEWAY,请根据具体网络环境做取舍。
logfile=/tmp/myself_log/lvs.log
#create log dir
mkdir -p /tmp/myself_log # 在这个目录可以看到启动lvs日志。
#general funiction
lvs_monitor(){
if [ $1 = stop ];then
PS=`ps -ef |grep lvs_monitor.sh |awk '{printf $2" "}'`
for X in `echo $PS`
do
kill -9 $X
done
else
sh ./lvs_monitor.sh &
fi
}
# LVS/NAT mode
# start
function nat_start {
nat_stop
VIP=$NAT_VIP
echo 1 >/proc/sys/net/ipv4/ip_forward
# start NAT mode
ifconfig eth1 $VIP netmask 255.255.255.0 up
route del -net 0.0.0.0
route add -net 0.0.0.0 netmask 0.0.0.0 gw $GATEWAY01
#ifconfig eth0:0 $VIP broadcast $VIP netmask 255.255.255.0 up
ipvsadm -C
ipvsadm -A -t $VIP:$DPORT -s wlc
ipvsadm -a -t $VIP:$DPORT -r $RIP1:$RPORT1 -m -w 1
ipvsadm -a -t $VIP:$DPORT -r $RIP2:$RPORT2 -m -w 2
ipvsadm -ln
ipvsadm -lnc
}
# stop
function nat_stop {
VIP=$NAT_VIP
echo 0 >/proc/sys/net/ipv4/ip_forward
# stop NAT mode
ifconfig eth1 down
route del -net 0.0.0.0
route add -net 0.0.0.0 netmask 0.0.0.0 gw $GATEWAY02 > /dev/null 2>&1
ipvsadm -C
}
# LVS/dricert routing mode
# start
function dr_start {
dr_stop
VIP=$DR_VIP
echo 1 >/proc/sys/net/ipv4/ip_forward
ifconfig eth0:0 $VIP broadcast $VIP netmask 255.255.255.255 up
route add -host $VIP dev eth0:0
# start DR mode
ipvsadm -C
ipvsadm -A -t $VIP:$DPORT -s wlc
ipvsadm -a -t $VIP:$DPORT -r $RIP1 -g -w 1
ipvsadm -a -t $VIP:$DPORT -r $RIP2 -g -w 2
ipvsadm -ln
ipvsadm -lnc
}
# stop
function dr_stop {
VIP=$DR_VIP
echo 0 >/proc/sys/net/ipv4/ip_forward
ifconfig eth0:0 $VIP broadcast $VIP netmask 255.255.255.255 down
route del -host $VIP dev eth0:0 > /dev/null 2>&1
# stop DR mode
ipvsadm -C
}
# LVS/tunneling mode
# start
function tun_start {
tun_stop
VIP=$TUN_VIP
# set interface
#echo 1 >/proc/sys/net/ipv4/ip_forward
ifconfig tunl0 $VIP broadcast $VIP netmask 255.255.255.255 up
route add -host $VIP dev tunl0
# start tun mode
ipvsadm -C
ipvsadm -A -t $VIP:$DPORT -s wlc
ipvsadm -a -t $VIP:$DPORT -r $RIP1 -i -w 1
ipvsadm -a -t $VIP:$DPORT -r $RIP2 -i -w 2
ipvsadm -ln
ipvsadm -lnc
}
# stop
function tun_stop {
VIP=$TUN_VIP
# set interface
#echo 1 >/proc/sys/net/ipv4/ip_forward
ifconfig tunl0 $VIP broadcast $VIP netmask 255.255.255.255 down
ip addr flush tunl0
route del -host $VIP dev tunl0 >/dev/null 2>&1
# stop tun mode
ipvsadm -C
}
service iptables stop
case "$1" in
nat)
echo -e "\n `date +%F" "%H:%M:%S` nat模式启动...." > $logfile
tun_stop
dr_stop
nat_start
lvs_monitor $1
;;
dr)
echo -e "\n `date +%F" "%H:%M:%S` dr模式启动...." > $logfile
tun_stop
nat_stop
dr_start
lvs_monitor $1
;;
tun)
echo -e " \n `date +%F" "%H:%M:%S ` tun模式启动...." > $logfile
dr_stop
nat_stop
tun_start
lvs_monitor $1
;;
stop)
echo -e "\n `date +%F" "%H:%M:%S` 关闭lvs...." > $logfile
dr_stop
nat_stop
tun_stop
lvs_monitor $1
;;
*)
echo $"Usage: $0 {nat|dr|tun|stop}"
;;
esac
lvs_realserver.sh 脚本如下:
#!/bin/bash
# write by lijing QQ 858080796,
# blog:http://qiaomiao.blog.51cto.com
# 20151223 v2.8 20160115
# home:http://www.qmlab.cn
#
# description:this script is use start lvs (nat/dr/tun).
#define the variable
#NAT_VIP="10.0.8.20"
DR_VIP="10.0.8.20"
TUN_VIP="10.0.8.20"
RIP1="10.0.8.21"
RIP2="10.0.8.22"
DPORT=80
RPORT1=80
RPORT2=80
#
# LVS/NAT mode
# start
function nat_start {
nat_stop
# set realserver gateway
route del -net 0.0.0.0
route add -net 0.0.0.0 netmask 0.0.0.0 gw $DIP
}
# stop
function nat_stop {
# set realserver gateway
route del -net 0.0.0.0
route add -net 0.0.0.0 netmask 0.0.0.0 gw $GATEWAY
}
# LVS/dricert routing mode
# start
function dr_start {
dr_stop
VIP=$DR_VIP
# set interface
ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
route add -host $VIP dev lo:0
# set realserver
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
}
# stop
function dr_stop {
VIP=$DR_VIP
# set interface
ifconfig lo:0 down
route del -host $VIP dev lo:0 >/dev/null 2>&1
# set realserver
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
}
# LVS/tunneling mode
# start
function tun_start {
tun_stop
VIP=$TUN_VIP
# set interface
ifconfig tunl0 $VIP broadcast $VIP netmask 255.255.255.255 up
route add -host $VIP dev tunl0
# set realserver
echo 1 > /proc/sys/net/ipv4/conf/tunl0/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/tunl0/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/tunl0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
}
# stop
function tun_stop {
VIP=$TUN_VIP
# set interface
ip addr flush tunl0
ifconfig tunl0 down
route del -host $VIP dev tunl0 >/dev/null 2>&1
# set realserver
echo 0 > /proc/sys/net/ipv4/conf/tunl0/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/tunl0/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/tunl0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
}
service iptables stop
case "$1" in
nat)
tun_stop
dr_stop
nat_start
;;
dr)
tun_stop
nat_stop
dr_start
;;
tun)
nat_stop
dr_stop
tun_start
;;
stop)
nat_stop
dr_stop
tun_stop
;;
*)
echo $"Usage: $0 {nat|dr|tun|stop}"
;;
esac
lvs_monitor.sh 脚本如下:(注意要放在lvs_redirector.sh在同一个目录下)
#!/bin/bash
# write by http://small.blog.51cto.com/259970/1728082
# 感谢 网友 super_color
rs=("10.0.8.21" "10.0.8.22")
port=80
function check_alldown {
#有一个rs主机能访问,就说明不是全部掉了
#检查到一个rs主机存活就退出检查
#如果全部rs不能访问,说明主机全掉了
for www in `echo ${rs[*]}`
do
curl --connect-timeout 1 http://$www &> /dev/null
if [ $? -eq 0 ]
then
echo 0
exit 0
fi
done
echo 100
}
function lvs_add {
ipvsadm -a -t $vip:$port -r $1
echo "add rs host:$1 to lvs"
}
function lvs_rm {
ipvsadm -d -t $vip:$port -r $1
echo "remove rs host:$1 to lvs"
}
function lvs_local {
#如果全部rs主机掉线,并且lvs中没有127.0.0.1就添加它
#如果可以访问一个rs主机,并且lvs中有127.0.0.1就删除它
all_down=`check_alldown`
rip=$(ipvsadm -L -n | gawk '/127.0.0.1/')
if [ $all_down -eq 100 ]
then
if [ "$rip" = "" ]
then
echo "`date +%F:%H-%M-%S` all rs host is down!" >> $logfile
lvs_add "127.0.0.1"
fi
else
if [ $all_down -eq 0 ] && [ ! "$rip" = "" ]
then
echo "`date +%F:%H-%M-%S` one rs host is up,remove local rs host!" >> $logfile
lvs_rm "127.0.0.1"
fi
fi
}
function lvs_rs {
#如果可以访问一个rs主机,并且lvs中没有它就添加它
#如果不能访问一个rs主机,并且lvs中有它就删除它
lvs_local
for www in `echo ${rs[*]}`
do
rip=$(ipvsadm -L -n | gawk "/$www/")
curl --connect-timeout 1 http://$www &> /dev/null
if [ $? -eq 0 ]
then
if [ "$rip" = "" ]
then
echo "`date +%F:%H-%M-%S` rs host:$www is up!" >> $logfile
lvs_add "$www"
fi
else
if [ ! "$rip" = "" ]
then
echo "`date +%F:%H-%M-%S` rs host:$www is down!" >> $logfile
lvs_rm "$www"
fi
fi
done
}
function lvs_monitor {
while true
do
# echo "check lvs rs health!"
lvs_rs
sleep 1
done
}
lvs_monitor
在验证结果之前,要保证你的路由器的端口映射是正确,且生效的,上面图中:
当外网客户端192.168.20.200访问时,nat模式路由器192.168.20.14映射到172.16.8.11这个IP,
dr和 tun模式映射到 10.0.8.20这个IP。
验证方法:先测试直接内网访问两台realserver web是不是正常,以及redirector server 的本地127.0.0.1 web 是不是正常,再测试访问192.168.20.14,当其中一台故障时是不是还可以访问,到全故障时,有没有切的本地127.0.0.1(故障提示页)的web,当其中只要有一台恢复时,会不会启动添加启用,并删除127.0.0.1的web.