前言

Nginx和Apache都可以用来作为反向代理服务器,来提供负载均衡的能力,使我们的web服务器,能够水平扩容,从而处理更多的用户请求,但是反向代理服务器又变成了一个单点,当反向代理服务器挂了,整合Web服务器就不能被外界访问到,所以我们必须要保证反向代理服务器的高可用。

下面我们来演示下如何使用Keepalived,搭建高可用的Web服务,我们选择使用Nginx作为反向代理服务器。

Keepalived 简要介绍

Keepalived 是一种高性能的服务器高可用或热备解决方案,Keepalived 可以用来防止服务器单点故障的发生,通过配合 Nginx 可以实现 web 前端服务的高可用。

这种Nginx+keepalived高可用架构一般有2种模式:
1、主备模式
使用一个VIP(虚拟IP)地址,前端使用2台机器,一台做主,一台做备,但同时只有一台机器工作,另一台备份机器在主机器不出现故障的时候,永远处于浪费状态,对于服务器不多的网站,该方案不经济实惠。

2、双主模式
使用两个VIP(虚拟IP)地址,前端使用2台机器,互为主备,同时有两台机器工作,当其中一台机器出现故障,两台机器的请求转移到一台机器负担,非常适合于当前架构环境。

Keepalived 以 VRRP 协议为实现基础,用 VRRP 协议来实现高可用性(HA)。VRRP(Virtual Router Redundancy Protocol)协议是用于实现路由器冗余的协议,VRRP 协议将两台或多台路由器设备虚拟成一个设备,对外提供虚拟路由器 IP(一个或多个),而在路由器组内部,如果实际拥有这个对外 IP 的路由器如果工作正常的话就是 MASTER, 或者是通过算法选举产生, MASTER 实现针对虚拟路由器 IP 的各种网络功能,如 ARP 请求,ICMP,以及数据的转发等;其他设备不拥有该虚拟 IP,状态是 BACKUP,除了接收 MASTER 的VRRP 状态通告信息外,不执行对外的网络功能。当主机失效时,BACKUP 将接管原先 MASTER 的网络功能。
VRRP 协议使用多播数据来传输 VRRP 数据, VRRP 数据使用特殊的虚拟源 MAC 地址发送数据而不是自身网卡的 MAC 地址,VRRP 运行时只有 MASTER 路由器定时发送 VRRP 通告信息,表示 MASTER 工作正常以及虚拟路由器 IP(组),BACKUP 只接收 VRRP 数据,不发送数据,如果一定时间内没有接收到 MASTER 的通告信息,各 BACKUP 将宣告自己成为 MASTER,发送通告信息,重新进行 MASTER 选举状态。

本篇博文将主要演示Keepalived+Nginx的主备模式

环境准备

Ubuntu 16.04 LTS
Keepalived v1.2.19 (03/13,2017)
Niginx nginx/1.10.3
Tomcat v8.0

准备4台虚拟机,两台Nginx和两台Tomcat,另外一个VIP(虚拟IP),192.168.224.150

虚拟机

IP

说明

Nginx1+Keepalived (Master)

192.168.224.101

Nginx Server 1

Nginx2+Keepalived (Backup)

192.168.224.102

Nginx Server 2

Tomcat1

192.168.224.103

Tomcat Web Server 1

Tomcat2

192.168.224.104

Tomcat Web Server 2

安装tomcat

在192.168.224.103,192.168.224.104上安装tomcat,并修改ROOT/index.jsp页面,在页面中加入tomcat的IP地址,并打印出request header中的X-NGINX值,这个值,我们将在后面配置NGINX时传入,如192.168.224.103上我们修改为:

<div id="asf-box">
    <h1>${pageContext.servletContext.serverInfo}(192.168.224.103)<%=request.getHeader("X-NGINX")%></h1>
</div>

启动103和104两台机器的tomcat,确认浏览器能正确访问http://192.168.224.103:8080http://192.168.224.104:8080

并且页面上能正确显示各自的IP地址,此时request header里没有X-NGINX,所以显示null。

安装nginx

安装gcc g++的依赖库

apt-get install build-essential
apt-get install libtool

安装 pcre依赖库

sudo apt-get update
sudo apt-get install libpcre3 libpcre3-dev

安装 zlib依赖库

apt-get install zlib1g-dev

安装 ssl依赖库

apt-get install openssl

安装nginx,并启动

apt-get install nginx
nignx

浏览器中访问http://192.168.224.101

看到如下界面,则nginx配置成功

配置nginx反向代理

cat /etc/nginx/nginx.conf
查看nignx配置,默认会加载/etc/nginx/conf.d下所有*.conf文件,和/etc/nginx/sites-enabled下所有文件

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

移除/etc/nginx/sites-enabled目录下的default配置,并在目录下创建tomcat.conf

cd /etc/nginx/sites-enabled
rm default
touch tomcat.conf
vi tomcat.conf

在tomcat.conf中,配置nginx的反向代理,将请求转发到103和104两台tomcat上。
为了能在tomcat页面上看出是哪台nginx转发过来的请求,我们这里给两台nginx分别配置两个自定义的header传给tomcat,tomcat将其取出打印在页面上。
在Master(101)中设置proxy_set_header X-NGINX “NGINX-1”

upstream tomcat {
    server 192.168.224.103:8080 weight=1;
    server 192.168.224.104:8080 weight=1;
}
server{
    location / {
        proxy_pass http://tomcat;
    proxy_set_header X-NGINX "NGINX-1";
    }
    #......其他省略
}

在Backup(102)中设置proxy_set_header X-NGINX “NGINX-2”

upstream tomcat {
    server 192.168.224.103:8080 weight=1;
    server 192.168.224.104:8080 weight=1;
}
server{
    location / {
        proxy_pass http://tomcat;
    proxy_set_header X-NGINX "NGINX-2";
    }
    #......其他省略
}

执行reload命令,使配置生效

nginx -s reload

按照上面方法在Master(101)上安装和配置好nginx,浏览器地址栏输入http://192.168.224.101

多次刷新页面,能看到页面上显式IP地址信息,192.168.224.103和192.168.224.104交替显式,说明nginx已经将用户请求负载均衡到了2台tomcat上

并且能看到NGINX-1也能显示在页面上,如下图

同理在Backup(102)上安装和配置好nginx,浏览器地址栏输入http://192.168.224.102

多次刷新页面,能看到页面上显式IP地址信息,192.168.224.103和192.168.224.104交替显式,说明nginx已经将用户请求负载均衡到了2台tomcat上

并且能看到NGINX-2也能显示在页面上,如下图

安装Keepalived

apt-get install keepalived

在/etc/keepalived目录下,添加check_nginx.sh(检查nginx存活的shell脚本)和keepalived.conf(keepalived配置文件)

check_nginx.sh

#!/bin/bash  
#代码一定注意空格,逻辑就是:如果nginx进程不存在则启动nginx,如果nginx无法启动则kill掉keepalived所有进程  
A=`ps -C nginx --no-header |wc -l`  
if [ $A -eq 0 ];then  
 /etc/init.d/nginx start  
 sleep 3  
 if [ `ps -C nginx --no-header |wc -l`-eq 0 ];then  
  killall keepalived  
 fi  
fi

Master(101)中的keepalived.conf配置如下

vrrp_script chk_nginx {  
 script "/etc/keepalived/check_nginx.sh" //检测nginx进程的脚本  
 interval 2  
 weight -20  
}  

global_defs {  
 notification_email {  
      //可以添加邮件提醒  
 }  
}  
vrrp_instance VI_1 {  
 state MASTER //主服务器  
 interface ens33  
 virtual_router_id 51   
 mcast_src_ip 192.168.224.101  
 priority 250  
 advert_int 1  

 authentication {  
        auth_type PASS  
        auth_pass 123456  
 }  
 track_script {  
        chk_nginx  
 }  
 virtual_ipaddress {  
        192.168.224.150  
 }  
}

Backup(102)中的keepalived.conf配置如下

vrrp_script chk_nginx {  
 script "/etc/keepalived/check_nginx.sh" //检测nginx进程的脚本  
 interval 2  
 weight -20  
}  

global_defs {  
 notification_email {  
      //可以添加邮件提醒  
 }  
}  
vrrp_instance VI_1 {  
 state BACKUP //从服务器  
 interface ens33  
 virtual_router_id 51   
 mcast_src_ip 192.168.224.102  
 priority 240  
 advert_int 1  

 authentication {  
        auth_type PASS  
        auth_pass 123456  
 }  
 track_script {  
        chk_nginx  
 }  
 virtual_ipaddress {  
        192.168.224.150  
 }  
}

关于keepalived配置的几点说明
- state - 主服务器需配成MASTER,从服务器需配成BACKUP
- interface - 这个是网卡名,我使用的是VM12.0的版本,所以这里网卡名为ens33
- mcast_src_ip - 配置各自的实际IP地址
- priority - 主服务器的优先级必须比从服务器的高,这里主服务器配置成250,从服务器配置成240
- virtual_ipaddress - 配置虚拟IP(192.168.224.150)
- authentication - auth_pass主从服务器必须一致,keepalived靠这个来通信
- virtual_router_id - 主从服务器必须保持一致

Step-1 Master,Backup都正常,只有Master对外提供服务

配置完成后,先启动Master(101)机器的keepalived,并查看启动log

root@ubuntu:/etc/keepalived# /etc/init.d/keepalived start
[ ok ] Starting keepalived (via systemctl): keepalived.service.
root@ubuntu:/etc/keepalived# tail /var/log/syslog
Nov 21 00:46:38 ubuntu Keepalived_vrrp[1296]: Configuration is using : 68579 Bytes
Nov 21 00:46:38 ubuntu Keepalived_vrrp[1296]: Using LinkWatch kernel netlink reflector...
Nov 21 00:46:38 ubuntu Keepalived_healthcheckers[1295]: Initializing ipvs 2.6
Nov 21 00:46:38 ubuntu Keepalived_healthcheckers[1295]: Registering Kernel netlink reflector
Nov 21 00:46:38 ubuntu Keepalived_healthcheckers[1295]: Registering Kernel netlink command channel
Nov 21 00:46:38 ubuntu Keepalived_healthcheckers[1295]: Opening file '/etc/keepalived/keepalived.conf'.
Nov 21 00:46:38 ubuntu Keepalived_healthcheckers[1295]: Configuration is using : 11193 Bytes
Nov 21 00:46:38 ubuntu Keepalived_healthcheckers[1295]: Using LinkWatch kernel netlink reflector...
Nov 21 00:46:39 ubuntu Keepalived_vrrp[1296]: VRRP_Instance(VI_1) Transition to MASTER STATE
Nov 21 00:46:40 ubuntu Keepalived_vrrp[1296]: VRRP_Instance(VI_1) Entering MASTER STATE

查看keepalived和nginx进程,确保keepalived和nginx启动正常

root@ubuntu:/etc/keepalived# ps -ef | grep keepalived
root       1293      1  0 00:46 ?        00:00:00 /usr/sbin/keepalived
root       1295   1293  0 00:46 ?        00:00:00 /usr/sbin/keepalived
root       1296   1293  0 00:46 ?        00:00:00 /usr/sbin/keepalived
root       1377   1121  0 00:47 pts/0    00:00:00 grep --color=auto keepalived
root@ubuntu:/etc/keepalived# ps -ef | grep nginx     
root       1365      1  0 00:47 ?        00:00:00 nginx: master process nginx
www-data   1366   1365  0 00:47 ?        00:00:00 nginx: worker process
www-data   1367   1365  0 00:47 ?        00:00:00 nginx: worker process
root       1381   1121  0 00:47 pts/0    00:00:00 grep --color=auto nginx

使用ip add命令,查看VIP是否被绑定到101机器上,可以看到192.168.224.150/32 scope global ens33已经被绑定到101机器上了

root@ubuntu:/etc/keepalived# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:46:80:15 brd ff:ff:ff:ff:ff:ff
    inet 192.168.224.101/24 brd 192.168.224.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet 192.168.224.150/32 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe46:8015/64 scope link 
       valid_lft forever preferred_lft forever

再启动Backup(102)机器的keepalived和nginx,确保其正常启动,查看Backup的IP信息,发现VIP现在还没有绑定到Backup(102)上。

浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-1,则表明是Master(101)在转发web请求

Step-2 Master挂了,Backup接替Master对外提供服务

接着,我们在Master(101)机器上关闭keepalived和nginx进程来模拟Master服务器挂掉,并查看VIP是否还在Master机器上

root@ubuntu:/etc/keepalived# killall keepalived 
root@ubuntu:/etc/keepalived# killall nginx 
root@ubuntu:/etc/keepalived# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:46:80:15 brd ff:ff:ff:ff:ff:ff
    inet 192.168.224.101/24 brd 192.168.224.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe46:8015/64 scope link 
       valid_lft forever preferred_lft forever

查看Backup(102)的VIP,发现VIP已经绑定到了Backup(102)

root@ubuntu:/etc/keepalived# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:8c:eb:5c brd ff:ff:ff:ff:ff:ff
    inet 192.168.224.102/24 brd 192.168.224.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet 192.168.224.150/32 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe8c:eb5c/64 scope link 
       valid_lft forever preferred_lft forever

浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-2,则表明是Backup(102)在转发web请求,也就是说Master挂掉后,Backup继续接管Master的服务。

Step-3 Master恢复正常后,Master继续提供服务,Backup停止服务,并继续等待Master出现故障

我们再启动Master(101)机器的keepalived和nginx,查看VIP,发现VIP已经被Master“夺回”了使用权限
浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-1,则表明是Master(101)在转发web请求,也就是说Master重新启动后,Master重新对外提供服务,Backup则停止服务继续等待Master挂掉。

Keepalived抢占模式和非抢占模式

keepalived的HA分为抢占模式和非抢占模式,抢占模式即MASTER从故障中恢复后,会将VIP从BACKUP节点中抢占过来。非抢占模式即MASTER恢复后不抢占BACKUP升级为MASTER后的VIP。
前面的例子中,我们实际上配置的是抢占模式,下面我们再来看看非抢占模式

Master(101)中的keepalived.conf配置如下

vrrp_script chk_nginx {  
 script "/etc/keepalived/check_nginx.sh" //检测nginx进程的脚本  
 interval 2  
 weight -20  
}  

global_defs {  
 notification_email {  
      //可以添加邮件提醒  
 }  
}  
vrrp_instance VI_1 {  
 state BACKUP //主服务器(非抢占模式需要配置成BACKUP)  
 interface ens33  
 virtual_router_id 51   
 mcast_src_ip 192.168.224.101  
 priority 250  
 advert_int 1  
 nopreempt //非抢占模式
 authentication {  
        auth_type PASS  
        auth_pass 123456  
 }  
 track_script {  
        chk_nginx  
 }  
 virtual_ipaddress {  
        192.168.224.150  
 }  
}

Backup(102)中的keepalived.conf配置如下

vrrp_script chk_nginx {  
 script "/etc/keepalived/check_nginx.sh" //检测nginx进程的脚本  
 interval 2  
 weight -20  
}  

global_defs {  
 notification_email {  
      //可以添加邮件提醒  
 }  
}  
vrrp_instance VI_1 {  
 state BACKUP //从服务器  
 interface ens33  
 virtual_router_id 51   
 mcast_src_ip 192.168.224.102  
 priority 240  
 advert_int 1  
 nopreempt //非抢占模式
 authentication {  
        auth_type PASS  
        auth_pass 123456  
 }  
 track_script {  
        chk_nginx  
 }  
 virtual_ipaddress {  
        192.168.224.150  
 }  
}

抢占模式配置说明
和非抢占模式的配置相比,只改了两个地方:
1> 在vrrp_instance块下两个节点各增加了nopreempt指令,表示不争抢vip
2> 节点的state都为BACKUP
两个keepalived节点都启动后,默认都是BACKUP状态,双方在发送组播信息后,会根据优先级来选举一个MASTER出来。由于两者都配置了nopreempt,所以MASTER从故障中恢复后,不会抢占vip。这样会避免VIP切换可能造成的服务延迟。

(非抢占模式)Step-1 Master,Backup都正常,只有Master对外提供服务

配置完成后,先启动Master(101)机器的keepalived和nginx,查看Master的IP信息,可以看到VIP已经被绑定到101机器上了,再启动Backup(102)机器的keepalived和nginx,查看Backup的IP信息,可以看到VIP没有被绑定到102机器上了
浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-1,则表明是Master(101)在转发web请求

(非抢占模式)Step-2 Master挂了,Backup接替Master对外提供服务

接着,我们在Master(101)机器上关闭keepalived和nginx进程来模拟Master服务器挂掉,查看Backup(102)的VIP,发现VIP已经绑定到了Backup(102)
浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-2,则表明是Backup(102)在转发web请求,也就是说Master挂掉后,Backup继续接管Master的服务。

(非抢占模式)Step-3 Master恢复正常后,Backup继续对外提供服务,Master不会抢占VIP,而是继续等待Backup出现故障

我们再启动Master(101)机器的keepalived和nginx,查看VIP,发现VIP已经被Master“夺回”了使用权限
浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-2,则表明是Backup(102)在转发web请求,也就是说Master恢复后,并未接管Backup的服务。

(非抢占模式)Step-4 Backup挂了,Master重新绑定VIP,接替Backup对外提供服务

我们在Backup(102)机器上关闭keepalived和nginx进程来模拟Backup服务器挂掉,查看Master(101)的VIP,发现VIP已经绑定到了Master(101)
浏览器多次刷新并访问http://192.168.224.150/
可以看到页面上IP交替显式103和104,并且显示NGINX-1,则表明是Master(101)在转发web请求