前言
利用 Nginx 的负载均衡和反向代理,我们可以实现后台应用的高可用。这时候的用户请求是先通过 nginx, 然后在访问我们的应用,如果 nginx 挂掉了,那我们所有的应用都无法访问了。所以我们必须实现 Nginx 的高可用,达到一台 Nginx 宕机,另一台备机自动接管服务的效果。
本文没有直接使用 nginx ,而是使用了 OpenResty 。其实效果是一样的。 OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台。
Keepalived 介绍
Keepalived是一个基于VRRP协议来实现的服务高可用方案,可以利用其来避免IP单点故障,类似的工具还有heartbeat、corosync、pacemaker。但是它一般不会单独出现,而是与其它负载均衡技术(如lvs、haproxy、nginx)一起工作来达到集群的高可用。
VRRP协议
VRRP全称 Virtual Router Redundancy Protocol,即 虚拟路由冗余协议。可以认为它是实现路由器高可用的容错协议,即将N台提供相同功能的路由器组成一个路由器组(Router Group),这个组里面有一个master和多个backup,但在外界看来就像一台一样,构成虚拟路由器,拥有一个虚拟IP(vip,也就是路由器所在局域网内其他机器的默认路由),占有这个IP的master实际负责ARP相应和转发IP数据包,组中的其它路由器作为备份的角色处于待命状态。master会发组播消息,当backup在超时时间内收不到vrrp包时就认为master宕掉了,这时就需要根据VRRP的优先级来选举一个backup当master,保证路由器的高可用。
在VRRP协议实现里,虚拟路由器使用 00-00-5E-00-01-XX 作为虚拟MAC地址,XX就是唯一的 VRID (Virtual Router IDentifier),这个地址同一时间只有一个物理路由器占用。在虚拟路由器里面的物理路由器组里面通过多播IP地址 224.0.0.18 来定时发送通告消息。每个Router都有一个 1-255 之间的优先级别,级别最高的(highest priority)将成为主控(master)路由器。通过降低master的优先权可以让处于backup状态的路由器抢占(pro-empt)主路由器的状态,两个backup优先级相同的IP地址较大者为master,接管虚拟IP。
VRRP协议 + nginx
keepalived可以认为是VRRP协议在Linux上的实现,主要有三个模块,分别是core、check和vrrp。core模块为keepalived的核心,负责主进程的启动、维护以及全局配置文件的加载和解析。check负责健康检查,包括常见的各种检查方式。vrrp模块是来实现VRRP协议的。
Keepalived 实现 OpenResty 的高可用
准备
笔者准备了二台服务器,其 ip 地址为:
192.168.56.101 worker-01 worker-01.joyxj.com
192.168.56.102 worker-02 worker-02.joyxj.com
系统为 Centos 7。
我们需要在这二台服务器上 安装 OpenResty 和 Keepalived 。其中 192.168.56.101 为 主机,192.168.56.102 为备机。
OpenResty 安装
可以参考 OpenResty 快速入门。
其安装后的目录在 /usr/local/openresty
, nginx 目录在 /usr/local/openresty/nginx
。
Keepliaved 安装
直接使用 yum 进行安装
yum -y install kernel kernel-devel* popt popt-devel libssl-dev libnl libnl-devel openssl openssl-* ipvsadm libnfnetlink-devel
yum install -y keepalived
安装后使用 keepalived -v
查看版本,会输出以下信息则表示安装成功。
Keepalived v1.3.5 (03/19,2017), git commit v1.3.5-6-g6fa32f2
Copyright(C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
监控脚本
需要一个脚本,用于监测 Nginx 是否运行,如果 nginx 已经宕机,需要把相应机器的 Keepalived 已关掉,才能实现虚 IP 的飘移。如果不关掉 Keepalived,是不会进行虚 IP 的飘移。
vim /etc/keepalived/check_nginx.sh
#!/bin/bash
# curl -IL http://localhost/member/login.htm
# curl --data "memberName=fengkan&password=22" http://localhost/member/login.htm
count=0
for (( k=0; k<2; k++ ))
do
# 通过 curl 的方式看可否可以访问 Nginx 的首页
check_code=$( curl --connect-timeout 3 -sL -w "%{http_code}\\n" http://localhost/index.html -o /dev/null )
if [ "$check_code" != "200" ]; then
count=$(expr $count + 1)
sleep 3
continue
else
count=0
break
fi
done
if [ "$count" != "0" ]; then
/etc/init.d/keepalived stop
exit 1
else
exit 0
fi
Keepalived 配置文件 keepalived.conf
我们需要对 /etc/keepalived/keepalived.conf
做一些修改。
vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 192.168.200.1
smtp_connect_timeout 30
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
# vrrp_strict 需要注释掉,否则会无法 ping 通虚 IP。
# vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_script chk_nginx {
# script "killall -0 nginx"
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -5
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface enp0s8 #使用的网卡,这个要和 IP 相对应的网卡一致
virtual_router_id 51
priority 101
advert_int 1
mcast_src_ip 192.168.56.101 # ip
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.56.106 # 虚拟出来的 IP
}
track_script {
chk_nginx
}
}
... 省略其它默认的配置 ...
在另外一台备机上,只需要改变 state MASTER -> state BACKUP,priority 101 -> priority 100,mcast_src_ip 192.168.56.101 -> mcast_src_ip 192.168.56.102 即可。
启动
分别在二台机器上启动 Nginx 和 Keepalived。
/usr/local/openresty/nginx/sbin/nginx
systemctrl start keepalived
可以通过 /var/log/message
查看日志。
或者通过 ps
命令查看进程查看 nginx 和 keepalived 是否已经启动。
ps -ef | grep nginx
ps -ef | grep keepalived
root 6973 1 0 15:22 ? 00:00:00 /usr/sbin/keepalived -D
root 6974 6973 0 15:22 ? 00:00:01 /usr/sbin/keepalived -D
root 6975 6973 0 15:22 ? 00:00:01 /usr/sbin/keepalived -D
root 23980 6975 0 16:22 ? 00:00:00 /usr/sbin/keepalived -D
root 23981 23980 0 16:22 ? 00:00:00 /bin/bash /etc/keepalived/check_nginx.sh
root 23987 3587 0 16:22 pts/0 00:00:00 grep --color=auto keepalived
使用 ip a
命令可以查看 ip 情况。
ip a
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:71:46:c1 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.101/24 brd 192.168.56.255 scope global noprefixroute enp0s8
valid_lft forever preferred_lft forever
inet 192.168.56.106/32 scope global enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::23e9:cd08:cf12:aa6c/64 scope link tentative noprefixroute dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::3725:99a9:e2e2:b667/64 scope link noprefixroute
valid_lft forever preferred_lft forever
从中,可以看到已经多了一个 IP 192.168.56.106
。这个 IP 就是 keepalived 虚拟出来的,通过在 keepalived.conf
文件中配置。
高可用测试
为了方面测试,我们对 nginx 的首页进行一些修改,增加一些标识。
直接通过服务器的 IP 进行访问。
curl http://192.168.56.101
... 省略 ...
<h1>Welcome to OpenResty! worker-01</h1>
... 省略 ...
curl http://192.168.56.102
... 省略 ...
<h1>Welcome to OpenResty! worker-02</h1>
... 省略 ...
其中 worker-01 和 worker-02 是笔者加的标识,分别标识这二台服务器。其它的输出内容省略。
下面我们通过虚拟出来的 IP 192.168.56.106
进行访问。
curl http://192.168.56.106
... 省略 ...
<h1>Welcome to OpenResty! worker-01</h1>
... 省略 ...
我们可以看到,由于 192.168.56.101
这台服务器被我们设置为 master ,所以 192.168.56.106
这个虚拟 IP 会漂移到 192.168.56.101
这台机器上。现在我们把 192.168.56.101
这台机器的 nginx 停掉,由于我们有一个监控脚本,Nginx 停掉后, Keepalived 也会被停掉。
# 停掉 nginx
/usr/local/openresty/nginx/sbin/nginx -s stop
重新访问 虚拟 IP。
curl http://192.168.56.106
... 省略 ...
<h1>Welcome to OpenResty! worker-02</h1>
... 省略 ...
此时,已经访问 192.168.56.102
这台服务器,实现了切换。
可以通过 ip a
命令查看 ip 信息,发现 192.168.56.106
这个 IP 已经在 192.168.56.102
这台服务器上了。
ip a
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:72:75:f8 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.102/24 brd 192.168.56.255 scope global noprefixroute enp0s8
valid_lft forever preferred_lft forever
inet 192.168.56.106/32 scope global enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::23e9:cd08:cf12:aa6c/64 scope link noprefixroute
valid_lft forever preferred_lft forever
这样,我们就实现了 OpenResty 的高可用。