Keepalived + Nginx 实现高可用

原理

如果杀掉 MASTER(主)节点的 nginx
如果nginx进程不存在脚本返回1,nginx进程存在脚本返回0

脚本返回0,表示nginx进程存在,由于上面配置的weight为负数,priority 不会改变。

脚本返回1,表示nginx进程不存在,由于上面配置的weight为负数,priority 会减少,减少后的值 priority = priority+weight
失败一次,将自己的优先级-20,我这里MASTER的priority=100,BACKUP的priority=99
那么MASTER要失败1次后变为80,低于BACKUP的99,MASTER节点会降级为BACKUP,而BACKUP节点升级为MASTER

#!/bin/bash
# keepalived会定时执行脚本并对脚本执行的结果进行分析,动态调整vrrp_instance的优先级。
# 如果脚本执行结果为0,并且weight配置的值大于0,则优先级相应的增加。
# 如果脚本执行结果非0,并且weight配置的值小于 0,则优先级相应的减少。
# 其他情况,维持原本配置的优先级,即配置文件中priority对应的值。
result=`pidof nginx`
if [ ! -z "${result}" ];
then
exit 0
else
exit 1
fi

提示:以上脚本判断推出码是0还是1,正常退出时0,非正常退出为1;

退出码1就把对应节点的优先级降低20(通常这个降低的值要大于两节点优先级之差就行,意思就是降低后的优先级要小于备份节点优先级,这样才有意义);
脚本执行连续3次检测都为成功状态(脚本退出码都为0),则keepalived就标记该实例为OK状态,并会一直检测下去,
如果连续3次检查都为失败状态(退出码1),则标记对应实例为KO状态;
一旦标记对应实例为失败状态就会触发当前节点的优先级降低;
从而在通告心跳时,会通告降低后的优先级,从而实现备份节点接管VIP来完成vip转移;

杀掉 MASTER(主)节点的 nginx 后,再次访问浮动VIP

编译安装 keepalived

可以去keepalived官网直接下载:https://www.keepalived.org/download.html

# 安装依赖
yum -y install gcc openssl openssl-devel libnl libnl-devel libnfnetlink-devel wget

# 下载
wget --no-check-certificate https://www.keepalived.org/software/keepalived-2.2.7.tar.gz
tar -zxvf keepalived-2.2.7.tar.gz
cd keepalived-2.2.7

# 选项--disable-fwmark 可用于禁用iptables规则,可访止VIP无法访问,无此选项默认会启用iptables规则
./configure --prefix=/usr/local/keepalived --disable-fwmark
make -j 2 && make install

mkdir /etc/keepalived
cp -p /usr/local/keepalived/etc/keepalived/keepalived.conf.sample /etc/keepalived/keepalived.conf

# 默认编译安装之后会在 /lib/systemd/system/ 自动生成启动配置文件,如果没生成,手动拷贝一下该文件
# cp ./keepalived/keepalived.service /lib/systemd/system/

# 启动
systemctl daemon-reload
systemctl enable --now keepalived.service

keepalived 配置说明

全剧配置

#/etc/keepalived/keepalived.conf 
global_defs {
notification_email {
root@localhost #keepalived 发生故障切换时邮件发送的目标邮箱,可以按行区分写多个
root@wangxiaochun.com
29308620@qq.com
}
notification_email_from keepalived@localhost #发邮件的地址
smtp_server 127.0.0.1 #邮件服务器地址
smtp_connect_timeout 30 #邮件服务器连接timeout
router_id ka1.example.com #每个keepalived主机唯一标识,建议使用当前主机名,如果多节点重名可能会影响切换脚本执行
vrrp_skip_check_adv_addr #对所有通告报文都检查,会比较消耗性能,启用此配置后,如果收到的通告报文和上一个报文是同一个路由器,则跳过检查,默认值为全检查
vrrp_strict #严格遵守VRRP协议,启用此项后以下状况将无法启动服务:1.无VIP地址 2.配置了单播邻居 3.在VRRP版本2中有IPv6地址,开启动此项并且没有配置vrrp_iptables时会自动开启iptables防火墙规则,默认导致VIP无法访问,建议不加此项配置
vrrp_garp_interval 0 #gratuitous ARP messages 报文发送延迟,0表示不延迟
vrrp_gna_interval 0 #unsolicited NA messages (不请自来)消息发送延迟
vrrp_mcast_group4 224.0.0.18 #指定组播IP地址范围:224.0.0.0到239.255.255.255,默认值:224.0.0.18
vrrp_iptables #此项和vrrp_strict同时开启时,则不会添加防火墙规则,如果无配置vrrp_strict项,则无需启用此项配置
}

虚拟路由器配置

vrrp_instance <STRING> { #<String>为vrrp的实例名,一般为业务名称
配置参数
......
}
#配置参数:
state MASTER|BACKUP#当前节点在此虚拟路由器上的初始状态,状态为MASTER或者BACKUP
interface IFACE_NAME #绑定为当前虚拟路由器使用的物理接口,如:eth0,bond0,br0,可以和VIP不在一个网卡
virtual_router_id VRID #每个虚拟路由器唯一标识,范围:0-255,每个虚拟路由器此值必须唯一,否则服务无法启动,同属一个虚拟路由器的多个keepalived节点必须相同,务必要确认在同一网络中此值必须唯一
priority 100 #当前物理节点在此虚拟路由器的优先级,范围:1-254,每个keepalived主机节点此值不同
advert_int 1 #vrrp通告的时间间隔,默认1s
authentication { #认证机制
auth_type AH|PASS #AH为IPSEC认证(不推荐),PASS为简单密码(建议使用)
auth_pass <PASSWORD> #预共享密钥,仅前8位有效,同一个虚拟路由器的多个keepalived节点必须一样
}
virtual_ipaddress { #虚拟IP,生产环境可能指定上百个IP地址
<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPE> label <LABEL>
192.168.200.100 #指定VIP,不指定网卡,默认为eth0,注意:不指定/prefix,默认
为/32
192.168.200.101/24 dev eth1 #指定VIP的网卡,建议和interface指令指定的网卡不在一个
网卡
192.168.200.102/24 dev eth2 label eth2:1 #指定VIP的网卡label
}
track_interface { #配置监控网络接口,一旦出现故障,则转为FAULT状态实现地址转移
eth0
eth1

}

keepalived + nginx 配置样例

MASTER(主)节点配置文件

# vi /etc/keepalived/keepalived.conf

! Configuration File for keepalived

# 全局配置
global_defs {
# 路由ID,不能重复,通常为 hostname
router_id 192.168.111.201
}

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

# 定义实例
vrrp_instance VI_1 {
# 可选值为 MASTER 或者 BACKUP
state MASTER
# 网卡名称,与本机 IP 地址所在的网卡名称相同
interface ens32
# 虚拟路由的ID,MASTER和BACKUP必须是一致的。
virtual_router_id 51
# 定义优先级,数字越大,优先级越高,MASTER的优先级必须大于BACKUP的优先级
priority 100
# 设定 MASTER 与 BACKUP 负载均衡之间同步检查的时间间隔,单位为秒
advert_int 1
# 设置验证类型和密码,两个节点必须一致
authentication {
# 认证方式,此处PASS表示为密码
auth_type PASS
# 生产环境设置6位随机密码
auth_pass 123456
}

# 设置虚拟IP地址
virtual_ipaddress {
192.168.111.250
}

# 执行脚本
track_script {
# 对应vrrp_script配置的脚本
chk_nginx
}
}

BACKUP(备)节点配置文件

! Configuration File for keepalived

global_defs {
# 路由ID,不能重复,通常为 hostname
router_id 192.168.111.202
}

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

# 定义实例
vrrp_instance VI_1 {
# 可选值为 MASTER 或者 BACKUP
state BACKUP
interface ens32
virtual_router_id 51
# 定义优先级,数字越大,优先级越高,MASTER的优先级必须大于BACKUP的优先级
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}

# 设置虚拟IP地址
virtual_ipaddress {
192.168.111.250
}

track_script {
chk_nginx
}
}

Nginx 状态检测脚本

vi /etc/keepalived/nginx_check.sh

方式一:

如果 nginx 停止运行,尝试启动,如果无法启动则杀死本机的 keepalived 进程, keepalied将虚拟 ip 绑定到 BACKUP 机器上。

#!/bin/bash
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then
# 这里需要替换成自己的nginx安装路径
# 尝试重新启动nginx
/usr/local/nginx/sbin/nginx
# 睡眠2秒
sleep 2
if [ $A -eq 0 ];then
#启动失败,将keepalived服务杀死。
killall keepalived
fi
fi

killall命令默认未安装,会报错 killall: 未找到命令。安装方式如下:

yum install -y psmisc

方式二:

如下,返回0或者1。我比较喜欢这种方式。
这里换成其他组件,比如mysql 修改为pidof mysql 即可。

#!/bin/bash
# keepalived会定时执行脚本并对脚本执行的结果进行分析,动态调整vrrp_instance的优先级。
# 如果脚本执行结果为0,并且weight配置的值大于0,则优先级相应的增加。
# 如果脚本执行结果非0,并且weight配置的值小于 0,则优先级相应的减少。
# 其他情况,维持原本配置的优先级,即配置文件中priority对应的值。
result=`pidof nginx`
if [ ! -z "${result}" ];
then
exit 0
else
exit 1
fi

给脚本添加执行权限

chmod +x /etc/keepalived/nginx_check.sh

开启组播防火墙

firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --in-interface ens32 --destination 224.0.0.18 --protocol vrrp -j ACCEPT
firewall-cmd --reload
# 查看规则是否生效
firewall-cmd --permanent --direct --get-all-rules

#########33
--direct:指定将要使用直接规则
--permanent:表示永久生效 ,没有此参数重启后失效
--add-rule ipv4 filter:表示添加一个新的策略
设置一条IPV4规则,表为防火墙表 :filter, 处理输入数据包 INPUT ,0 代表在头部添加。后面就是常用的iptables语法
--in-interface ens32:设置网卡名,这里我的网卡名是ens32
--destination 224.0.0.18:设置目标ip地址,也就是设置放行组播地址224.0.0.18
--protocol vrrp:设置拦截的协议
-j ACCEPT:表示放行,-j DROP表示丢弃(不放行)

启动 Keepalived

# 启动
systemctl start keepalived.service
# 重启
systemctl restart keepalived.service
# 停止
systemctl stop keepalived.service
# 查看运行状态
systemctl status keepalived
# 查看运行进程
ps -ef|grep keepalived

验证VIP漂移

我们先关闭Master,验证VIP是否会漂移到BACKUP上。

关闭 MASTER(主)节点 的 keepalived:

systemctl stop keepalived.service

常见错误

  1. keepalived 配置了虚拟ip,发现无法ping 通这个虚拟ip。
    解决方法,把配置文件中的 vrrp_strict 给注释掉,重启 keepalived 服务后再次ping就可以了。

  2. 启动失败、卡住
    使用命令 journalctl -xe 查看日志,有这样一行:
    Failed to parse PID from file /usr/local/keepalived/var/run/keepalived.pid: Invalid argument
    这里因为源码安装的方式,pid文件找不到,使用命令 systemctl status keepalived 查看 load文件路径:loaded (/usr/lib/systemd/system/keepalived.service)
    修改/usr/lib/systemd/system/keepalived.service该文件内容:PIDFile=/var/run/keepalived.pid