LVS(DR模式)+Keepalived+Nginx+web高可用集群

keepalived+lvs+nginx

在微服务项目中前段的负载均衡的稳定性是一个非常大的考验,这里我们使用keepalived+lvs+nginx完成我们前段的负载均衡

什么是高可用?

nginx做负载均衡,能达到分发请求的目的,但是不能很好的避免单点故障。

1、Nginx集群单点问题

  • 分发器宕机

    假如nginx服务器挂掉了,那么所有的服务也会跟着瘫痪 。
    一种方法是人为监控,发现主分发器宕机后,立马登录备分发器,并给它分配虚ip。
    另一种办法是用软件来替代人来监控,自动登录备分发器,分配虚ip。

  • 数据服务器宕机

    分发器可以自动判断数据服务器的存活状态,不对宕机服务器要数据。

2、keepalived介绍

Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使 其他服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。
总结来说:Keepalived软件是一个监控+自愈的软件。
运行协议是VRRP,主分发器的keepalived会向网络中发组播,宣告自己还活着,组播地址:224.0.0.18。

3、LVS

在keepalived+nginx的主备容灾高可用的架构中,nginx是作为外部访问系统的唯一入口,理论上一台nginx的最大并发量可以高达50000,但是当并发量更大的时候,keepalived+nginx的高可用机制是没办法满足需求的,因为keepalived+nginx的架构中确确实实是一台nginx在工作,只有当master宕机或异常的时候,备份机才会上位。那么如何解决更大的高并发问题呢,也许你会问能不能搭建nginx集群,直接对外提供访问?很显然这是欠妥当的,因为当nginx作为外部的唯一访问入口,没办法直接以集群的形式对外提供服务,没有那么多的公网ip资源可用,既太浪费也不友好。但是在内网环境下,是可以用nginx集群(nginx横向扩展服务集合)的,当然总得有一个对外入口,所以需要在nginx集群之上,在加一层负载均衡器LVS,作为系统的唯一入口。

环境准备

主机名 IP 作用
node1 192.168.31.145 虚拟IP(VIP)
node2(lvskeepalived1) 192.168.31.93 LVS+ Keepalived Master(LVS将请求负载至nginx1 或者 nginx2)
node3(lvskeepalived2) 192.168.31.91 LVS+ Keepalived Backup (lvskeepalived1的备机)
node4(nginx1) 192.168.31.165 Nginx1(将请求负载至后端web1 或者 web2)
node5(nginx2) 192.168.31.179 Nginx2(将请求负载至后端web1 或者 web2)
node6(web1) 192.168.31.97 Web1(静态web服务器)
node7(web2) 192.168.31.86 Web2(静态web服务器)

软件安装

1、在lvskeepalived1 和 lvskeepalived2 机器上面安装 ipvsadm 和 keepalived:

1
[root@node1 ~]# yum -y install ipvsadm keepalived

查看两台负载均衡机器是否支持lvs

1
2
3
4
5
6
7
8
9
10
11
[root@node1 ~]# lsmod |grep ip_vs

# 上面命令没有任何反应,执行下面命令打开ipvs即可
[root@node1 ~]# ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
[root@node1 ~]# lsmod |grep ip_vs
ip_vs 145458 0
nf_conntrack 139264 1 ip_vs
libcrc32c 12644 3 xfs,ip_vs,nf_conntrack

2、在Nginx1和Nginx2机器上安装Nginx

自行安装Nginx

3、在web1和web2上面安装Apache

1
yum -y install httpd

配置网页:

实际配置网页内容应该一样,但是为了演示nginx负载均衡效果,我们将网页内容一个配置成web1, 一个配置成web2。

1
2
3
4
5
6
7
8
9
#在web1配置如下:
cd /var/www/html
echo "web1" > index.html
systemctl restart httpd

#在web2配置如下:
cd /var/www/html
echo "web2" > index.html
systemctl restart httpd

4、配置keepalived

  • 在lvskeepalived1(master)上配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
[root@node2 ~]# vim /etc/keepalived/keepalived.conf

# Global Configuration
global_defs {
lvs_id director1 # 指定lvs的id
}

vrrp_instance LVS {
state MASTER # 指定当前节点为master节点
interface ens33 # 这里的ens33是网卡的名称,通过ifconfig或者ip addr可以查看
virtual_router_id 51 # 这里指定的是虚拟路由id,master节点和backup节点需要指定一样的
priority 151 # 指定了当前节点的优先级,数值越大优先级越高,master节点要高于backup节点
advert_int 1 # 指定发送VRRP通告的间隔,单位是秒
authentication {
auth_type PASS # 鉴权,默认通过
auth_pass 123456 # 鉴权访问密码
}

virtual_ipaddress {
192.168.31.145 # 指定了虚拟ip
}

}

# Virtual Server Configuration - for www server
# 后台真实主机的配置
virtual_server 192.168.31.145 80 {
delay_loop 1 # 健康检查的时间间隔
lb_algo rr # 负载均衡策略,这里是轮询
lb_kind DR # 调度器类型,这里是DR
persistence_time 1 # 指定了持续将请求打到同一台真实主机的时间长度
protocol TCP # 指定了访问后台真实主机的协议类型

# Real Server 1 configuration
# 指定了真实主机1的ip和端口
real_server 192.168.31.165 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}

# Real Server 2 Configuration
real_server 192.168.31.179 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}
}

  • 在lvskeepalived2(backup)上配置:

    lvs_id director1 改成 lvs_id director2
    state MASTER 改成 state BACKUP
    priority 151 改成 priority 101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
[root@node3 ~]# vim /etc/keepalived/keepalived.conf


# Global Configuration
global_defs {
lvs_id director2 # 指定lvs的id
}

# VRRP Configuration
vrrp_instance LVS {
state BACKUP # 指定当前节点为backup节点
interface ens33 # 这里的ens33是网卡的名称,通过ifconfig或者ip addr可以查看
virtual_router_id 101 # 这里指定的是虚拟路由id,master节点和backup节点需要指定一样的
priority 101 # 指定了当前节点的优先级,数值越大优先级越高,master节点要高于backup节点
advert_int 1 # 指定发送VRRP通告的间隔,单位是秒
authentication {
auth_type PASS # 鉴权,默认通过
auth_pass 123456 # 鉴权访问密码
}

virtual_ipaddress {
192.168.31.145 # 指定了虚拟ip
}

}

# Virtual Server Configuration - for www server
# 后台真实主机的配置
virtual_server 192.168.31.145 80 {
delay_loop 1 # 健康检查的时间间隔
lb_algo rr # 负载均衡策略,这里是轮询
lb_kind DR # 调度器类型,这里是DR
persistence_time 1 # 指定了持续将请求打到同一台真实主机的时间长度
protocol TCP # 指定了访问后台真实主机的协议类型

# Real Server 1 configuration
# 指定了真实主机1的ip和端口
real_server 192.168.31.165 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}

# Real Server 2 Configuration
real_server 192.168.31.179 80 {
weight 1 # 指定了当前主机的权重
TCP_CHECK {
connection_timeout 10 # 指定了进行心跳检查的超时时间
nb_get_retry 3 # 指定了心跳超时之后的重复次数
delay_before_retry 3 # 指定了在尝试之前延迟多长时间
}
}
}

5、配置Nginx

  • 在nginx1 和 nginx2 机器上面配置负载均衡:(两台机器一模一样的配置)

    实现将访问本机80端口的请求轮询定向到后端的两台apache服务器:

    我们这里只演示两台apache web 服务器, 你也可以配置多台。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
[root@node5 conf]# vim /usr/local/nginx/conf/nginx.conf


#user nobody;
worker_processes auto;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;

#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';

#access_log logs/access.log main;

sendfile on;
tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;

upstream serverList {
server 192.168.31.97:80; #web server1,后端真实服务器
server 192.168.31.86:80; #web server2,后端真实服务器
}

server {
listen 80;
#server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
# root html;
# index index.html index.htm;
proxy_pass http://serverList; # 指向负载的列表的模块,如serverList
proxy_redirect off;
proxy_set_header Host $host;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

  • 在nginx1 和 nginx2 机器上面配置虚拟IP: (两台nginx机器, 一模一样的配置)

    因为我们使用的lvs调度器是DR模式,前面我们讲到过,这种模式下,对客户端的响应是真实服务器直接返回给客户端的,而真实服务器需要将响应报文中的源ip修改为虚拟ip,这里配置的虚拟ip就是起这个作用的。我们编辑/etc/init.d/lvsrs文件,写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@node4 conf]# vim /etc/init.d/lvsrs

#虚拟的vip 根据自己的实际情况定义
SNS_VIP=192.168.31.145
/etc/rc.d/init.d/functions
case "$1" in
start)
ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP
/sbin/route add -host $SNS_VIP dev lo:0
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
sysctl -p >/dev/null 2>&1
echo "RealServer Start OK"
;;
stop)
ifconfig lo:0 down
route del $SNS_VIP >/dev/null 2>&1
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
echo "RealServer Stoped"
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
exit 0

然后执行脚本:(两台Nginx)

1
2
3
4
5
6
[root@node4 init.d]# chmod 755 lvsrs

[root@node4 init.d]# ./lvsrs start
./lvsrs:行4: /etc/rc.d/init.d/functions: 权限不够
SIOCADDRT: 文件已存在
RealServer Start OK

查看ip lo:0 和路由是否添加成功:

1
2
3
4
5
6
7
8
9
[root@node4 init.d]# ip a | grep -i lo:0
inet 192.168.31.145/32 brd 192.168.31.145 scope global lo:0

[root@node4 init.d]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.31.1 0.0.0.0 UG 100 0 0 ens33
192.168.31.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
192.168.31.145 0.0.0.0 255.255.255.255 UH 0 0 0 lo

如果可以看到上述结果,证明 lo:0和路由都已经配置成功。

测试

1、启动Nginx服务,验证Nginx负载功能

1
[root@node4 init.d]# /usr/local/nginx/sbin/nginx

测试分别访问nginx1和nginx2的网页, 可以看到请求轮询定向到后端的两台web服务器上, 所以Nginx 负载功能验证成功。

1
2
3
4
5
6
7
[root@node2 ~]# while [ TRUR ]; do curl http://192.168.31.165:80; sleep 2 ; done
web2
web2
web1
web2
web1
web2

2、启动keepalived

在lvskeepalived1 和 lvskeepalived2 服务器上启动keepalived.

1
2
systemctl start keepalived
systemctl enable keepalived

过几秒就可以看到VIP在master和backup机器上面

1
2
3
4
[root@node2 ~]# ip a | grep -i ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.168.31.93/24 brd 192.168.31.255 scope global noprefixroute dynamic ens33
inet 192.168.31.145/32 scope global ens33

通过VIP访问网页:

访问成功, 可见LVS 成功将请求转发到了后端web server 上。

3、keepalived高可用测试

  • 停掉主keepalived,备份keepalived还是可以运行的

4、LVS监控真实服务测试

  • 查看最新的虚拟ip对应的RealServer的情况
    VIP:192.168.31.145
    Realserver1 IP: 192.168.31.165
    Realserver2 IP: 192.168.31.179
1
2
3
4
5
6
7
[root@node2 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.31.145:80 rr
-> 192.168.31.165:80 Route 1 0 0
-> 192.168.31.179:80 Route 1 0 0

停掉其中一个Nginx也可以正常运行

注意:

1
2
3
4
# 开启端口
/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT
# 或者关闭防火墙
systemctl disable firewalld.service

docker方式

1
2
3
4
5
6
7
8
[root@VM-24-12-centos ~]# docker run -itd --name lvsKeepalived1 --privileged=true -p 101:22 centos:7
[root@VM-24-12-centos ~]# docker run -itd --name lvsKeepalived2 --privileged=true -p 102:22 centos:7

[root@VM-24-12-centos ~]# docker run --name nginx1 --privileged=true -d -p 201:80 nginx
[root@VM-24-12-centos ~]# docker run --name nginx2 --privileged=true -d -p 202:80 nginx

[root@VM-24-12-centos ~]# docker run -itd --name web1 --privileged=true -p 301:22 centos:7
[root@VM-24-12-centos ~]# docker run -itd --name web2 --privileged=true -p 302:22 centos:7

全部容器

1
2
3
4
5
6
7
8
9
10
11
12
[root@VM-24-12-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e34c86bb0435 centos:7 "/bin/bash" 7 seconds ago Up 6 seconds 0.0.0.0:302->22/tcp, :::302->22/tcp web2
08c818debfec centos:7 "/bin/bash" 17 seconds ago Up 16 seconds 0.0.0.0:301->22/tcp, :::301->22/tcp web1
719c03f59c58 nginx "/docker-entrypoint.…" 48 seconds ago Up 47 seconds 0.0.0.0:202->80/tcp, :::202->80/tcp nginx2
4411e0e5d7cb nginx "/docker-entrypoint.…" 58 seconds ago Up 57 seconds 0.0.0.0:201->80/tcp, :::201->80/tcp nginx1
495ee8a27741 centos:7 "/bin/bash" 13 minutes ago Up 13 minutes 0.0.0.0:102->22/tcp, :::102->22/tcp lvsKeepalived2
5c45ad280824 centos:7 "/bin/bash" 13 minutes ago Up 13 minutes 0.0.0.0:101->22/tcp, :::101->22/tcp lvsKeepalived1
9d54fbdb7e5f redis "docker-entrypoint.s…" 2 days ago Up 2 days 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp myredis
8f474f903dc2 mysql:5.7 "docker-entrypoint.s…" 6 days ago Up 6 days 33060/tcp, 0.0.0.0:3313->3306/tcp, :::3313->3306/tcp slave02
07abacc7cfa4 mysql:5.7 "docker-entrypoint.s…" 6 days ago Up 6 days 33060/tcp, 0.0.0.0:3312->3306/tcp, :::3312->3306/tcp slave
20a63bf1a35a mysql:5.7 "docker-entrypoint.s…" 6 days ago Up 6 days 33060/tcp, 0.0.0.0:3311->3306/tcp, :::3311->3306/tcp master

ip地址情况

1
2
3
4
5
6
172.17.0.5   # nginx1
172.17.0.6 # nginx2
172.17.0.7 # web1
172.17.0.8 # web1
172.17.0.9 # lvsKeepalived1
172.17.0.10 # lvsKeepalived2

配置lvskeepalived时,宿主机要开启ipvsadm,否则容器内会报错