声明:本文多处直接引用了文末的参考链接中的内容,如有侵权,请联系删除
服务高可用方案
方案一:使用Nginx
通过Nginx进行反向代理到多个真实服务器,在此方案中当Nginx挂掉后,服务会停止
方案二:使用Keepalived结合Nginx实现双机主备
该方案是在每台Nginx服务器上使用Keepalived配置虚拟ip的方式,将多台提供相同功能的服务器组成一个路由器组,在路由器组中指定一个或多个虚拟ip对外提供服务,在此种方式下实现了Nginx主节点挂掉后,从节点的Nginx会启用,从而实现了双机主备
Keepalived节点配置
- 主节点配置
global_defs {
# 路由id:当前安装keepalived节点主机的表示符,全局唯一
router_id keep_88
}
# 脚本配置,此处配置的脚本用于在nginx宕机后自动重启nginx
vrrp_script check_nginx_alive {
script "/etc/keepalived/check_nginx_alive_or_not.sh"
interval 2 # 每隔两秒运行上一行脚本
weight 10 # 如果脚本运行成功,则升级权重+10
# weight -10 # 如果脚本运行失败,则升级权重-10
}
# 基于vrrp协议的实例,是一个服务器节点
vrrp_instance VI_1 {
# 表示的状态,当前的LVS_DEVEL为nginx的主节点,MASTER/BACKUP
state MASTER
# 当前实例绑定的网卡,可通过ip addr查看
interface ens33
# 虚拟路由id,在一个路由器组中需要保证主备节点一致
virtual_router_id 51
# 优先级/权重,谁的优先级高,在MASTER宕机以后就能成为MASTER
priority 100
# 主备之间同步检查的时间间隔,默认为1,单位为秒
advert_int 1
# 认证授权的密码,防止非法节点的进入,所有主备节点需保持一致
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟出来的ip(vip),可以有多个
virtual_ipaddress {
# 注意:需要保证主备两台的vip都是一样的,绑定到同一个vip
# 同时虚拟ip的网段需要与组内的其他路由的网段相同
192.168.31.88
}
# 追踪 nginx 脚本
track_script {
check_nginx_alive
}
}
- 备用节点配置
global_defs {
router_id keep_88
}
# 脚本配置,此处配置的脚本用于在nginx宕机后自动重启nginx
vrrp_script check_nginx_alive {
script "/etc/keepalived/check_nginx_alive_or_not.sh"
interval 2 # 每隔两秒运行上一行脚本
weight 10 # 如果脚本运行成功,则升级权重+10
# weight -10 # 如果脚本运行失败,则升级权重-10
}
# 基于vrrp协议的实例,是一个服务器节点
vrrp_instance VI_1 {
# 表示的状态,当前的LVS_DEVEL为nginx的主节点,MASTER/BACKUP
state BACKUP
# 当前实例绑定的网卡,可通过ip addr查看
interface ens33
# 虚拟路由id,在一个路由器组中需要保证主备节点一致
virtual_router_id 51
# 优先级/权重,谁的优先级高,在MASTER宕机以后就能成为MASTER,官网推荐主备节点的优先级只差需要至少为50
priority 40
# 主备之间同步检查的时间间隔,默认为1,单位为秒
advert_int 1
# 认证授权的密码,防止非法节点的进入,所有主备节点需保持一致
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟出来的ip(vip),可以有多个
virtual_ipaddress {
192.168.31.88
}
# 追踪 nginx 脚本
track_script {
check_nginx_alive
}
}
- Nginx重启脚本
# 路径:/etc/keepalived/check_nginx_alive_or_not.sh
#!/bin/bash
A=`ps -C nginx --no-header |wc -l`
# 判断nginx是否宕机,如果宕机了,尝试重启
if [ $A -eq 0 ];then
/usr/local/nginx/sbin/nginx
# 等待一小会再次检查nginx,如果没有启动成功,则停止keepalived,使其启动备用机
sleep 3
if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
killall keepalived
fi
fi
在此方案中,由于备用Keepalived只会在主Keepalived宕机时才会发挥作用,而且备用服务器由于也需要支持相同的用户访问,往往其配置与性能需要与主服务器相同或者相差不大,因此在这种方案中备用服务器的存在会造成资源的巨大浪费
方案三:使用Keepalived结合Nginx实现双主热备
双主热备的硬件要求与双机主备完全相同,但需要在Keepalived的配置文件中增加额外的配置,使得多台Keepalived服务器互为主备,同时为保证备用服务器也能正常提供服务,在同一个Keepalived中,在作为主节点和备用节点的时候,需要设置为不同的虚拟ip,同时设置域名访问时通过dns轮询的方式解析到所有的虚拟ip上,其网络架构图如下
Keepalived节点配置
- 主节点配置
global_defs {
router_id keep_101
}
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.31.88
}
}
vrrp_instance VI_2 {
state BACKUP
interface ens33
# 注意,此处定义的是另外一组路由,因此不能再使用51
virtual_router_id 52
priority 40
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.31.89
}
}
- 备用节点配置
global_defs {
router_id keep_93
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 40
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.31.88
}
}
vrrp_instance VI_2 {
state MASTER
interface ens33
# 注意,此处定义的是另外一组路由,因此不能再使用51
virtual_router_id 52
# 由于是主节点,此处将优先级设置为100
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
# 设置新的虚拟ip
192.168.31.89
}
}
在以上配置文件中,互为主备的Keepalived分别对应了182.168.31.88和192.168.31.89两个虚拟ip,在配置域名解析时,只需要将域名轮询解析到2个ip即可
注意,不管是阿里云或者腾讯云的云服务主机,如果需要配置虚拟ip,需要购买额外的付费服务
此方案虽然解决了服务器资源浪费的问题,但是由于Nginx是七层负载均衡,因此并不足以支持高并发的用户访问,无论有多少从节点,还是会有挂掉的风险,从而演进第四种方案
方案四:LVS + Keepalived + Nginx服务高可用方案
通过LVS+Keepalived+Nginx,搭建了Nginx集群,对于LVS使用DR模式,将请求请求转发到各个真实服务器,同时真实服务器的响应不通过LVS,而是直接通过Nginx响应给用户,这样LVS负载就更高了,从而提高性能
LVS
LVS 简介
LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。LVS项在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。LVS具有以下特点个
- LVS基于四层负载均衡,工作效率更高
- LVS可以充当Nginx集群的调度者,用来解决单个Nginx抗压能力不强的问题
- LVS可以只接受请求不响应,即只处理请求转发,由于具有这样的特性,其支持的并发比Nginx更高
LVS 与 Nginx 网络拓扑图对比
LVS 工作模式
- NAT
NAT工作模式下与Nginx类似,不适用于超大并发的场景,通常LVS位于公网,真实的服务器位于企业内网中,用户不能直接访问真实服务器
- TUN(隧道)
TUN是一种IP隧道模式,要求所有的计算机节点必须具有网卡用于建立隧道,也即是所有的真实服务器也需要暴露在公网中,在这种模式下用户所有的响应不会经过LVS,可以提高并发和吞吐量
- DR(直接路由)
DR是使用一个虚拟路由来统一处理用户响应的一种模式,可以避免将真实服务器暴露在公网中
LVS + Keepalived + Nginx高可用服务实现
服务器准备
准备4台虚拟机(CentOS 7),用于测试使用
主机 | IP | 作用 |
---|---|---|
主机1 | 192.168.31.169 | Nginx1 |
主机2 | 192.168.31.190 | Nginx2 |
主机3 | 192.168.31.101 | Keepalived主节点 |
主机4 | 192.168.31.93 | Keepalived备用节点 |
无 | 192.168.31.89 | 虚拟IP(VIP) |
准备工作
关闭网络配置管理器
开发阶段准备工作:关闭网络配置管理器,在开发阶段,由于采用的时虚拟机的方式,在这种方式下如果不关闭可能会引起网络接口冲突
systemctl stop NetworkManager
systemctl disable NetworkManager
安装集群管理工具
使用以下命令安装集群管理工具(ipvsadm),ipvs是LVS的管理工具,用于提供集群配置管理的功能
yum install -y ipvsadm
ipvsadmin常用命令
- 查看帮助文档:ipvsadm -h 或者 man ipvsadm
- 查看集群列表:ipvsadm -Ln
- 查看集群状态:ipvsadm -Ln --stats
- 重启ipvsadm,重启后需要重新配置:service ipvsadm restart
- 查看持久化连接:ipvsadm -Ln --persistent-conn
- 查看连接请求过期时间以及请求源ip和目标ip:ipvsadm -Lnc
- 设置tcp tcpfin upd的过期时间(一般保持默认):ipvsadm --set 1 1 1
- 查看过期时间:ipvsadm -Ln --timeout
搭建步骤
安装Nginx和Keepalived
构建虚拟网络接口子接口
分别在主机1和主机2上构建虚拟网络接口子接口
- 进入到/etc/sysconfig/network-scripts:
cd /etc/sysconfig/network-scripts
- 复制配置文件:
cp ifcfg-lo ifcfg-lo:1
- 修改配置文件,将配置文件改为如下内容
# 名称需要与配置文件的名称保持一致
DEVICE=lo:1
# 构建的虚拟ip
IPADDR=192.168.31.89
NETMASK=255.255.255.255
NETWORK=127.0.0.0
# If you're having problems with gated making 127.0.0.0/8 a martian,
# you can change this to something else (255.255.255.255, for example)
BROADCAST=127.255.255.255
ONBOOT=yes
NAME=loopback
- 重启网络:
service network restart
或者ifup lo
,使用ip addr
查看网络信息,出现新配置的网卡对应的虚拟ip即表示虚拟ip构建成功
配置ARP(Address Resolution Protocal,地址解析协议)
- 修改 /etc/sysctl.conf 配置文件,增加以下内容
# 配置所有网卡,默认网卡以及虚拟网卡的arp响应级别和通告行为,分别对应:all,default,lo
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
- 使用
sysctl -p
进行刷新 - 添加host路由用于接收数据报文,接收报文后交给本机的lo:1处理,配置命令为:
route add -host 192.168.31.89 dev lo:1
- 查看host路由配置结果:
route -n
- 以上添加host路由的方式在服务器重启后会失效,因此需要将配置加入到开机自启动文件中,添加方式:
echo "route add -host 192.168.31.89 dev lo:1" >> /etc/rc.local
ARP配置参数说明
- arp_ignore:ARP响应级别(处理请求)
- 0:只要本机配置了ip,就能响应请求
- 1:请求的目标地址达到对应的网络接口,才会响应请求
- arp_announce:ARP通告行为(返回响应)
- 0:本机上任何网络接口都可以向外通告,所有的网卡都能接收到通告
- 1:尽可能避免本网卡与不匹配的目标进行通告
- 2:只在本网卡通告
配置Keepalived节点
- 配置Keepalived主节点,配置文件路径为:
/etc/keepalived/keepalived.conf
,配置内容如下
global_defs {
router_id keep_101
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 41
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.31.89
}
}
# 配置集群地址访问的ip + 端口,端口和nginx保存一致,都是80
virtual_server 192.168.31.89 80 {
# 健康检查的时间,单位:秒
delay_loop 6
# 配置负载均衡的算法,默认是轮询
lb_algo rr
# 设置LVS的工作模式:NAT|TUN|DR
lb_kind DR
# 设置会话持久化的时间,单位:秒
persistence_timeout 5
# 协议 -t
protocol TCP
# 负载均衡的真实服务器,也就是nginx节点的具体的真实ip地址
real_server 192.168.31.169 80 {
# 轮询的默认权重配比设置为1
weight 1
# 设置健康检查
TCP_CHECK {
# 检查80端口
connect_port 80
# 超时时间 2s
connect_timeout 2
# 重试次数
nb_get_retry 2
# 间隔时间 3s
delay_before_retry 3
}
}
real_server 192.168.31.190 80 {
weight 1
TCP_CHECK {
# 检查80端口
connect_port 80
# 超时时间 2s
connect_timeout 2
# 重试次数
nb_get_retry 2
# 间隔时间 3s
delay_before_retry 3
}
}
}
- 清除主节点已有的负载均衡规则:
ipvsadm -C
- 重启主节点keepalived:
service keepalived restart
- 查看主节点集群配置结果:
ipvsadm -Ln
- 使用
ip addr
查看主节点的网络信息如下,可以看到192.168.31.89已经出现在网卡ip地址中
- 配置Keepalived备用节点,配置文件路径为:
/etc/keepalived/keepalived.conf
,配置内容如下
global_defs {
router_id keep_93
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 41
priority 40
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.31.89
}
}
# 配置集群地址访问的ip + 端口,端口和nginx保存一致,都是80
virtual_server 192.168.31.89 80 {
# 健康检查的时间,单位:秒
delay_loop 6
# 配置负载均衡的算法,默认是轮询
lb_algo rr
# 设置LVS的工作模式:NAT|TUN|DR
lb_kind DR
# 设置会话持久化的时间
persistence_timeout 5
# 协议 -t
protocol TCP
# 负载均衡的真实服务器,也就是nginx节点的具体的真实ip地址
real_server 192.168.31.169 80 {
# 轮询的默认权重配比设置为1
weight 1
# 设置健康检查
TCP_CHECK {
# 检查80端口
connect_port 80
# 超时时间 2s
connect_timeout 2
# 重试次数
nb_get_retry 2
# 间隔时间 3s
delay_before_retry 3
}
}
real_server 192.168.31.190 80 {
weight 1
TCP_CHECK {
# 检查80端口
connect_port 80
# 超时时间 2s
connect_timeout 2
# 重试次数
nb_get_retry 2
# 间隔时间 3s
delay_before_retry 3
}
}
}
- 清除备用节点已有的负载均衡规则:
ipvsadm -C
- 重启备用节点keepalived:
service keepalived restart
- 查看备用节点集群配置结果:
ipvsadm -Ln
- 使用
ip addr
查看主节点的网络信息如下,可以看到192.168.31.89已经出现在网卡ip地址中
测试
扩展内容
LVS 负载均衡算法
静态算法
静态算法是根据LVS本身自由的固定的算法分发用户请求,静态算法主要有以下几种
- 轮询(Round Robin 简写’rr’):轮询算法假设所有的服务器处理请求的能力都一样的,调度器会把所有的请求平均分配给每个真实服务器。(同Nginx的轮询)
- 加权轮询(Weight Round Robin 简写’wrr’):安装权重比例分配用户请求。权重越高,被分配到处理的请求越多。(同Nginx的权重)
- 源地址散列(Source Hash 简写’sh’):同一个用户ip的请求,会由同一个RS来处理。(同Nginx的ip_hash)
- 目标地址散列(Destination Hash 简写’dh’):根据url的不同,请求到不同的RS。(同Nginx的url_hash)
动态算法
动态算法是根据流量的不同,或者服务器的压力不同来分配用户请求,动态算法主要有以下几种
- 最小连接数(Least Connections 简写’lc’):把新的连接请求分配到当前连接数最小的服务器
- 加权最少连接数(Weight Least Connections 简写’wlc’):服务器的处理性能用数值来代表,权重越大处理的请求越多。Real Server 有可能会存在性能上的差异,此时会动态获取不同服务器的负载状况,把请求分发到性能好并且比较空闲的服务器
- 最短期望延迟(Shortest Expected Delay 简写’sed’):特殊的wlc算法。举例阐述,假设有ABC三台服务器,权重分别为1、2、3 。如果使用wlc算法的策略,当请求进来,它可能会分给ABC中的任意一个。使用sed算法后会进行如下运算:
- A:(1+1)/1=2
- B:(1+2)/2=3/2
- C:(1+3)/3=4/3
最终结果,会把这个请求交给得出运算结果最小的服务器
- 最少队列调度(Never Queue 简写’nq’):永不使用队列。如果有Real Server的连接数等于0,则直接把这个请求分配过去,不需要在排队等待运算了
常见问题
为什么LVS设置了轮询,浏览器测试还是不能轮询?
这关系到两个地方的配置:
- /etc/keepalived/keepalived.conf的persistence_timeout会话保持时间配置,测试轮询时设置为0
- ipvsadm默认超时时间
查看ipvsmadm默认超时时间:
ipvsadm -L --timeout
Timeout (tcp tcpfin udp): 900 120 300
900 120 300 这三个数值分别是TCP TCPFIN UDP的时间,也就是说一条tcp的连接经过lvs后,lvs会把这台记录保存15分钟,就是因为这个时间过长,导致轮询现象并没有发生,可使用以下命令设置默认的超时时间:ipvsadm --set 1 2 1
再次测试轮询效果,就可以了!而实际配置中还是按照默认配置,那么在大量IP访问VIP时,就有轮询效果?有待验证