Keepalived nginx HA负载均衡

一、开始

1.1 keepalived HA模式

keepalived的HA分为抢占模式和非抢占模式,抢占模式即MASTER从故障中恢复后,会将VIP从BACKUP节点中抢占过来。非抢占模式即MASTER恢复后不抢占BACKUP升级为MASTER后的VIP。本例主要介绍抢占模式。

1.2 方案规划

virtual_server(vip)

real_server(ip)

MASTER/BACKUP

服务端

192.168.177.100

192.168.177.181

MASTER

192.168.176

192.168.177.100

192.168.177.191

BACKUP

192.168.167

 分别在两台real_server安装keepalived和nginx,通过keepalive保证nginx的高可用。请求流程如下:客户端发起请求到vip,如果master存活,就通过master的nginx负载分发到服务端;如果master节点挂掉,则keepalived会将vip漂移到backup,此时backup的nginx会将请求负载分发到服务端。如果master完成了故障处理,恢复服务,那么会将vip抢占回来,而客户端无需关心具体是那一台nginx server对请求进行了分发,实现对服务器的解耦(本例nginx负载均衡策略为最少连接)。

1.3 环境介绍

服务器版本:CentOS Linux release 7.6.1810 (Core)

keepalived版本:keepalived-1.3.5-16.el7.x86_64

nginx版本:nginx-1.18.0-1.el7.ngx.x86_64

1.4 环境准备

1.4.1 防火墙

防火墙添加arrp组播规则,或关闭防火墙。本例关闭防火墙:

# 查看防火墙状态
firewall-cmd --state
# 停止firewall
systemctl stop firewalld.service
# 禁止firewall开机启动
systemctl disable firewalld.service

1.4.2 关闭selinux

如果没有关闭可能会导致keepalived.server或nginx.service启动失败。将SELINUX= enforcing改为SELINUX=disabled。

vi /etc/sysconfig/selinux

SELINUX=disabled

1.5 安装Nginx(有网络)

1.5.1 配置yum存储库

1>创建文件
vi /etc/yum.repos.d/nginx.repo
2>插入如下信息(指定下载地址,本机系统及系统版本)
[nginx]
name=nginx repo  
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

1.5.2 下载安装

yum install nginx -y

1.5.3 查看安装的nginx版本

nginx -v

1.6安装Keepalived

1.6.1  下载安装

yum install keepalived -y

1.7.2  启动并设置开机自启

systemctl start keepalived.service
systemctl enable keepalived.service

二、修改配置

2.1 keepalived配置

2.11 编辑/etc/keepalived/keepalived.conf配置文件

1> MASTER(192.168.177.181):

! Configuration File for keepalived

global_defs {
   notification_email {
     # 邮件通知
     root@localhost
   }
   # 指定发件人
   notification_email_from Alexandre.Cassen@firewall.loc
   # 指定smtp服务器地址
   smtp_server 127.0.0.1
   # 指定smtp连接超时时间
   smtp_connect_timeout 30
   # 此处注意router_id为负载均衡标识,在局域网内应该是唯一的,通常为hostname
   router_id LVS_DEVEL_181
}

# keepalived会定时执行脚本并对脚本执行的结果进行分析,动态调整vrrp_instance的优先级。
# 如果脚本执行结果为0,并且weight配置的值大于0,则优先级相应的增加。如果脚本执行结果非0,
# 并且weight配置的值小于 0,则优先级相应的减少。其他情况,维持原本配置的优先级,即配置文件中priority对应的值。
vrrp_script chk_nginx {
       script "/etc/keepalived/nginx_check.sh"
       # 每2秒检测一次nginx的运行状态
       interval 2
       # 失败一次,将自己的优先级-20
       weight -20
}

# 虚拟路由的标识符
vrrp_instance VI_1 {
    # 状态只有MASTER和BACKUP两种,并且要大写,MASTER为工作状态,BACKUP是备用状态
    state MASTER
    # 通信所使用的网络接口,可用ifconfig查看
    interface ens32
    # 虚拟路由的ID号,是虚拟路由MAC的最后一位地址
    virtual_router_id 51
    # 指定发送组播数据包的源IP地址。默认是绑定VRRP实例的接口的主IP地址
    mcast_src_ip 192.168.177.181
    # 此节点的优先级,主节点的优先级需要比其他节点高
    priority 100
    # 通告的间隔时间
    advert_int 1
    # 认证配置
    authentication {
        # 认证方式
        auth_type PASS
        # 认证密码
        auth_pass 1111  
    }
# 虚拟IP,两个节点设置必须一样。可以设置多个,一行写一个 
# 虚拟ip地址,可以有多个地址,每个地址占一行,不需要子网掩码,同时这个ip 必须与我们在lvs 客户端设定的vip 相一致!                   
    virtual_ipaddress {
        192.168.177.100
    }
    track_script {
       # nginx存活状态检测脚本
       chk_nginx
    }
}

# 集群所使用的VIP和端口
virtual_server 192.168.177.100 443 {
    # 健康检查间隔,单位为秒
    delay_loop 6
    # lvs调度算法rr|wrr|lc|wlc|lblc|sh|dh
    lb_algo rr
    # 负载均衡转发规则。一般包括DR,NAT,TUN 3种
    lb_kind NAT
    # 会话保持时间,会话保持,就是把用户请求转发给同一个服务器,不然刚在1上提交完帐号密码,就跳转到另一台服务器2上了
    persistence_timeout 50
    # 转发协议,有TCP和UDP两种,一般用TCP
    protocol TCP
    
    # 真实服务器,包括IP和端口号
    real_server 192.168.177.181 443 {
    # 默认为1,0为失效
        weight 1
    # 通过tcpcheck判断RealServer的健康状态
    TCP_CHECK {
            # 连接超时时间
            connect_timeout 3
            # 重连次数
            nb_get_retry 3
            # 重连间隔时间
            delay_before_retry 3
            # 健康检查的端口
            connect_port 23
        }
   }
}

2> BACKUP(192.168.177.191)

global_defs {
   # 此处注意router_id为负载均衡标识,在局域网内应该是唯一的
   router_id LVS_DEVEL_191 
}

vrrp_script chk_nginx {
    script "/etc/keepalived/nginx_check.sh"
    interval 2
    weight -20
}

vrrp_instance VI_1 {
    # 与master不同,备份节点为BACKUP
    state BACKUP
    # 根据实际配置 ifconfig查看
    interface ens33 
    virtual_router_id 51
    # 备,本机ip
    mcast_src_ip 192.168.177.191
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        # VIP 要同master 一致
        192.168.177.100
    }

    track_script {
       chk_nginx
    }
}

virtual_server 192.168.177.100 443 {
    delay_loop 6       
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP
    # 真实服务器,包括IP和端口号
    real_server 192.168.177.191 443 {
        weight 1
        
    TCP_CHECK {
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
            connect_port 23
        }
   }
}

2.1.2 创建nginx服务检测脚本

分别在主、备服务器/etc/keepalived目录下创建nginx_check.sh脚本,并为其添加执行权限chmod +x /etc/keepalived/nginx_check.sh。用于keepalived定时检测nginx的服务状态,如果nginx停止了,会尝试重新启动nginx,如果启动失败,会将keepalived进程杀死,将vip漂移到备份机器上。

#!/bin/bash
#监测心跳脚本
#查看nginx是否启动,如果没启动则启动,如果启动不起来,停掉keepalived服务,此时心跳断掉,服务转向另一个nginx
counter=$(ps -C nginx --no-heading|wc -l)
echo "${counter}"
if [ "${counter}" = "0" ]; then
    echo "即将启动nginx"
    service nginx start ##/usr/sbin/nginx #尝试重新启动nginx
    sleep 2 #睡眠2秒

    counter=$(ps -C nginx --no-heading|wc -l)
    if [ "${counter}" = "0" ]; then
        service keepalived stop # killall keepalived #启动失败,将keepalived服务杀死。将vip漂移到其它备份节点
        echo "nginx启动失败,将vip漂移到其它备份节点"
    else
        echo "nginx启动成功"
    fi
fi

2.1.3 启动keepalived服务

service keepalived start

查看keepalived是否启动成功

ps -ef | grep keepalived

查看nginx是否启动成功(如果启动失败检查nginx检测脚本,或keepalived配置文件中的检测脚本路径是否正确,检查是否多了或少了花括号)

ps -ef | grep nginx

如果看到如下进程信息,表示keepalived已经启动成功:

下面用ip add命令查看vip绑定的情况,如下图所示:

ip addr

从上图可以看出,vip地址192.168.177.100绑定在MASTER(192.168.177.181)的ens32网卡上。

2.1.4 测试故障转移

将MASTER(192.168.177.181)上的keepalived停止,查看vip是否会漂移到192.168.177.191上。

service keepalived stop
ip addr

 

 

从上图可以看出,vip已经成功从181漂移到了191。此时再将181的keepalived服务启动,由于181是MASTER,所以会将191的VIP抢占过来。

启动181的keepalived服务:

service keepalived start

2.2 nginx配置

2.1.1 nginx.conf

使用安装默认配置,不做任何修改。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/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  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;
    
    # 指定自定义配置文件目录、名称(esb.conf生效的原因)
    include /etc/nginx/conf.d/*.conf;
}

2.1.2 my.conf

分别在主、备服务器/etc/nginx/conf.d目录下新增my.conf

upstream my10016 {
                least_conn; #最少连接
                server 192.168.177.167:10016; 
                server 192.168.177.176:10016;
    }
    server {
        listen       10016;
        server_name  192.168.177.100;# 监听ip使用vip
        location / {
            proxy_pass   http://my10016;  # 设置代理
            index  index.html index.htm;
        }
  }

三、负载均衡测试

 可自行测试

PostMan调用http://192.168.177.100:10016/aaa接口

 {"msg":"success","status":"1","server_ip":"192.168.177.176"}

{"msg":"success","status":"1","server_ip":"192.168.177.167"}

四、nginx容错

4.1 nginx http健康检查

Nginx健康检查分为主动健康检查和被动健康检查,本例主要介绍被动健康检查(主动健康检查参考官方文档:https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#active-health-checks)。对于被动运行状况检查,NGINX会监视事务的发生,并尝试恢复失败的连接。如果仍然无法恢复交易,则NGINX将服务器标记为不可用,并暂时停止向服务器发送请求,直到再次将其标记为活动。

upstream my10016 {
    #负载均衡策略,最少连接,默认为轮询
    #least_conn;
    # 被动健康检查
    # 如果NGINX无法在30秒内向服务器发送请求或没有收到3次响应,则会将服务器标记为30秒不可用
    # 如果只有一个单一的服务器组中,将fail_timeout和max_fails参数被忽略,服务器永远不会标记为不可用
    # 意思是在fail_timeout时间内失败了max_fails次请求后,则认为该上游服务器不可用,然后将该服务地址踢除掉。fail_timeout时间后会再次将该服务器加入存活列表,进行重试。
    server 192.168.177.167:10016 fail_timeout=30s max_fails=3;
    server 192.168.177.176:10016 fail_timeout=30s max_fails=3;
}

4.2重试机制

proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...;

Default:    proxy_next_upstream error timeout;

Context:    http, server, location

指定应将请求传递到下一个服务器的情况:

error             # 与服务器建立连接,向其传递请求或读取响应头时发生错误;

timeout           # 在与服务器建立连接,向其传递请求或读取响应头时发生超时;

invalid_header    # 服务器返回空的或无效的响应;

http_500          # 服务器返回代码为500的响应;

http_502          # 服务器返回代码为502的响应;

http_503          # 服务器返回代码为503的响应;

http_504          # 服务器返回代码504的响应;

http_403          # 服务器返回代码为403的响应;

http_404          # 服务器返回代码为404的响应;

http_429          # 服务器返回代码为429的响应(1.11.13);

non_idempotent    # 通常,请求与 非幂等 方法(POST,LOCK,PATCH)不传递到请求是否已被发送到上游服务器(1.9.13)的下一个服务器; 启用此选项显式允许重试此类请求;

off               # 禁用将请求传递给下一个服务器。

proxy_next_upsstream_tries 0表示不限次数

完整示例如下:

upstream my10016 {
    #负载均衡策略,最少连接,默认为轮询
    #least_conn;
    # 被动健康检查
    # 如果NGINX无法在30秒内向服务器发送请求或没有收到3次响应,则会将服务器标记为30秒不可用
    # 如果只有一个单一的服务器组中,将fail_timeout和max_fails参数被忽略,服务器永远不会标记为不可用
    # 意思是在fail_timeout时间内失败了max_fails次请求后,则认为该上游服务器不可用,然后将该服务地址踢除掉。fail_timeout时间后会再次将该服务器加入存活列表,进行重试。
    server 192.168.177.167:10016 fail_timeout=30s max_fails=3;
    server 192.168.177.176:10016 fail_timeout=30s max_fails=3;
}

server {
    listen       10016;

# 监听vips
    server_name  192.168.177.100;
    location / {
       # 定义了什么情况下进行重试,此处error,timeout,http_500
       # non_idempotent 允许非幂等请求重试
       # post, lock, patch 这种会对服务器造成不幂等的方法,默认是不进行重试的,如果一定要进行重试,则要加上这个配置
       proxy_next_upstream error timeout http_500 non_idempotent;
       # 读取超时时间,默认值60s,此处10s
       # proxy_read_timeout 10s;
       # 连接超时时间
       proxy_connect_timeout 3s;
       # 6s后nginx 重试
       proxy_next_upstream_timeout 6s;
       # 重试次数,注意:此机制可能会导致数据重复插入的情况
       # 参考:https://www.cnblogs.com/lc0605/p/10444086.html
        proxy_next_upstream_tries 3;
        proxy_pass   http://my10016;  # 设置代理
        ndex  index.html index.htm;
    }
}

五、参考文章

5.1 Keepalived系列—配置文件keepalived.conf详解

5.2 Keepalived+Nginx实现高可用(HA)

5.3 Nginx 失败重试机制

原文地址:https://www.cnblogs.com/wintercloud/p/13844919.html