一 简介
nginx作为一款企业级的代理服务器,在各种场景下均有广泛的使用,尤其是在前后端分离的项目中,nginx作为路由转发的功能是非常常用的。在一些流量比较大的项目中,为了应对高并发的场景,后端服务往往采用集群部署,这时候,就需要使用到nginx的负载均衡功能,从而避免应用节点的单节点故障问题。
那么问题又来了,nginx的单点问题怎么解决?
我们可以通过nginx+keepalived的高可用集群方案来避免nginx的单节点故障问题,从而大幅度提高nginx的稳定性和可靠性。
Keepalived的作用是检测服务器的状态,如果有一台web服务器、数据库服务器宕机,或工作出现故障,Keepalived将检测到后,会将有故障的web服务器或者数据库服务器从系统中剔除,当服务器工作正常后Keepalived自动将web、数据库服务器加入到服务器群中。
Keepalived软件程序主要的功能有两个:
1)健康检测 基于类似交换机制OSI七层模型:3(网络层:IP)、4(传输层:IP+PORT)、7层(应用层:FTP、HTTP APP)来实现健康检查;
2)VRRP漂移 基于VRRP(虚拟路由冗余协议)路由协议来实现两台主机之间高可用,其中包括:MASTER和BACKUP,当MASTER宕机,能够自动化切换至BACKUP,从而让用户持续的访问;
二 规划
IP地址 | 主机名 | 软件 | vip | OS |
---|---|---|---|---|
11.0.1.131 | jie01 | nginx+keepalived | 11.0.1.140 | Euler 21.10 |
11.0.1.137 | jie02 | nginx+keepalived | 11.0.1.140 | Euler 21.10 |
三 部署
3.1 nginx 部署
说明:我这里部署的版本为1.26版本,使用脚本部署
[root@jie01 weihu]# cat install_nginx.sh
#!/bin/bash
#
#******************************************************************************************
#Author: a_jie
#QQ: 1484011523
#Date: 2024-05-26
#FileName: install_nginx.sh
#URL: xxxxxxHAHAxxxxxxx
#Description: The test script
#Copyright (C): wujunjie 2024 All rights reserved
#******************************************************************************************
NGINX_VERSION=1.26.0
NGINX_FILE=nginx-${NGINX_VERSION}.tar.gz
NGINX_URL=http://nginx.org/download/
NGINX_INSTALL_DIR=/usr/local/nginx
NGINX_DIR=`echo $NGINX_FILE | sed -rn 's/^(.*[0-9]).*/\1/p'`
SRC_DIR=/home/weihu/src
USER=nginx
SYSTEM_TYPE=`sed -rn 's/^ID="(.*)"/\1/p' /etc/os-release`
SYSTEM_VERSION=`sed -rn 's/^VERSION_ID="(.*)"/\1/p' /etc/os-release`
CPU=`lscpu |sed -rn 's/^CPU: +([0-9])/\1/p'`
color () {
RES_COL=60
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
SETCOLOR_FAILURE="echo -en \\033[1;31m"
SETCOLOR_WARNING="echo -en \\033[1;33m"
SETCOLOR_NORMAL="echo -en \E[0m"
echo -n "$1" && $MOVE_TO_COL
echo -n "["
if [ $2 = "success" -o $2 = "0" ] ;then
${SETCOLOR_SUCCESS}
echo -n $" OK "
elif [ $2 = "failure" -o $2 = "1" ] ;then
${SETCOLOR_FAILURE}
echo -n $"FAILED"
else
${SETCOLOR_WARNING}
echo -n $"WARNING"
fi
${SETCOLOR_NORMAL}
echo -n "]"
echo
}
check () {
if [ -d "$NGINX_INSTALL_DIR" ]; then
color "nginx 已安装,请卸载后再安装" 1; exit;
fi
cd ${SRC_DIR}
if [ -e ${NGINX_FILE} ];then
color "NGINX源码包已准备好" 0
else
color 'NGINX源码包不存在,现在开始下载' 0
wget ${NGINX_URL}${NGINX_FILE}
[ $? -ne 0 ] && { color "下载 ${NGINX_FILE}${TAR}文件失败" 1; exit; }
fi
}
install_nginx () {
color "开始安装 nginx" 0
# if id ${USER} &> /dev/null 2>&1;then
# color "${USER} 用户已存在" 1
# else
# useradd -s /sbin/nologin -r ${USER}
# color "创建 ${USER} 用户" 0
# fi
color "开始安装 nginx 依赖包" 0
if [ $SYSTEM_TYPE == "centos" ] ;then
if [[ $SYSTEM_VERSION =~ ^7 ]];then
yum -y install gcc make pcre-devel openssl-devel zlib-devel perl-ExtUtils-Embed
elif [[ $SYSTEM_VERSION =~ ^8 ]];then
yum -y install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed
else
color '不支持此centos系统! 请确认版本信息' 1
fi
elif [ $SYSTEM_TYPE == "bclinux" ];then
if [[ $SYSTEM_VERSION =~ 21.10 ]];then
#yum -y install gcc make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed
yum -y install gcc gcc-c++ pcre pcre-devel openssl openssl-devel zlib zlib-devel gd gd-devel perl-ExtUtils-Embed
elif [[ $SYSTEM_VERSION =~ 22.10 ]];then
#yum -y install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed
yum -y install gcc gcc-c++ pcre pcre-devel openssl openssl-devel zlib zlib-devel gd gd-devel perl-ExtUtils-Embed
else
color '不支持此bclinux系统! 请确认版本信息' 1
fi
else
echo "不支持的系统类型! 请检查 SYSTEM_TYPE 的值。" 1
fi
[ $? -ne 0 ] && { color "安装依赖包失败" 1; exit; }
cd ${SRC_DIR}
tar xf ${NGINX_FILE}
cd ${NGINX_DIR}
./configure --prefix=${NGINX_INSTALL_DIR} \
--with-pcre \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_image_filter_module \
--with-http_slice_module \
--with-mail \
--with-threads \
--with-file-aio \
--with-stream \
--with-mail_ssl_module \
--with-stream_ssl_module
make && make install
# make -j $CPU && make install
[ $? -eq 0 ] && color "nginx 编译安装成功" 0 || { color "nginx 编译安装失败,退出!" 1 ;exit; }
#chown -R nginx.nginx ${NGINX_INSTALL_DIR}
#ln -s ${NGINX_INSTALL_DIR}/sbin/nginx /usr/local/sbin/nginx
#echo "PATH=${NGINX_INSTALL_DIR}/sbin:${PATH}" > /etc/profile.d/nginx.sh
cat > /lib/systemd/system/nginx.service <<EOF
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=${NGINX_INSTALL_DIR}/logs/nginx.pid
ExecStartPre=/bin/rm -f ${NGINX_INSTALL_DIR}/logs/nginx.pid
ExecStartPre=${NGINX_INSTALL_DIR}/sbin/nginx -t
ExecStart=${NGINX_INSTALL_DIR}/sbin/nginx
ExecReload=/bin/kill -s HUP \$MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now nginx &> /dev/null
systemctl is-active nginx &> /dev/null || { color "nginx 启动失败,退出!" 1 ; exit; }
color "nginx 安装完成" 0
}
check
install_nginx
[root@jie01 weihu]# chmod +x install_nginx.sh
[root@jie01 weihu]# ./install_nginx.sh
创建测试页面
[root@jie01 ~]# echo "11.0.1.131_jie01" > /usr/local/nginx/html/index.html
[root@jie02 weihu]# echo "11.0.1.137_jie02" > /usr/local/nginx/html/index.html
#重启nginx
[root@jie01 ~]# systemctl restart nginx
[root@jie02 weihu]# systemctl restart nginx
访问测试
3.2 keepalived 部署
说明:我这里采用编译安装,如果有yum源可以直接yum安装 。
1)yum安装
yum -y install keepalived
#如果是yum装的配置文件在/etc/keepalived/keepalived.conf
2)编译安装(两台都要编译)
# 安装依赖包
[root@jie01 ~]# yum install -y gcc openssl-devel libnl* popt-devel
# 下载源码包
https://www.keepalived.org/software/keepalived-2.2.8.tar.gz
# 上传至服务器并解压
[root@jie01 src]# tar -xf keepalived-2.2.8.tar.gz
#编译
[root@jie01 src]# cd keepalived-2.2.8
[root@jie01 keepalived-2.2.8]# ./configure --prefix=/usr/local/keepalived
#安装
[root@jie01 keepalived-2.2.8]# make && make install
#查看
[root@jie01 ~]# ll -d /usr/local/keepalived/
drwxr-xr-x 6 root root 4.0K 6月 6 22:03 /usr/local/keepalived/
3)设置配置文件
# jie_01主机配置文件
[root@jie01 keepalived]# cat keepalived.conf
! Configuration File for keepalived
global_defs {
router_id nginx-ha01
}
vrrp_script check_nginx {
script "/usr/local/keepalived/sbin/chk_nginx.sh"
interval 2
weight -20
fall 2
rise 1
}
vrrp_instance nginx-ha {
state BACKUP
interface ens160
virtual_router_id 52
priority 100
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass R65yudzK
}
track_script {
check_nginx
}
virtual_ipaddress {
11.0.1.140/24 brd 11.0.1.255
}
}
#jie_02主机配置文件
[root@jie02 ~]# cat /usr/local/keepalived/etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
router_id nginx-ha02
}
vrrp_script check_nginx {
script "/usr/local/keepalived/sbin/chk_nginx.sh"
interval 2
weight -20
fall 2
rise 1
}
vrrp_instance nginx-ha {
state BACKUP
interface ens160
virtual_router_id 52
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass R65yudzK
}
track_script {
check_nginx
}
virtual_ipaddress {
11.0.1.140/24 brd 11.0.1.255
}
}
[root@jie02 ~]#
4)编写检测nginx脚本(两台都要创建)
#创建nginx检测脚本
vim /usr/local/keepalived/sbin/chk_nginx.sh
###############################################
#!/bin/bash
count=`ps -C nginx --no-header |wc -l`
if [ "${count}" -eq 0 ];then
/usr/local/nginx/sbin/nginx
sleep 5
count=`ps -C nginx --no-header |wc -l`
if [ "${count}" -eq 0 ]
then
systemctl stop keepalived
fi
fi
###############################################
#添加执行权限
[root@jie01 keepalived]# chmod +x /usr/local/keepalived/sbin/chk_nginx.sh
5)启动keepalived
#设置为开机自启并且立即启动
[root@jie01 keepalived]# systemctl enable --now keepalived
[root@jie02 keepalived]# systemctl enable --now keepalived
#扩展
systemctl start keepalived 启动keepalived服务
systemctl stop keepalived 停止keepalived服务
systemctl reload keepalived 重新加载
systemctl eable keepalived 开机自启服务
6)查看VIP地址
[root@jie01 keepalived]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:3e:2c:55 brd ff:ff:ff:ff:ff:ff
inet 11.0.1.131/24 brd 11.0.1.255 scope global noprefixroute ens160
valid_lft forever preferred_lft forever
inet 11.0.1.140/24 brd 11.0.1.255 scope global secondary ens160
valid_lft forever preferred_lft forever
inet6 fe80::9f1d:93bc:d40b:a5d4/64 scope link noprefixroute
valid_lft forever preferred_lft forever
[root@jie01 keepalived]#
# 发现目前vip地址漂移在 jie01 这台主机上面
7)页面通过vip地址访问
3.3 测试验证
1)先将11.0.1.131(jie01)服务器上的nginx停掉,看脚本是否会自拉起
[root@jie01 keepalived]# /usr/local/nginx/sbin/nginx -s stop
[root@jie01 keepalived]#
[root@jie01 keepalived]#
[root@jie01 keepalived]# ps -ef |grep nginx
root 13719 11745 0 22:36 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody 13720 13719 0 22:36 ? 00:00:00 nginx: worker process
root 13727 2101 0 22:36 pts/0 00:00:00 grep --color=auto nginx
2)将11.0.1.131(jie01)服务器上的keepalived停掉或者直接将这台关机,看VIP是否漂移到11.0.1.137(jie02)服务器上
# 11.0.1.131(jie01) 已关机
[root@jie02 keepalived]# ping 11.0.1.131
PING 11.0.1.131 (11.0.1.131) 56(84) bytes of data.
From 11.0.1.137 icmp_seq=1 Destination Host Unreachable
From 11.0.1.137 icmp_seq=2 Destination Host Unreachable
From 11.0.1.137 icmp_seq=3 Destination Host Unreachable
# 查看VIP发现在已经漂移在11.0.1.137(jie_02)服务器上
[root@jie02 keepalived]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:3c:d3:cc brd ff:ff:ff:ff:ff:ff
inet 11.0.1.137/24 brd 11.0.1.255 scope global noprefixroute ens160
valid_lft forever preferred_lft forever
inet 11.0.1.140/24 brd 11.0.1.255 scope global secondary ens160
valid_lft forever preferred_lft forever
inet6 fe80::3a5d:3c35:2a0a:119e/64 scope link dadfailed tentative noprefixroute
valid_lft forever preferred_lft forever
inet6 fe80::3149:f1bd:ec73:336c/64 scope link noprefixroute
valid_lft forever preferred_lft forever
[root@jie02 keepalived]#
页面访问VIP地址
3)将11.0.1.131(jie01)服务器重启开机,回复nginx、keepalived服务,会发现VIP不会切换回来,因为上面配置的是非抢占模式。
[root@jie02 keepalived]# ping 11.0.1.131 -c 3
PING 11.0.1.131 (11.0.1.131) 56(84) bytes of data.
64 bytes from 11.0.1.131: icmp_seq=1 ttl=64 time=0.730 ms
64 bytes from 11.0.1.131: icmp_seq=2 ttl=64 time=0.654 ms
64 bytes from 11.0.1.131: icmp_seq=3 ttl=64 time=0.702 ms
--- 11.0.1.131 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2085ms
rtt min/avg/max/mdev = 0.654/0.695/0.730/0.031 ms
[root@jie02 keepalived]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:3c:d3:cc brd ff:ff:ff:ff:ff:ff
inet 11.0.1.137/24 brd 11.0.1.255 scope global noprefixroute ens160
valid_lft forever preferred_lft forever
inet 11.0.1.140/24 brd 11.0.1.255 scope global secondary ens160
valid_lft forever preferred_lft forever
inet6 fe80::3a5d:3c35:2a0a:119e/64 scope link dadfailed tentative noprefixroute
valid_lft forever preferred_lft forever
inet6 fe80::3149:f1bd:ec73:336c/64 scope link noprefixroute
valid_lft forever preferred_lft forever
[root@jie02 keepalived]#
四 扩展
4.1 keepalived服务脚本,编译安装后自动生成
[root@jie01 ~]# ll /usr/lib/systemd/system/keepalived.service
-rw-r--r-- 1 root root 540 6月 6 22:03 /usr/lib/systemd/system/keepalived.service
[root@jie01 ~]# cat /usr/lib/systemd/system/keepalived.service
[Unit]
Description=LVS and VRRP High Availability Monitor
After=network-online.target syslog.target
Wants=network-online.target
Documentation=man:keepalived(8)
Documentation=man:keepalived.conf(5)
Documentation=man:genhash(1)
Documentation=https://keepalived.org
[Service]
Type=forking
PIDFile=/run/keepalived.pid
KillMode=process
EnvironmentFile=-/usr/local/keepalived/etc/sysconfig/keepalived
ExecStart=/usr/local/keepalived/sbin/keepalived $KEEPALIVED_OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
[root@jie01 ~]#
4.2 keepalived配置文件详解
第一部分:全局定义块
1、email通知。作用:有故障,发邮件报警。
2、Lvs负载均衡器标识(lvs_id)。在一个网络内,它应该是唯一的。
3、花括号“{}”。用来分隔定义块,因此必须成对出现。
global_defs {
notification_email { #指定keepalived在发生切换时需要发送email到的对象,一行一个
sysadmin@fire.loc
}
notification_email_from Alexandre.Cassen@firewall.loc #指定发件人
smtp_server localhost #指定smtp服务器地址
smtp_connect_timeout 30 #指定smtp连接超时时间
router_id LVS_DEVEL #运行keepalived机器的一个标识
}
第二部分:VRRPD配置
# 定义chk_nginx脚本,脚本执行间隔10秒,权重-10,检测nginx服务是否在运行。有很多方式,比如进程,用脚本检测等等
vrrp_script chk_nginx {
#这里通过脚本监测
script "/data/chk_nginx.sh"
#脚本执行间隔,每2s检测一次
interval 2
#脚本结果导致的优先级变更,检测失败(脚本返回非0)则优先级 -5
weight -10
#检测连续2次失败才算确定是真失败。会用weight减少优先级(1-255之间)
fall 2
#检测1次成功就算成功。但不修改优先级
rise 1
}
第三部分:定义vrrp实例,VI_1 为虚拟路由的标示符,自己定义名称,keepalived在同一virtual_router_id中priority(0-255)最大的会成为master,也就是接管VIP,当priority最大的主机发生故障后次priority将会接管
vrrp_instance VI_1 {
state BACKUP #指定那个为master,那个为backup,如果设置了nopreempt这个值不起作用,主备考priority决
定
interface eth0 #设置实例绑定的网卡
dont_track_primary #忽略vrrp的interface错误(默认不设置)
track_interface{ #设置额外的监控,里面那个网卡出现问题都会切换
eth0
eth1
}
mcast_src_ip #发送多播包的地址,如果不设置默认使用绑定网卡的primary ip
garp_master_delay #在切换到master状态后,延迟进行gratuitous ARP请求
virtual_router_id 50 #VPID标记
priority 99 #优先级,高优先级竞选为master
advert_int 1 #检查间隔,默认1秒
nopreempt #设置为不抢占 注:这个配置只能设置在backup主机上,而且这个主机优先级要比另外一台高
preempt_delay #抢占延时,默认5分钟
debug #debug级别
authentication { #设置认证
auth_type PASS #认证方式
auth_pass 111111 #认证密码
}
#VRRP HA 虚拟地址 如果有多个VIP,继续换行填写
#设置VIP,它随着state变化而增加删除,当state为master的时候就添加,当state为backup的时候则删除,由优先级决定
virtual_ipaddress {
192.168.202.200
}
}
第四部分:执行nginx检测脚本。注意这个设置不能紧挨着写在vrrp_script配置块的后面。一定要注意
track_script {
#引用VRRP脚本,即在 vrrp_script 部分指定的名字。定期运行它们来改变优先级,并最终引发主备切换。
chk_nginx
}
}