Keepalived+LVS-DR+Docker Swarm实现高可用的负载均衡集群
业务需求
项目中需要在后端服务器集群中虚拟一个IP出来,所有终端设备使用UDP或TCP协议向虚拟IP上报数据,并通过负载均衡调度后,分发给不同的后端服务器上,且后端要能获取到终端的实际IP和端口。
实现方案
目前,有硬件负载均衡设备,也有软件负载均衡,硬件负载均衡的价格价格太过高大上,用不起。只能考虑软件负载均衡了。当前最流行的两个软件方案是Keepalived+Nginx 和Keepalived+LVS(DR),经过测试实验后,发现Nginx 的UDP协议,后端应用无法获取到终端设备的真实IP,最终选定Keepalived+LVS(DR)+Docker Swarm来实现业务需求。
Keepalived 主要的工作是提供一个虚拟IP(VIP),提供健康检查,故障转移,实现真实机的故障隔离及负载均衡器间的失败 切换,提高系统的可用性。
LVS主要的工作是提供负载均衡,把终端的上报数据按照需求调度分发给后端真实服务器处理。
Docker Swarm主要是提供一个分布式应用程序集群,其自身也提供内部负载均衡功能,拥有应用服务秒启动、一致的运行环境、持续交付和部署、轻松迁移、故障自动切换节点运行等优势。后端应用服务都运行在该集群上。
相关术语
LVS (Linux Virtual Server Linux 虚拟服务器)
DS (Director Server),指的是负载均衡器节点
RS (Real Server),后端真实的工作服务器
VIP (Virtual IP),虚拟的 IP 地址,向外部直接面向用户请求,作为用户请求的目标的 IP 地址
RIP (Real Server IP),后端服务器的 IP 地址
CIP (Client IP),客户端的 IP 地址
参考文章
Load Balancing - Linux Virtual Server (LVS) and Its Forwarding Modes
【均衡负载之LVS 系列三】 - 高可用 LVS+KeepAlived 集群
具体实现
工作原理
(1) 当用户请求到达 Director Server,此时请求的数据报文会先到内核空间的 PREROUTING 链。 此时报文的源 IP 为 CIP,目标 IP 为 VIP
(2) PREROUTING 检查发现数据包的目标 IP 是本机,将数据包送至 INPUT 链
(3) IPVS 比对数据包请求的服务是否为集群服务,若是,将请求报文中的源 MAC 地址修改为 DIP 的 MAC 地址,将目标 MAC 地址修改 RIP 的 MAC 地址,然后将数据包发至 POSTROUTING 链。 此时的源 IP 和目的 IP 均未修改,仅修改了源 MAC 地址为 DIP 的 MAC 地址,目标 MAC 地址为 RIP 的 MAC 地址
(4) 由于 DS 和 RS 在同一个网络中,所以是通过二层来传输。POSTROUTING 链检查目标 MAC 地址为 RIP 的 MAC 地址,那么此时数据包将会发至 Real Server。
(5) RS 发现请求报文的 MAC 地址是自己的 MAC 地址,就接收此报文。处理完成之后,将响应报文通过 lo 接口传送给 eth0 网卡然后向外发出。 此时的源 IP 地址为 VIP,目标 IP 为 CIP
(6) 响应报文最终送达至客户端
LVS/DR 模型的特性
保证前端路由将目标地址为 VIP 报文统统发给 Director Server,而不是 RS
RS 可以使用私有地址;也可以是公网地址,如果使用公网地址,此时可以通过互联网对 RIP 进行直接访问
RS 跟 Director Server 必须在同一个物理网络中
所有的请求报文经由 Director Server,但响应报文必须不能进过 Director Server
不支持地址转换,也不支持端口映射
RS 可以是大多数常见的操作系统
RS 的网关绝不允许指向 DIP(因为我们不允许他经过 director)
RS 上的 lo 接口配置 VIP 的 IP 地址
注意:LVS(DR)模式要求 LVS 调度器及所有应用服务器在同一个网段中
系统架构
2个节点服务器组成Director Server,在这两个服务器上面部署Keepalived和LVS,实现VIP,负载均衡、健康检查、故障切换等功能。
5个节点服务器组成Real Server集群,部署Docker Swarm,后端应用都运行在Docker Swarm集群中。
案例中实现了TCP80端口和UDP8000端口的分发。
System OS:Debian10
Software:Keepalived、LVS、ncat、Docker
DS Server
节点 | IP |
---|---|
DS-Master | 192.168.1.232 |
DS-Backup | 192.168.1.233 |
(VIP)虚拟IP地址 :192.168.1.234 |
Real Server(Dovker Swarm集群)
节点 | IP |
---|---|
RS1 | 192.168.1.225 |
RS2 | 192.168.1.226 |
RS3 | 192.168.1.227 |
RS4 | 192.168.1.228 |
RS5 | 192.168.1.229 |
配置Director Server
安装软件
在DS Server的两个服务器节点上都安装 Keepalived、LVS和ncat,ncat软件是一个端口扫描工具,在这用于检测RS Server的测试网络端口是否正常。
Debian10中已经内置LVS,无需安装。。。
apt install keepalived ncat -y
配置keepalived
在DS-Master和DS-Backup两个节点配置keepalived,keepalived内置TCP_CHECK来检测TCP端口对RS进行健康检查,但无UDP的端口检测,故需在MISC_CHECK添加一个nc命令脚本检测UDP端口。
DS-Master
sudo nano /etc/keepalived/keepalived.conf
编辑Keepalived文件
! Configuration File for keepalived
global_defs { # 全局配置
router_id LVS_VIP # 负载均衡标识,在局域网应该是唯一的
#vrrp_strict #将严格遵守vrrp协议这一项关闭,否则会因为不是组播而无法启动keepalived
}
vrrp_instance VI_1 {
state MASTER # 指定该keepalived节点的初始状态 这里这个并不意味着他就是主机!!是根据下面比较 priority 值来判断主机的
interface ens192 # vrrp实例绑定的接口,用于发送VRRP包 这里需要写成ip addr 显示的网卡名
virtual_router_id 51 # 指定VRRP实例ID,范围是0-255
priority 100 # 指定优先级,优先级高的将成为MASTER
advert_int 1 # 指定发送VRRP通告的间隔。单位是秒
authentication {
auth_type PASS # 指定认证方式。PASS简单密码认证(推荐),AH:IPSEC认证(不推荐)
auth_pass 2021 # 指定认证所使用的密码。最多8位
}
unicast_src_ip 192.168.1.232 #配置单播的源地址
unicast_peer {
192.168.1.233 #配置单播的目标地址
}
virtual_ipaddress {
192.168.1.234 # 指定VIP地址
}
}
virtual_server 192.168.1.234 80 {
delay_loop 6
lb_algo wrr
lb_kind DR
protocol TCP
real_server 192.168.1.225 80 {
weight 1
TCP_CHECK { #对后端服务器做tcp的监测
connect_timeout 5 #定义连接超时时长
retry 3 #重试次数
delay_before_retry 3 #每次重试的间隔时间
connect_port 80 #监测的端口
}
}
real_server 192.168.1.226 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.227 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.228 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.229 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
}
virtual_server 192.168.1.234 8000 {
delay_loop 6
lb_algo wrr
lb_kind DR
protocol UDP
real_server 192.168.1.225 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.225 8000"
misc_timeout 5
}
}
real_server 192.168.1.226 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.226 8000"
misc_timeout 5
}
}
real_server 192.168.1..227 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.227 8000"
misc_timeout 5
}
}
real_server 192.168.1..228 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.228 8000"
misc_timeout 5
}
}
real_server 192.168.1.229 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.229 8000"
misc_timeout 5
}
}
}
udp_check.sh的脚本如下
#!/bin/bash
/usr/bin/nc -unvz -w 1 $1 $2 2>&1 | grep success &> /dev/null
exit $?
DS-Backup
sudo nano /etc/keepalived/keepalived.conf
编辑Keepalived文件
! Configuration File for keepalived
global_defs { # 全局配置
router_id LVS_VIP2 # 负载均衡标识,在局域网应该是唯一的
#vrrp_strict #将严格遵守vrrp协议这一项关闭,否则会因为不是组播而无法启动keepalived
}
vrrp_instance VI_1 {
state BACKUP # 指定该keepalived节点的初始状态
interface ens192 # vrrp实例绑定的接口,
virtual_router_id 51 # 指定VRRP实例ID,范围是0-255
priority 100 # 指定优先级,优先级高的将成为MASTER
advert_int 1 # 指定发送VRRP通告的间隔。单位是秒
authentication {
auth_type PASS # 指定认证方式。PASS简单密码认证(推荐),AH:IPSEC认证(不推荐)
auth_pass 2021 # 指定认证所使用的密码。最多8位
}
unicast_src_ip 192.168.1.233 #配置单播的源地址
unicast_peer {
192.168.1.232 #配置单播的目标地址
}
virtual_ipaddress {
192.168.1.234 # 指定VIP地址
}
}
virtual_server 192.168.1.234 80 {
delay_loop 6
lb_algo wrr
lb_kind DR
protocol TCP
real_server 192.168.1.225 80 {
weight 1
TCP_CHECK { #对后端服务器做tcp的监测
connect_timeout 5 #定义连接超时时长
retry 3 #重试次数
delay_before_retry 3 #每次重试的间隔时间
connect_port 80 #监测的端口
}
}
real_server 192.168.1.226 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.227 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.228 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.229 80 {
weight 1
TCP_CHECK {
connect_timeout 5
retry 3
delay_before_retry 3
connect_port 80
}
}
}
virtual_server 192.168.1.234 8000 {
delay_loop 6
lb_algo wrr
lb_kind DR
protocol UDP
real_server 192.168.1.225 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.225 8000"
misc_timeout 5
}
}
real_server 192.168.1.226 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.226 8000"
misc_timeout 5
}
}
real_server 192.168.1.227 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.227 8000"
misc_timeout 5
}
}
real_server 192.168.1.228 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.228 8000"
misc_timeout 5
}
}
real_server 192.168.1.229 8000 {
weight 1
MISC_CHECK {
misc_path "/home/zcbuser/gfs-share/keepalived/udp_check.sh 192.168.1.229 8000"
misc_timeout 5
}
}
}
udp_check.sh的脚本如下
#!/bin/bash
/usr/bin/nc -unvz -w 1 $1 $2 2>&1 | grep success &> /dev/null
exit $?
在DS两个节点上启动Keepalived,并设置开机自启动
#启动
systemctl start keepalived
#开机自启动
systemctl enable keepalived
#查看运行状态
systemctl status keepalived
查看LVS的状态
sudo ipvsadm -Ln --stats
返回的信息:
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Conns InPkts OutPkts InBytes OutBytes
-> RemoteAddress:Port
TCP 192.168.1.234:80 0 0 0 0 0
-> 192.168.1.225:80 0 0 0 0 0
-> 192.168.1.226:80 0 0 0 0 0
-> 192.168.1.227:80 0 0 0 0 0
-> 192.168.1.228:80 0 0 0 0 0
-> 192.168.1.229:80 0 0 0 0 0
UDP 192.168.1.234:8000 0 0 0 0 0
-> 192.168.1.225:8000 0 0 0 0 0
-> 192.168.1.226:8000 0 0 0 0 0
-> 192.168.1.227:8000 0 0 0 0 0
-> 192.168.1.228:8000 0 0 0 0 0
-> 192.168.1.229:8000 0 0 0 0 0
配置RS Server
搭建Docker Swarm集群,可以参考我以前写的《4个树莓派搭建Docker Swarm集群》,这里就不详述了。
需要注意的是,创建Swarm服务时,必须加上–network host ,否则后端应用无法获取到客户真实IP。
这里主要是详述在5台RS Server节点服务器的 lo 接口配置 VIP 的 IP 地址
依次编辑5台RS Server的网卡配置文件
sudo nano /etc/network/interfaces
在打开的文件里添加
auto lo:0
iface lo:0 inet static
address 192.168.1.234
netmask 255.255.255.255
保存退出
对arp_ignore和arp_announce参数进行配置,主要是实现禁止响应对VIP的ARP请求。
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
分别重启5台RS的网络服务
sudo service networking restart
上面是临时修改,重启后就失效了,永久修改
sudo nano /etc/sysctl.conf
插入
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
sudo reboot 命令重启5台RS节点
至此,安装完毕,进行测试,TCP80端口和UDP8000端口都能正常工作,后端服务也可以获取到终端设备的真实IP和端口。