在Linux基于Keepalived搭建LVS实现高可用负载均衡
前言
参考这篇CSDN - 在Linux配置LVS实现负载均衡可以搭建LVS(Linux Virtual Server)实现负载均衡。但是它有下列主要缺陷:
- 没有备机,LVS故障时将不能提供服务。就算准备了备机,也需要人为监控LVS状态,在故障的时候修复或者用备机顶替
- 不能监控RS(Real Server)存活状态,如果有RS故障,将会有部分请求分发到这些机器,导致请求无法处理
Keepalived出现就是为了解决这些问题,它可以带来以下好处:
- 根据Keepalived配置设置,自动配置LVS
- 搭建主备负载均衡服务器(LVS),监控存活状态,在主机宕机后及时选出新主提供服务
- 检测RS(Real Server)存活状态,及时剔除无效的RS,避免将请求转发到无效的RS上,也会将恢复的RS重新加入负载列表
这一篇搭建步骤其实很简单,安装配置下Keepalived就可以了,只不过在测试部分写的比较详细,篇幅相对较长一点。
搭建步骤
搭建RS的步骤和上面那篇文章是一样的,我们现在需要做的是准备2台及以上的负载均衡服务器DS(Director Server)作主备。在主备上都配置Keepalived。在这里我们用4台主机:
- node1(DS主):IP为192.168.252.131
- node2(RS):IP为192.168.252.132
- node3(RS):IP为192.168.252.133
- node4(DS备):IP为192.168.252.134
建议先关掉防火墙。
echo "停止、关闭防火墙" > /dev/null
systemctl stop firewalld
systemctl disable firewalld
配置真实服务器(RS)
这一步和CSDN - 在Linux配置LVS实现负载均衡中的步骤一样。
要注意的是,要先改ARP配置,再配置VIP,不然有可能会在配置ARP之前暴露VIP地址。
- 修改内核地址解析协议(Address Resolution Protocol,ARP)内核参数映射文件让其他网卡不暴露VIP,保/证VIP对内(本机)可见,对外隐藏
- 对虚拟环回网卡配置子接口,绑定VIP,并设置掩码为255.255.255.255,让RS能处理请求并能直接返回
echo "给ens33网卡接口配置" > /dev/null
echo 1 > /proc/sys/net/ipv4/conf/ens33/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/ens33/arp_announce
echo "为了方便,给所有网卡接口配置" > /dev/null
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo "对虚拟环回网卡配置子接口" > /dev/null
ifconfig lo:2 192.168.252.100 netmask 255.255.255.255
配置负载均衡服务器(DS主)
echo "安装keepalived和ipvs管理工具" > /dev/null
yum install keepalived ipvsadm -y
echo "备份keepalived配置文件" > /dev/null
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
echo "将配置覆盖到keepalived配置文件" > /dev/null
echo '! Configuration File for keepalived
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.252.100/24 dev ens33 label ens33:2
}
}
virtual_server 192.168.252.100 80 {
delay_loop 6
lb_algo rr
lb_kind DR
nat_mask 255.255.255.0
persistence_timeout 0
protocol TCP
real_server 192.168.252.132 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.252.133 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}' > /etc/keepalived/keepalived.conf
echo "启动keepalived" > /dev/null
service keepalived start
下面是配置中每个参数的解释。如果安装了Linux/Unix环境下命令与函数的帮助文档(Manual page)可以执行man 5 keepalived.conf
查看相关配置文档。也可以访问Keepalived官网的Keepalived Configuration Manual Page。
vrrp_instance VI_1 { # 虚拟路由冗余协议(Virtual Router Redundancy Protocol,VRRP),监控LVS主和RS存活状态走这个协议
state MASTER # 这台机器初始状态,MASTER|BACKUP
interface ens33 # VRRP使用的网卡接口
virtual_router_id 51 # Keepalived可以配置多套LVS,同一套LVS使用相同的虚拟路由ID
priority 100 # 优先级,按优先级选主,一般服务器性能好的优先级配高点
advert_int 1 # VRRP通告间隔(秒)
authentication {
auth_type PASS # 鉴权类型,PASS|AH,官网推荐的是PASS简单密码验证
auth_pass 1111 # 密码
}
virtual_ipaddress { # 配置VIP
192.168.252.100/24 dev ens33 label ens33:2 # 使用ens33创建(dev)子接口命名(label)为ens33:2,VIP为192.168.252.100,子网掩码为255.255.255.0(/24)
}
}
virtual_server 192.168.252.100 80 { # 配置LVS虚拟服务指定IP和端口号
delay_loop 6 # 健康监测时间间隔(秒)
lb_algo rr # 负载均衡算法为轮询(rr)
lb_kind DR # 类型为直接路由(DR)
nat_mask 255.255.255.0 # 子网掩码
persistence_timeout 0 # 保证在这个时间内(秒)同一客户端的请求发送到相同的RS,避免每次请求跟不同RS建立连接。LVS会记录TCP握手数据包所以是知道之前哪个IP转发到了哪台RS,我们为了测试能实时看到切换效果设置成0
protocol TCP # TCP协议
real_server 192.168.252.132 80 { # RS配置,指定IP和监听的端口
weight 1 # 权重
HTTP_GET { # 健康检查,HTTP_GET|SSL_GET
url {
path / # 请求路径
status_code 200 # 返回状态码为这个视作正常
}
connect_timeout 3 # 超时时间(秒)
nb_get_retry 3 # 重试次数
delay_before_retry 3 # 重试时间间隔(秒)
}
}
}
配置负载均衡服务器(DS备)
备机配置和主机几乎一样,只需要将Keepalived配置中初始状态(state)改成BACKUP,优先级(priority)设置比主低一点。
测试
查看DS主备配置
- 查看DS(主)配置
echo "查看网卡接口" > /dev/null
ifconfig
echo "查看ipvs路由规则" > /dev/null
ipvsadm -ln
可以看到Keepalived根据我们的配置生成了VIP网卡接口(ens33:2)和IPVS路由表规则。客户端可以通过VIP来访问。
- 查看DS(备)配置
使用相同的方式在备机只能看到IPVS路由表配置,但是看不到配置VIP的网卡接口。没有VIP,客户端无法通过VIP访问,IPVS就算配置了也不会生效,因为是备机,主机正常的情况下不生效是对的。
这一步我们验证的出了主备之间差异就在于只有主有VIP,有VIP才能对外提供服务。
访问VIP验证负载均衡
给两台RS(node2、node3)都安装httpd,并分别指定不同的httpd主页内容,测试时好区分。
echo "安装" > /dev/null
yum -y install httpd
echo "启动" > /dev/null
service httpd start
echo "覆盖指定httpd主页内容" > /dev/null
echo 'node2' > /var/www/html/index.html
我们通过命令行或者浏览器(浏览器可能有GET缓存,不切换等几秒缓存过期)访问VIP(192.168.252.100)可以看从返回结果看到在两个RS间轮询切换。
我们可以在DS(主)上看到IPVS连接记录。
ipvsadm -lcn
但是在DS(备)上就看不到了,因为没有VIP,是无法提供服务的。
这一步我们验证出主机成功实现了负载均衡,而备机啥也没干。
查看虚拟路由冗余协议(VRRP)数据包
echo "安装tcpdump" > /dev/null
yum install tcpdump -y
echo "查看ens33网卡接口数据" > /dev/null
tcpdump -i ens33 vrrp -n
在主备执行,都可以看到主节点向VRRP组播地址(224.0.0.18)发送报文,报文内容包我们在Keepalived配置的信息,如:含虚拟路由ID(vrid 51)、优先级(prio 100)、简单密码验证(authtype simple)等等。
验证主备切换
将DS(主)关机,或者宕掉VIP和VRRP网卡。
ifconfig ens33 down
我们还是可以继续访问VIP,查看备机网卡配置可以发现新增了VIP。
查看备机IPVS连接记录,可以看到请求都到达了备机上。
而对于客户端来说,整个切换过程无感知,也不知道备机的存在。
恢复DS(主)
重启主节点或者,重新启动网卡。会看到主节点重新配置了VIP、IPVS路由表规则,再次成为了主对外提供服务。而备机VIP配置被清除,不能再对外提供服务。对客户端而言,这个过程同样是透明的。
ifconfig ens33 up
验证监控RS
我们宕掉一台RS,或者停掉RS的httpd。
service httpd stop
等待IPVS一段时间重试之后,查看主(node1)备(node4)的IPVS路由条目可以看到,路由条目中宕掉的node2(192.168.252.132)已被剔除。
客户端访问VIP也只会转发到健康的node3(192.168.252.133)上。
如果恢复宕掉的node2,将会看到IPVS路由条目中会恢复,访问VIP也会看到有请求负载到node2。
到这一步,我们验证了IPVS监控RS,及时剔除和恢复负载。
查看Keepalived进程
ps -ef | grep keepalived
从主备都可以看到有3个相关进程,①是进程ID,②是进程的父进程ID。
主进程(12138)通告自己健康状态;两个子进程(12139、12140)分别连接两个RS,检查它们的存活状态。
其他问题
DS(主)Keepalived进程异常退出
如果我们让DS(主)的Keepalived进程异常退出(如:kill -9
),它就无法清除IPVS配置,主机仍然有VIP和IPVS路由规则,且能对外提供服务。
然后DS(备)没收到DS(主)的健康报文,顶替成为主,添加VIP,也能对外提供服务。
这样就导致了网络中有两个VIP存在,那就就可能一个TCP握手中,客户端两次发送的包分别给了不同的VIP,导致连接失败。所以Keepalived在这种情况下还是有缺陷。
下面这段是我测试客户端在这种情况下访问结果,纯粹好奇,作用不大。
但是我查看客户端的ARP缓存发现,出现上面这种情况后,缓存中VIP对应的MAC地址由指向DS(主)的更新成了指向DS(备)的。
arp -an
客户端能正常访问VIP。
请求都到了备机的VIP上。
清空客户端的ARP缓存,又会发现VIP地址重新指向了DS(主机),然后请求又重新到了DS主上。
虽然不是很正常,但用起来居然没出问题,也许是都在同一局域网,网络关系简单,但是同时存在多个VIP这种情况还是不要出现。
防火墙导致多个VIP
问题是主机启动Keepalived后,自动添加了VIP,然后备机也启动,也直接绑定了VIP。原因是备机的防火墙限制VRRP协议导致没收到主机报文,备机认为主机故障,自己直接顶替成了主机。
解决办法是让防火墙允许接收VRRP。细节可以参考CSDN - keepalived出现主备机同时绑定vip的解决方法。
echo "设置防火墙允许接收VRRP" > /dev/null
firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --in-interface ens33 --destination 224.0.0.18 --protocol vrrp -j ACCEPT
echo "刷新防火墙" > /dev/null
firewall-cmd --reload
或者将防火墙关掉。