背景:
梳理知识体系,关于架构,有做过一个项目,是关于双机热备高可用的方案。两台机器相对独立,两套一模一样且独立运行的系统,客户要求实现高可用。经过和架构部的商讨,通过lvs+keepalived来做。
LVS是什么
LVS是一个开源的软件,可以实现传输层四层负载均衡。LVS是Linux Virtual Server的缩写,意思是Linux虚拟服务器。目前已被集成到 Linux 内核模块中。该项目在 Linux 内核中实现了基于 TCP 层的 IP 数据负载均衡分发,其工作在内核空间且仅做负载均衡分发处理,所以稳定性相对较好,性能相对较强,对内存及 CPU 资源的消耗也最低。目前有三种IP负载均衡技术(VS/NAT、VS/TUN和VS/DR);八种调度算法(rr,wrr,lc,wlc,lblc,lblcr,dh,sh)。
keepalived是什么
keepalived是基于VRRP协议实现的保证集群高可用的一个服务软件,主要功能是实现真机的故障隔离和负载均衡器间的失败切换,防止单点故障。LVS可以实现负载均衡,但是不能够进行健康检查,比如一个rs出现故障,LVS 仍然会把请求转发给故障的rs服务器,这样就会导致请求的无效性。keepalive 软件可以进行健康检查,而且能同时实现 LVS 的高可用性,解决 LVS 单点故障的问题,其实 keepalive 就是为 LVS 而生的。
Keepalived相关术语
展开
虚拟 IP(VIP):对外提供用户访问的 IP 地址,与 LVS 的 VIP 概念相同;
真实服务器(Real Server):被负载的后端服务器;
服务器池(Server Pool):同一虚拟 IP 及端口的一组真实服务器;
虚拟服务器(Virtual Server):服务器池的外部访问点,每个虚拟 IP 和端口组成一个虚拟服务器;
虚拟服务(Virtual Service):与 VIP 关联的 TCP/UDP 服务;
VRRP:Keepalived 实现高可用的虚拟路由器冗余协议;
VRRP 路由器(VRRP Router):运行 VRRP 协议的路由器设备;
虚拟路由器(Virtual Router):一个抽象对象,一组具有相同 VRID(虚拟路由器标识符)的多个 VRRP 路由器集合;
MASTER 状态:主路由状态,是 VIP 地址的拥有者,负责转发到达虚拟路由的三层数据包,负责对虚拟 IP 地址的 ARP 请求进行响应;
BACKUP 状态:备份路由状态,当主路由状态设备故障时,负责接管数据包转发及ARP请求响应。
工作原理:
展开
Keepalived 为 LVS 提供了文件形式的配置方式,并为真实服务器提供了多种主动健康检测机制,通过 VRRP 协议为 LVS 提供了高可用的负载集群解决方案。Keepalived 的工作模式如下图所示。
处于 MASTER 状态的 Keepalived 主机是 VIP 的拥有者,负责上层路由 VIP 的 ARP 查询响应和数据包转发;
处于 MASTER 状态的 Keepalived 主机通过 VRRP 协议在局域网内组播 VRRP 通告信息;
处于 MASTER 状态的 Keepalived 主机通过配置的健康检测机制主动检查服务器池中真实服务器的状态;
处于 BACKUP 状态的 Keepalived 主机接收 VRRP 通告信息,并根据通告信息判断本机状态是否变更;
当处于 MASTER 状态的路由发生故障时,处于 BACKUP 状态的路由确认主路由状态的 VRRP 通告超时时,则改变自身状态为 MASTER 状态,负责上层路由 IP 地址的 ARP 请求响应,并对外组播 VRRP 通告。
部署:
openssl脚本安装
展开
#!/bin/bash
# auth:chenjf
# func:install openssl
# version:v1.0
# sys:CentOS Linux release 7.6.1810 (Core)
# openssl version:openssl-OpenSSL_1_1_1q.tar.gz
path=$(cd $(dirname $0); pwd)
openssl_home=/usr/local/openssl
##用yum安装依赖包
yum repolist
yum -y install gcc gcc-c++ automake
check_xxx_home(){
read -n3 -p "$xxx_home already exists,Do you want to delete and reinstall it? please set yes or no [Y/N][y/n]?" aaa
case $aaa in
Y|y|yes)
sudo rm -rf $xxx_home
echo "$xxx_home remove successful ";;
N|n|no)
echo "ok,bye bye~~"
exit 0;;
*)
echo "answer yes or no [Y/N][y/n] ,please.."
check_xxx_home;;
esac
}
install_openssl(){
mv /usr/local/openssl /usr/local/openssl_old
tar -zxf $path/openssl-OpenSSL_1_1_1q.tar.gz -C $path/
cd $path/openssl-OpenSSL_1_1_1q
./config --prefix=$openssl_home --openssldir=$openssl_home shared
#./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared
#./config
make && make install
#cd $path/openssl-OpenSSL_1_1_1q
\cp -a $path/openssl-OpenSSL_1_1_1q/libssl.so.1.1 /usr/local/lib/
\cp -a $path/openssl-OpenSSL_1_1_1q/libcrypto.so.1.1 /usr/local/lib/
\cp -a $path/openssl-OpenSSL_1_1_1q/libssl.so.1.1 /usr/local/lib64/
\cp -a $path/openssl-OpenSSL_1_1_1q/libcrypto.so.1.1 /usr/local/lib64/
mv -f /usr/lib/libssl.so.1.1 /usr/lib/libssl.so.1.1_old
mv -f /usr/lib/libcrypto.so.1.1 /usr/lib/libcrypto.so.1.1_old
mv -f /usr/lib64/libssl.so.1.1 /usr/lib64/libssl.so.1.1_old
mv -f /usr/lib64/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1_old
ln -sf /usr/local/lib/libssl.so.1.1 /usr/lib/libssl.so.1.1
ln -sf /usr/local/lib/libcrypto.so.1.1 /usr/lib/libcrypto.so.1.1
ln -sf /usr/local/lib/libssl.so.1.1 /usr/lib64/libssl.so.1.1
ln -sf /usr/local/lib/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1
#ln -s /usr/local/lib /usr/local/openssl/lib
mv /usr/include/openssl /usr/include/openssl.old
ln -sf /usr/local/include/openssl/ /usr/include/openssl
egrep "$openssl_home" /etc/ld.so.conf >& /dev/null
if [ $? -ne 0 ];then
echo "/usr/local/openssl/lib" >> /etc/ld.so.conf
fi
ldconfig -v
#/usr/local/openssl/bin/openssl version
mv /usr/bin/openssl /usr/bin/openssl.old
ln -sf /usr/local/openssl/bin/openssl /usr/bin/openssl
openssl version
echo "OpenSSL_1_1_1q install successfully!!!"
}
if [ ! -d $openssl_home ];then
echo "openssl_home does not exist,start to install........"
install_openssl
else
xxx_home=$openssl_home
check_xxx_home
install_openssl
fi
keepalived安装
展开
#!/bin/bash
# auth:chenjf
# func:install keepalived
# version:v1.0
# sys:CentOS Linux release 7.6.1810 (Core)
# version:keepalived-2.2.2
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
##要用root安装
[ $(id -u) -gt 0 ] && echo "please use root to execute the script!" && exit 1
#安装目录
install_path=/usr/local
#安装包(压缩包)
keepalived_package=keepalived-2.2.2.tar.gz
#解压后的文件夹
keepalived_installer=keepalived-2.2.2
yum repolist
yum -y install gcc gcc-c++ automake
check_home(){
read -n3 -p "$home already exists,Do you want to delete and reinstall it? please set yes or no [Y/N][y/n]?" aaa
case $aaa in
Y|y|yes)
sudo rm -rf $home
echo "$home remove successful ";;
N|n|no)
echo "ok,bye bye~~"
exit 0;;
*)
echo "Input yes or no [Y/N][y/n] ,please.."
check_home;;
esac
}
if [ -d $keepalived_home ];then
home=$keepalived_home
check_home
fi
tar -zxf $shell_dir/$keepalived_package
cd $shell_dir/$keepalived_installer
export LDFLAGS="$LDFLAGS -L /usr/local/openssl/lib" CFLAGS="$CFLAGS -I /usr/local/openssl/include"
./configure --prefix=$keepalived_home
make -j4 && make install
mkdir $keepalived_home/run -p
mkdir /etc/keepalived -p
keepalived加入开机启动
vim /etc/systemd/system/keepalived.service
[Unit]
Description=LVS and VRRP High Availability Monitor
After=network-online.target syslog.target
Wants=network-online.target
[Service]
Type=forking
#PIDFile=/usr/local/keepalived/run/keepalived.pid
PIDFile=/var/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
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
配置规划
组件 | vip | node1 | node2 | 模式 |
---|---|---|---|---|
nginx | 172.16.249.4 | 172.16.249.2 | 172.16.249.3 | 非抢占模式 |
mysql | 172.16.249.5 | 172.16.249.2 | 172.16.249.3 | 非抢占模式 |
redis | 172.16.249.6 | 172.16.249.2(备) | 172.16.249.3(主) | 抢占模式 |
keepalived.conf(node1)配置
! Configuration File for keepalived
global_defs {
#该主机的负载均衡标识
router_id xxx-01
#运行脚本的用户
script_user root
#如过路径为非root可写,不会以root身份运行脚本
enable_script_security
}
vrrp_script chk_nginx {
script "/etc/keepalived/nginx_check.sh"
#指定脚本执行的间隔。单位是秒
interval 2
#<-254 --- 254>:调整优先级,默认为2.
weight -20
}
vrrp_instance VI_1 {
#指定实例的模式,如果是非抢占模式,该值不起作用
state BACKUP
#实例绑定的网卡,用于发送vrrp包
interface eth0
##vrrp实例id(虚拟路由器的VRID),范围0-255
virtual_router_id 100
#发送组播包地址,不设置默认使用网卡的primary ip
mcast_src_ip 172.16.249.2
#设置优先级
priority 100
#检查间隔,默认为1秒
advert_int 5
#设置为非抢占模式(不参与MASTER的选举)
nopreempt
#指定认证方式。PASS简单密码认证(推荐),AH:IPSEC认证(不推荐)
authentication {
auth_type PASS
#认证密码,最多8位。
auth_pass 10207138
}
#vip地址配置
virtual_ipaddress {
172.16.249.4/26
}
track_script {
chk_nginx
}
}
vrrp_script chk_mysql {
script "/etc/keepalived/mysql_check.sh"
#设置检测脚本的执行间隔。单位是 s。默认为 1s
interval 2
#用于调整 VRRP 路由器优先级的权重值,如果脚本执行成功且 weight 为正时,则优先级增力相应值;如果脚本执行失败且 weight 为负,则优先级减少相应值。优先级的取值范围为 1~254
weight -5
#连续检测失败次数为设定值时才确认为失败状态
fall 2
#连续检测成功次数为设定值时才确认为成功状态
rise 1
}
vrrp_instance VI_2 {
state BACKUP
interface eth0
virtual_router_id 11
mcast_src_ip 172.16.249.2
priority 100
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 05929696
}
virtual_ipaddress {
172.16.249.5/26
}
track_script {
chk_mysql
}
}
vrrp_script chk_redis {
script "/etc/keepalived/redis_check.sh"
interval 2
fall 3
rise 1
}
vrrp_instance VI_3 {
state BACKUP
interface eth0
virtual_router_id 12
mcast_src_ip 172.16.249.2
advert_int 1
priority 100
track_script {
chk_redis
}
authentication {
auth_type PASS
auth_pass 05929696
}
virtual_ipaddress {
172.16.249.6/26
}
#指定一个转换为 MASTER 状态后执行的 shell 脚本
notify_master "/etc/keepalived/redis_master.sh 172.16.249.3 6389"
#指定一个转换为 BACKUP 状态后执行的 shell 脚本
notify_backup "/etc/keepalived/redis_backup.sh 172.16.249.3 6389"
}
keepalived.conf(node2)配置
! Configuration File for keepalived
global_defs {
#该主机的负载均衡标识
router_id xxx-02
#运行脚本的用户
script_user root
#如过路径为非root可写,不会以root身份运行脚本
enable_script_security
}
vrrp_script chk_nginx {
script "/etc/keepalived/nginx_check.sh"
#指定脚本执行的间隔。单位是秒
interval 2
#<-254 --- 254>:调整优先级,默认为2.
weight -20
}
vrrp_instance VI_1 {
#指定实例的模式,如果是非抢占模式,该值不起作用
state BACKUP
#实例绑定的网卡,用于发送vrrp包
interface eth0
#vrrp实例id,范围0-255
virtual_router_id 100
#发送组播包地址,不设置默认使用网卡的primary ip
mcast_src_ip 172.16.249.3
#设置优先级
priority 70
#检查间隔,默认为1秒
advert_int 1
#设置为非抢占模式
nopreempt
#指定认证方式。PASS简单密码认证(推荐),AH:IPSEC认证(不推荐)
authentication {
auth_type PASS
#认证密码,最多8位。
auth_pass 10207138
}
#vip地址配置
virtual_ipaddress {
172.16.249.4/26
}
track_script {
chk_nginx
}
}
vrrp_script chk_mysql {
script "/etc/keepalived/mysql_check.sh"
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_2 {
state BACKUP
interface eth0
virtual_router_id 11
mcast_src_ip 172.16.249.3
priority 110
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 05929696
}
virtual_ipaddress {
172.16.249.5/26
}
track_script {
chk_mysql
}
}
vrrp_script chk_redis {
script "/etc/keepalived/redis_check.sh"
interval 2
fall 3
rise 1
}
vrrp_instance VI_3 {
state MASTER
interface eth0
virtual_router_id 12
mcast_src_ip 172.16.249.3
advert_int 1
priority 110
track_script {
chk_redis
}
authentication {
auth_type PASS
auth_pass 05929696
}
virtual_ipaddress {
172.16.249.6/26
}
notify_master "/etc/keepalived/redis_master.sh 172.16.249.2 6389"
}
检测脚本
nginx_check.sh
#!/bin/bash
# check nginx server status
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" = "0" ]; then
systemctl start nginx
sleep 2
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" = "0" ]; then
systemctl stop keepalived
fi
fi
mysql_check.sh
#!/bin/bash
counter=$(netstat -na|grep "LISTEN"|grep "3308"|wc -l)
if [ "${counter}" -eq 0 ]; then
systemctl stop keepalived
fi
redis_check.sh
#!/bin/bash
ALIVE=`/app/redis-6389/bin/redis-cli -p 6389 -a 密码 ping`
if [ "$ALIVE" == "PONG" ]; then
echo $ALIVE
exit 0
else
echo $ALIVE
exit 1
fi
redis_master.sh
#!/bin/bash
REDISCLI="/app/redis-6389/bin/redis-cli -p 6389 -a 密码"
LOGFILE="/etc/keepalived/log/keepalived-redis-state.log"
echo "[master]" >> $LOGFILE
date >> $LOGFILE
echo "Being master...." >> $LOGFILE 2>&1
echo "Run SLAVEOF cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF $1 $2 >> $LOGFILE 2>&1
sleep 10 #延迟10秒以后待数据同步完成后再取消同步状态
echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1
redis_backup.sh
#!/bin/bash
REDISCLI="/app/redis-6389/bin/redis-cli -p 6389 -a 密码"
LOGFILE="/etc/keepalived/log/keepalived-redis-state.log"
echo "[backup]" >> $LOGFILE
date >> $LOGFILE
echo "Being slave...." >> $LOGFILE 2>&1
sleep 15 #..15....................
echo "Run SLAVEOF cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF $1 $2 >> $LOGFILE 2>&1
全局参数
配置关键字 | 功能描述 |
---|---|
global_defs | 全局配置区域标识 |
notification_email | 设置接收告警邮件的地址列表 |
notification_email_from | 设置发送邮件的地址列表 |
smtp_server | 设置用于发送邮件的 SMTP 服务器地址 |
smtp_connection_timeout | 设置 SMTP 服务器连接超时时间 |
router_id | 设置当前设备的路由 ID,每个设备均不相同 |
vrrp_version | VRRP 协议版本 |
nopreempt | 是否启用非抢占模式,即不参与 MASTER 的选举,默认为抢占模式 |
VRRP配置
配置关键字 | 功能描述 |
---|---|
vrrp_instance | VRRP 实例配置区域标识 |
state | 设置当前 VRRP 路由的初始状态 |
interface | 设置 VRRP 绑定的设备网络接口 |
virtual_router_id | 设置当前设备所属的虚拟路由 ID |
priority | 设置当前 VRRP 路由的初始优先级,优先级最高的会被选举为 MASTER,优先级取值范围为 1~254 |
advert_int | 发送组播包的间隔时间,默认为 1 秒 |
nopreempt | 是否启用非抢占模式,即不参与 MASTER 的选举,默认为抢占模式 |
preempt_delay | 设置抢占延时,取值范围为 0~1000,默认为 0,单位为秒。即等待多少秒才参与 MASTER 选举 |
authentication | VRRP 通信认证配置区域标识 |
auth_type | 指定 VRRP 通信的认证类型,有 PASS 简单密码认证和 AH:IPSEC 认证两种类型 |
auth_pass | 指定 VRRP 通信密码字符串,最大为 8 位 |
virtual_ipaddress | VIP 地址配置区域标识 |
notify_master | 指定一个转换为 MASTER 状态后执行的 shell 脚本 |
notify_backup | 指定一个转换为 BACKUP 状态后执行的 shell 脚本 |
notify_fault | 指定一个转换为 FAULT 状态后执行的 shell 脚本 |
smtp_alert | 使用 SMTP 的配置发送邮件告警通知 |
脚本检测参数
配置关键字 | 功能描述 |
---|---|
vrrp_script | VRRP 脚本配置区域标识 |
scrip | 指定要执行的脚本路径 |
weight | 用于调整 VRRP 路由器优先级的权重值,如果脚本执行成功且 weight 为正时,则优先级增力相应值;如果脚本执行失败且 weight 为负,则优先级减少相应值。优先级的取值范围为 1~254 |
interval | 设置检测脚本的执行间隔。单位是 s。默认为 1s |
timeout | 脚本执行返回结果超时时间,超过指定时间则认为检测失败 |
rise | 连续检测成功次数为设定值时才确认为成功状态 |
fall | 连续检测失败次数为设定值时才确认为失败状态 |
init_fail | 设置脚本初始检测状态为失败状态 |
验证
略略略
总结
这样配置当一方某个组件发生故障的话,vip全都会切到另一台上,以后有时间再研究,再做调整优化。
届时参考
https://www.weixueyuan.net/a/827.html
https://blog.csdn.net/A79800/article/details/125624620