ithuang
ithuang
发布于 2025-12-20 / 5 阅读
0

企业级高可用组件 Keepalived

企业级高可用组件 Keepalived

Keepalived 背景描述及其方案设计

背景描述

时间:2022.6-2023.9

发布产品类型:互联网动态站点 商城

用户数量: 60000-80000(用户量猛增了4倍)

PV : 300000-5000000(24小时访问次数总和)

DAU: 25000(每日活跃用户数)

随着用户量增多,总的页面数量一直在持续增加,可以预料到的是,WEB服务器,压力会越来越大,虽然部署了集群和负载均衡,前端依然存在单点故障。如果一旦 Nginx 宕机,用户就完全失去服务了,用户体验特别不好,这就是造成了所谓的单点故障。

解决方案:

需要备用一台前端负载均衡服务器进行使用,如果出现宕机,切换为备用服务器。

梳理思路:

Nginx 解决的是两台 Web 服务器之间的负载均衡问题,但是没解决 Nginx 自己的高可用问题。

引入 高可用组件 Keepalived,当 Nginx 本身挂了,还有另一个 Nginx 可用。

运维方案设计

企业级高可用组件 Keepalived1.png

针对业务背景下的需求,升级为以下架构:

企业级高可用组件 Keepalived2.png

单点故障

过去我们可能依赖 单台负载均衡服务器(如 proxy1) 处理所有流量,但它存在巨大风险:

  • 一旦这台服务器宕机(硬件故障、进程崩溃、网络中断),所有依赖它的服务会 立即中断(如用户无法访问网站、支付系统瘫痪)。
  • 人工恢复耗时且不可靠:需紧急排查问题、重启服务或切换服务器,期间业务持续受损。

高可用集群

HA(High Availability) 是 高可用性 的缩写。

HA Cluster(高可用集群)是通过 多节点协作 保障业务 持续稳定运行 的解决方案,至少 2 个节点,通常更多。

高可用架构设计的核心准则是:冗余 + 故障切换 FailOver —— 不要把鸡蛋放在一个篮子里。

冷备份 vs 热备份

根据备用节点的 状态和切换速度,高可用方案分为两类:

  1. 冷备(Cold Standby)—— “紧急备用,但需手动”

特点:备用服务器 平时不运行服务(不启用、不解析域名),仅作为“灾备物资”存放。

缺点:恢复时间长(可能分钟级甚至小时级),业务中断明显。

  1. 热备(Hot Standby)—— “时刻待命,自动接管”

特点:备用服务器 始终处于运行状态(监控主节点健康),实时同步,随时准备接管。

关键支持:依赖 虚拟 IP(VIP)漂移(主备节点共享同一个对外 IP,故障时 VIP 自动绑定到备用节点)和 健康监测(如心跳检测、服务端口检查)。

企业级高可用组件 Keepalived3.png

Keepalived 整体概述(理解)

核心概念

Keepalived 是一个基于 VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议) 协议开发的开源软件,最初设计用于为 LVS(Linux Virtual Server,Linux虚拟服务器) 提供**高可用性(HA)**支持。

后来扩展为通用的 IP 漂移(VIP 漂移)和高可用解决方案,广泛应用于 Web 服务、数据库、负载均衡等场景的故障切换。

核心目标:

通过主备/主主节点的自动故障检测与切换,确保关键服务对外提供访问的 IP(VIP)始终可用,避免单点故障。

Redundancy 音 /rɪˈdʌndənsi/ 瑞当登西,意 冗余的,多余的。

核心功能

功能说明
VRRP 协议实现通过组播心跳协商主备角色,主节点持有 VIP,备节点监听并抢占。
VIP 漂移主节点故障时,备节点自动绑定 VIP 并通知局域网(通过 ARP)。
健康检查监测本地服务(如进程、端口、HTTP),异常时主动降权或触发切换。
轻量级部署基于 Linux 系统,仅需安装软件包并配置,资源占用低。

组成原理

Keepalived 的高可用功能基于 虚拟路由冗余协议 VRRP 实现,其核心思想是在多个节点(主/备)之间协商“谁是当前的主节点(Master)”,并由

主节点持有虚拟 IP(VIP);

备节点(Backup)监听主节点状态,主节点故障时快速接管 VIP。

工作流程

初始化

集群中所有节点(Master/Backup)配置相同的 VRID(虚拟路由标识符) 和 虚拟IP(VIP)。其中 Master节点初始优先级最高(如100),Backup节点优先级较低(如90),通过优先级确定主备角色。

Master 心跳

Master节点通过周期性(默认1秒间隔)向局域网广播,报文中包含自身 优先级、VRID、状态(Master) 等关键信息,用于宣告其对VIP的所有权,意思是”我是主节点!“。

Backup 监听

Backup节点持续监听组播报文;若在 Master_Down_Interval(默认3×通告间隔=3秒)超时时间内未收到有效的Master通告报文,则判定当前Master节点失效(如宕机或网络隔离)。

角色切换

当Backup节点确认Master节点失效后,优先级最高的Backup节点 自动升级为新的Master节点:

  • 立即开始周期性发送通告报文;
  • 通过内核网络接口绑定VIP,并更新局域网ARP表(通过免费ARP通告VIP对应的自身MAC地址),确保终端设备可将VIP流量路由至本节点。

原 Master 恢复

若原Master节点故障恢复并重新加入集群:

  • 抢占模式(默认启用):若原Master节点优先级仍高于当前Master节点(如恢复后优先级100 > 当前90),则原Master节点通过更高优先级的通告报文夺回主角色,原Backup节点降级;
  • 非抢占模式:原Master节点即使优先级更高也不主动抢占,维持当前Master节点继续持有VIP(需手动干预或关闭非抢占配置)。

核心机制:基于组播心跳维持主备状态同步,通过优先级竞争实现故障自动切换,结合ARP动态维护VIP的可达性。

Keepalived HA 高可用服务搭建(掌握)

环境准备

CentOS Stream release 9

角色IP主机名功能备注
proxy1192.168.88.101/192.168.88.102proxy1.itcast.cnKeepalived/Nginx代理Keepalived/代理1
proxy2192.168.88.102/192.168.88.103proxy2.itcast.cnKeepalived/Nginx代理Keepalived/代理2
slave192.168.88.104/192.168.88.105slave.itcast.cnMySQL从库(可选)从库(可选)

修改主机名和 hosts

hostnamectl set-hostname proxy1 && bash
hostnamectl set-hostname proxy2 && bash
hostnamectl set-hostname slave && bash # 可选

cat >/etc/hosts<<EOF
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.88.101   proxy1 proxy1.itcast.cn
192.168.88.102   proxy2 proxy2.itcast.cn
EOF

cat >/etc/hosts<<EOF
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.88.102   proxy1 proxy1.itcast.cn
192.168.88.103   proxy2 proxy2.itcast.cn
192.168.88.104   slave slave.itcast.cn
EOF

企业级高可用组件 Keepalived4.png

企业级高可用组件 Keepalived5.png

关闭防火墙与 SELinux

iptables -F
systemctl stop firewalld
systemctl disable firewalld
setenforce 0
sed -i '/SELINUX=enforcing/cSELINUX=disabled' /etc/selinux/config

配置 yum 源

阿里源(推荐)

cat >/etc/yum.repos.d/aliyun.repo<<EOF
[baseos]
name=CentOS Stream \$releasever - BaseOS
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[baseos-debug]
name=CentOS Stream \$releasever - BaseOS - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[baseos-source]
name=CentOS Stream \$releasever - BaseOS - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream]
name=CentOS Stream \$releasever - AppStream
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[appstream-debug]
name=CentOS Stream \$releasever - AppStream - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream-source]
name=CentOS Stream \$releasever - AppStream - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb]
name=CentOS Stream \$releasever - CRB
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[crb-debug]
name=CentOS Stream \$releasever - CRB - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb-source]
name=CentOS Stream \$releasever - CRB - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability]
name=CentOS Stream \$releasever - HighAvailability
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[highavailability-debug]
name=CentOS Stream \$releasever - HighAvailability - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability-source]
name=CentOS Stream \$releasever - HighAvailability - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv]
name=CentOS Stream \$releasever - NFV
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[nfv-debug]
name=CentOS Stream \$releasever - NFV - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv-source]
name=CentOS Stream \$releasever - NFV - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt]
name=CentOS Stream \$releasever - RT
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[rt-debug]
name=CentOS Stream \$releasever - RT - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt-source]
name=CentOS Stream \$releasever - RT - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage]
name=CentOS Stream \$releasever - ResilientStorage
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[resilientstorage-debug]
name=CentOS Stream \$releasever - ResilientStorage - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage-source]
name=CentOS Stream \$releasever - ResilientStorage - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[extras-common]
name=CentOS Stream \$releasever - Extras packages
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/\$basearch/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[extras-common-source]
name=CentOS Stream \$releasever - Extras packages - Source
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/source/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
EOF

清华源(了解)

CentOS Stream 9 默认启用了包管理工具 dnf,其是 yum 包管理工具的替代品。dnf 与 yum 大部分的命令都是通用的,dnf 也使用 /etc/yum.repos.d/ 进行镜像配置。

CentOS Stream 9 中源被整合入两个文件 centos.repo 和 centos-addons.repo,由于文件中不包含 baseurl 字段,需要手动插入,通过文本替换修改源的方法较为复杂,也可以选择直接复制最后的替换结果覆盖源文件。

将这段代码保存为一个文件,例如 update_mirror.pl

#!/usr/bin/perl

use strict;
use warnings;
use autodie;

my $mirrors = 'https://mirrors.tuna.tsinghua.edu.cn/centos-stream';

if (@ARGV < 1) {
    die "Usage: $0 <filename1> <filename2> ...\n";
}

while (my $filename = shift @ARGV) {
    my $backup_filename = $filename . '.bak';
    rename $filename, $backup_filename;

    open my $input, "<", $backup_filename;
    open my $output, ">", $filename;

    while (<$input>) {
        s/^metalink/# metalink/;

        if (m/^name/) {
            my (undef, $repo, $arch) = split /-/;
            $repo =~ s/^\s+|\s+$//g;
            ($arch = defined $arch ? lc($arch) : '') =~ s/^\s+|\s+$//g;

            if ($repo =~ /^Extras/) {
                $_ .= "baseurl=${mirrors}/SIGs/\$releasever-stream/extras" . ($arch eq 'source' ? "/${arch}/" : "/\$basearch/") . "extras-common\n";
            } else {
                $_ .= "baseurl=${mirrors}/\$releasever-stream/$repo" . ($arch eq 'source' ? "/" : "/\$basearch/") . ($arch ne '' ? "${arch}/tree/" : "os") . "\n";
            }
        }

        print $output $_;
    }
}

然后,在命令行中使用以下命令来执行它:

dnf install perl-autodie -y
perl ./update_mirror.pl /etc/yum.repos.d/centos*.repo

注意:如果清华源无法使用,我们也可以基于阿里源

cp /etc/yum.repos.d/centos.repo /etc/yum.repos.d/centos.repo.bak
cp /etc/yum.repos.d/centos-addons.repo /etc/yum.repos.d/centos-addons.repo.bak

centos.repo

[baseos]
name=CentOS Stream $releasever - BaseOS
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/BaseOS/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[baseos-debug]
name=CentOS Stream $releasever - BaseOS - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/BaseOS/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[baseos-source]
name=CentOS Stream $releasever - BaseOS - Source
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/BaseOS/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream]
name=CentOS Stream $releasever - AppStream
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/AppStream/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[appstream-debug]
name=CentOS Stream $releasever - AppStream - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/AppStream/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream-source]
name=CentOS Stream $releasever - AppStream - Source
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/AppStream/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb]
name=CentOS Stream $releasever - CRB
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/CRB/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[crb-debug]
name=CentOS Stream $releasever - CRB - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/CRB/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb-source]
name=CentOS Stream $releasever - CRB - Source
baseurl=https://mirrors.aliyun.com/centos-stream/$stream/CRB/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

centos-addons.repo

[highavailability]
name=CentOS Stream $releasever - HighAvailability
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/HighAvailability/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[highavailability-debug]
name=CentOS Stream $releasever - HighAvailability - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/HighAvailability/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability-source]
name=CentOS Stream $releasever - HighAvailability - Source
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/HighAvailability/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv]
name=CentOS Stream $releasever - NFV
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/NFV/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[nfv-debug]
name=CentOS Stream $releasever - NFV - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/NFV/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv-source]
name=CentOS Stream $releasever - NFV - Source
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/NFV/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt]
name=CentOS Stream $releasever - RT
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/RT/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[rt-debug]
name=CentOS Stream $releasever - RT - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/RT/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt-source]
name=CentOS Stream $releasever - RT - Source
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/RT/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage]
name=CentOS Stream $releasever - ResilientStorage
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/ResilientStorage/$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[resilientstorage-debug]
name=CentOS Stream $releasever - ResilientStorage - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/ResilientStorage/$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage-source]
name=CentOS Stream $releasever - ResilientStorage - Source
baseurl=http://mirrors.aliyun.com/centos-stream/$stream/ResilientStorage/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[extras-common]
name=CentOS Stream $releasever - Extras packages
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/$stream/extras/$basearch/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[extras-common-source]
name=CentOS Stream $releasever - Extras packages - Source
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/$stream/extras/source/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

最后,更新软件包缓存(可选):

# 使用 dnf
dnf clean all && dnf makecache

# 使用 yum
yum clean all && yum makecache

注意,如果需要启用其中一些 repo,需要将其中的 enabled=0 改为 enabled=1。

设置网络(可选)

ls /etc/NetworkManager/system-connections/
vi /etc/NetworkManager/system-connections/ens160.nmconnection
------------------------------------------------------  设置开始 -------------------------------------------------------------
[ipv4]
method=manual
addresses=192.168.88.101/24
gateway=192.168.88.2
dns=114.114.114.114

[ipv6]
method=ignore
------------------------------------------------------  设置结束 -------------------------------------------------------------
systemctl restart NetworkManager
nmcli device show ens160

如果不想重启整个 NetworkManager 服务,可以只重新激活特定的网络连接:

nmcli connection down <连接名称>
nmcli connection up <连接名称>

nmcli connection down ens160
nmcli connection up ens160

如果想重启所有网络接口,可以使用以下命令:

nmcli networking off
nmcli networking on

当然,你也可以使用传统方式,如:ifup,ifdown等等

ntpdate 时间同步(可选)

yum install epel-release -y
yum install ntpsec -y
ntpdate cn.ntp.org.cn

企业级高可用组件 Keepalived6.png

安装必备软件(推荐)

yum install vim net-tools rsync -y

安装配置 Keepalived

前提:让proxy1机器与proxy2机器保持配置完全一致(主机名称与IP地址要区分开)

keepalived可以使用通过yum方式或者源码编译的方式实现。

在目前案列实现中,通过yum安装的版本即可。

使用yum方式安装

master和backup都需要进行安装,所以给proxy1和proxy2进行安装keepalived

yum install keepalived -y
keepalived -v

企业级高可用组件 Keepalived7.png

企业级高可用组件 Keepalived8.png

Keepalived不需要自己单独的主机吗?

是的,对谁(比如这里的Nginx负载均衡)做高可用,就直接在集群上面安装部署即可。

配置文件:

# /etc/keepalived/keepalived.conf
[root@proxy1 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.200.16
        192.168.200.17
        192.168.200.18
    }
}

virtual_server 192.168.200.100 443 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 192.168.201.100 443 {
        weight 1
        SSL_GET {
            url {
              path /
              digest ff20ad2481f97b1754ef3e12ecd3a9cc
            }
            url {
              path /mrtg/
              digest 9b3a0c85a887a256d6939da88aabd8cd
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}

virtual_server 10.10.10.2 1358 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    sorry_server 192.168.200.200 1358

    real_server 192.168.200.2 1358 {
        weight 1
        HTTP_GET {
            url {
              path /testurl/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            url {
              path /testurl2/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            url {
              path /testurl3/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }

    real_server 192.168.200.3 1358 {
        weight 1
        HTTP_GET {
            url {
              path /testurl/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334c
            }
            url {
              path /testurl2/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334c
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}

virtual_server 10.10.10.3 1358 {
    delay_loop 3
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 192.168.200.4 1358 {
        weight 1
        HTTP_GET {
            url {
              path /testurl/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            url {
              path /testurl2/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            url {
              path /testurl3/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }

    real_server 192.168.200.5 1358 {
        weight 1
        HTTP_GET {
            url {
              path /testurl/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            url {
              path /testurl2/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            url {
              path /testurl3/test.jsp
              digest 640205b7b0fc66c1ea91c463fac6334d
            }
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}
[root@proxy1 ~]#

注意事项:

启动keepalived之前,一定要配置好/etc/keepalived/keepalived.conf

192.168.88.102(MASTER)/etc/keepalived/keepalived.conf

cat >/etc/keepalived/keepalived.conf<<EOF
! Configuration File for keepalived

global_defs {
   router_id ka102          # 本机标识,两台不能重复
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160         #  <-- 改成实际网卡名
    virtual_router_id 88     #  0-255,两台必须一致
    priority 100             #  主节点更高
    advert_int 1    # 是 VRRP 报文的发送间隔,单位秒

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
EOF

192.168.88.103(BACKUP)/etc/keepalived/keepalived.conf

cat >/etc/keepalived/keepalived.conf<<EOF
! Configuration File for keepalived

global_defs {
   router_id ka103          # 与对端不同
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens160         #  同上,改成实际网卡
    virtual_router_id 88     #  必须与 MASTER 一致
    priority 90             #  比主节点低
    advert_int 1    # 是 VRRP 报文的发送间隔,单位秒

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
EOF

日志文件:

# /var/log/messages

设置VIP注意事项:

① 用户可以访问VIP,VIP所在的机器没有限制外网访问 ② VIP是没有被占用的

以下实现,VIP选择同网段的。

启动 Keepalived

通过yum方式安装的keepalived,直接使用systemctl命令进行管理,如果是通过源码包安装的可以使用keepalived -D 参数启动。

注意:先在proxy1上启动keepalived,然后再到proxy2上启动keepalived

systemctl start keepalived
systemctl enable keepalived
systemctl status keepalived --no-pager
# systemctl stop keepalived

查看主备服务器的网卡信息

MASTER的网卡信息:

ip a

或者

ifconfig

BACKUP的网卡信息:

ip a

或者

ifconfig

通过keepalived的主备模式,实现默认VIP绑定到了master服务器

MASTER服务器宕机了(关机,断电了,网线断了),VIP会切换到了BACKUP

MASTER

[root@proxy1 ~]# systemctl start keepalived
[root@proxy1 ~]# systemctl enable keepalived
Created symlink /etc/systemd/system/multi-user.target.wants/keepalived.service → /usr/lib/systemd/system/keepalived.service.
[root@proxy1 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; disabled; preset: disabled)
     Active: active (running) since Thu 2025-12-11 23:27:30 CST; 5min ago
   Main PID: 31463 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 1.8M
        CPU: 65ms
     CGroup: /system.slice/keepalived.service
             ├─31463 /usr/sbin/keepalived --dont-fork -D
             └─31464 /usr/sbin/keepalived --dont-fork -D

Dec 11 23:27:34 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:34 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:34 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:34 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:39 proxy1 Keepalived_vrrp[31464]: (VI_1) Sending/queueing gratuitous ARPs on ens160 for 192.168.88.200
Dec 11 23:27:39 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:39 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:39 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:39 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
Dec 11 23:27:39 proxy1 Keepalived_vrrp[31464]: Sending gratuitous ARP on ens160 for 192.168.88.200
[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59109  bytes 82166292 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 21740  bytes 1366362 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]#

BACKUP

[root@proxy2 ~]# systemctl start keepalived
[root@proxy2 ~]# systemctl enable keepalived
Created symlink /etc/systemd/system/multi-user.target.wants/keepalived.service → /usr/lib/systemd/system/keepalived.service.
[root@proxy2 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; disabled; preset: disabled)
     Active: active (running) since Thu 2025-12-11 23:32:29 CST; 1min 47s ago
   Main PID: 31699 (keepalived)
      Tasks: 2 (limit: 22926)
     Memory: 1.8M
        CPU: 17ms
     CGroup: /system.slice/keepalived.service
             ├─31699 /usr/sbin/keepalived --dont-fork -D
             └─31700 /usr/sbin/keepalived --dont-fork -D

Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: Registering Kernel netlink reflector
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: Registering Kernel netlink command channel
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: Assigned address 192.168.88.103 for interface ens160
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: Registering gratuitous ARP shared channel
Dec 11 23:32:29 proxy2 Keepalived[31699]: Startup complete
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: (VI_1) removing VIPs.
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: (VI_1) removing VIPs.
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: (VI_1) Entering BACKUP STATE (init)
Dec 11 23:32:29 proxy2 Keepalived_vrrp[31700]: VRRP sockpool: [ifindex(  2), family(IPv4), proto(112), fd(12,13) multicast, addres>
Dec 11 23:32:29 proxy2 systemd[1]: Started LVS and VRRP High Availability Monitor.
[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 60487  bytes 82026148 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23567  bytes 1512933 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]#

常见问题说明:

问题1:keepalived启动了,但是没有产生VIP

答:大部分情况都是因为网卡名称写错了,最好在配置前使用ip a确认物理网卡名称:ens160或ens160或ens160

问题2:keepalived启动了,但是两台机器上都有VIP(脑裂)

答:在同一个HA Cluster组中有两个VIP,每台机器都认为自己是主服务器,核心原因在于网络通信异常,意识不到对方的存在。① 网络故障 ② 防火墙没关,vrrp协议默认无法通过防火墙。firewall-cmd --add-protocol=vrrp

问题3:脑裂有什么危害?

答:如果一个组中有两个VIP,就会出现资源争抢情况。

问题4:抢占模式

默认state master和backup模式,优先master,如果一旦master恢复,就会把VIP重新切换master上,本来没有问题,但是对于用户的友好性不好。如果master恢复,不争抢作为备机更好。

可以通过把两台服务器的state状态,都调整为backup,就不存在恢复争抢的问题。

非抢占模式

MASTER节点

[root@proxy1 ~]# vim /etc/keepalived/keepalived.conf
[root@proxy1 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id ka102          # 本机标识,两台不能重复
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens160         #  <-- 改成实际网卡名
    virtual_router_id 88     #  0-255,两台必须一致
    priority 100             #  改成相同的数字
    advert_int 1    # 是 VRRP 报文的发送间隔,单位秒
    nopreempt # 加上这个参数更加稳妥
    
    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
[root@proxy1 ~]# systemctl restart keepalived
[root@proxy1 ~]# systemctl status keepalived --no-pager
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; preset: disabled)
     Active: active (running) since Wed 2026-03-25 15:54:50 CST; 5s ago
   Main PID: 35039 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 1.8M
        CPU: 18ms
     CGroup: /system.slice/keepalived.service
             ├─35039 /usr/sbin/keepalived --dont-fork -D
             └─35041 /usr/sbin/keepalived --dont-fork -D

Mar 25 15:54:52 proxy1 Keepalived_vrrp[35041]: (VI_1) received lower priori…ing
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: (VI_1) Receive advertisement…out
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: (VI_1) Entering MASTER STATE
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: (VI_1) setting VIPs.
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: (VI_1) Sending/queueing grat…200
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: Sending gratuitous ARP on en…200
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: Sending gratuitous ARP on en…200
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: Sending gratuitous ARP on en…200
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: Sending gratuitous ARP on en…200
Mar 25 15:54:53 proxy1 Keepalived_vrrp[35041]: Sending gratuitous ARP on en…200
Hint: Some lines were ellipsized, use -l to show in full.
[root@proxy1 ~]#

BACKUP节点

[root@proxy2 ~]# vim /etc/keepalived/keepalived.conf
[root@proxy2 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id ka103          # 与对端不同
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens160         #  同上,改成实际网卡
    virtual_router_id 88     #  必须与 MASTER 一致
    priority 100             #  改成相同的数字
    advert_int 1    # 是 VRRP 报文的发送间隔,单位秒
    nopreempt # 加上这个参数更加稳妥
    
    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
[root@proxy2 ~]# systemctl restart keepalived
[root@proxy2 ~]# systemctl status keepalived --no-pager
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; preset: disabled)
     Active: active (running) since Wed 2026-03-25 15:58:41 CST; 4s ago
   Main PID: 33265 (keepalived)
      Tasks: 2 (limit: 22926)
     Memory: 1.8M
        CPU: 21ms
     CGroup: /system.slice/keepalived.service
             ├─33265 /usr/sbin/keepalived --dont-fork -D
             └─33266 /usr/sbin/keepalived --dont-fork -D

Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: Registering Kernel netlink r…tor
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: Registering Kernel netlink c…nel
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: Assigned address 192.168.88.…160
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: Registering gratuitous ARP s…nel
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: (VI_1) removing VIPs.
Mar 25 15:58:41 proxy2 Keepalived[33265]: Startup complete
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: (VI_1) removing VIPs.
Mar 25 15:58:41 proxy2 systemd[1]: Started LVS and VRRP High Availability …tor.
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: (VI_1) Entering BACKUP STATE…it)
Mar 25 15:58:41 proxy2 Keepalived_vrrp[33266]: VRRP sockpool: [ifindex(  2)…8)]
Hint: Some lines were ellipsized, use -l to show in full.
[root@proxy2 ~]#

Keepalived VIP 漂移

抢占模式

vim /etc/keepalived/keepalived.conf
state MASTER # proxy1
state BACKUP # proxy2
systemctl restart keepalived
[root@proxy1 ~]# grep state /etc/keepalived/keepalived.conf
    state MASTER
[root@proxy2 ~]# grep state /etc/keepalived/keepalived.conf
    state BACKUP
[root@proxy2 ~]#

MASTER 节点

[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59218  bytes 82175614 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22290  bytes 1399290 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]# systemctl stop keepalived
[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59290  bytes 82181608 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22340  bytes 1403718 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]#

BACKUP 节点

[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 60487  bytes 82026148 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23567  bytes 1512933 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 61031  bytes 82062642 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23614  bytes 1516235 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]#

MASTER 节点

[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59290  bytes 82181608 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22340  bytes 1403718 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]# systemctl start keepalived
[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59218  bytes 82175614 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22290  bytes 1399290 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

BACKUP 节点

[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 61031  bytes 82062642 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23614  bytes 1516235 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 60487  bytes 82026148 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23567  bytes 1512933 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

非抢占模式

vim /etc/keepalived/keepalived.conf
state BACKUP # proxy1
state BACKUP # proxy2

nopreempt # 加上这个参数更加稳妥

systemctl restart keepalived

注意:

把原来的 MASTER keepalived 配置文件里面的优先级改一下

优先级改为跟原来的 BACKUP 的优先级一样

把 priority 100 改为 priority 90

[root@proxy1 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id ka102          # 本机标识,两台不能重复
}

vrrp_instance VI_1 {
    # state MASTER
    state BACKUP
    interface ens160         #  <-- 改成实际网卡名
    virtual_router_id 88     #  0-255,两台必须一致
    priority 100             #  主节点更高
    advert_int 1    # 是 VRRP 报文的发送间隔,单位秒

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
[root@proxy1 ~]# vim /etc/keepalived/keepalived.conf
[root@proxy1 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id ka102          # 本机标识,两台不能重复
}

vrrp_instance VI_1 {
    # state MASTER
    state BACKUP
    interface ens160         #  <-- 改成实际网卡名
    virtual_router_id 88     #  0-255,两台必须一致
    priority 90             
    advert_int 1    # 是 VRRP 报文的发送间隔,单位秒

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
[root@proxy1 ~]# systemctl restart keepalived
[root@proxy1 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; preset: disabled)
     Active: active (running) since Tue 2026-05-26 12:07:25 CST; 3s ago
   Main PID: 42083 (keepalived)
      Tasks: 2 (limit: 22926)
     Memory: 1.8M
        CPU: 12ms
     CGroup: /system.slice/keepalived.service
             ├─42083 /usr/sbin/keepalived --dont-fork -D
             └─42084 /usr/sbin/keepalived --dont-fork -D

May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: Registering Kernel netlink reflector
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: Registering Kernel netlink command channel
May 26 12:07:25 proxy1 systemd[1]: Started LVS and VRRP High Availability Monitor.
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: Assigned address 192.168.88.102 for interface ens160
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: Registering gratuitous ARP shared channel
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: (VI_1) removing VIPs.
May 26 12:07:25 proxy1 Keepalived[42083]: Startup complete
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: (VI_1) removing VIPs.
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: (VI_1) Entering BACKUP STATE (init)
May 26 12:07:25 proxy1 Keepalived_vrrp[42084]: VRRP sockpool: [ifindex(  2), family(IPv4), proto(112), fd(12,13) multicast, add>
[root@proxy1 ~]#
[root@proxy1 ~]# grep state /etc/keepalived/keepalived.conf
    state BACKUP
[root@proxy2 ~]# grep state /etc/keepalived/keepalived.conf
    state BACKUP
[root@proxy2 ~]#

BACKUP 节点(原 MASTER 节点)

[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59218  bytes 82175614 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22290  bytes 1399290 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
[root@proxy1 ~]# systemctl stop keepalived
[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59290  bytes 82181608 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22340  bytes 1403718 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]# systemctl start keepalived # 再次启动keepalived
[root@proxy1 ~]# ifconfig # VIP不会被抢回来
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 59360  bytes 82187651 (78.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22371  bytes 1406868 (1.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]#

BACKUP 节点

[root@proxy2 ~]# ifconfig 
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 61058  bytes 82065039 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23778  bytes 1525711 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]# ifconfig 
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 61058  bytes 82065039 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23778  bytes 1525711 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]# ifconfig # VIP仍然在BACKUP节点
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 61058  bytes 82065039 (78.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23778  bytes 1525711 (1.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]#

让 Keepalived 与 CRMEB 建立联系(拓展)

第一步:更改 Windows 中域名解析( hosts 文件)

192.168.88.200   www.itheimashop.com

第二步:注释掉 keepalived 配置文件中的 vrrp_strict,如果不注释,VIP 无法 ping 通

特别注意:1.3 以后版本要注释 vrrp_strict,否则虚拟 IP 无法 ping 通

global_defs {
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 192.168.200.163
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   # vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

第三步:配置 proxy1 和 proxy2 代理到 CRMEB 的后端服务器

iptables 网络代理转发(拓展)

企业级高可用组件 Keepalived9.png

[root@server3 ~]# ls /etc/NetworkManager/system-connections/
ens160.nmconnection  ens224.nmconnection
[root@server3 ~]# cat /etc/NetworkManager/system-connections/ens160.nmconnection
[connection]
id=ens160
#uuid=39fc68b0-865c-3bd3-a42d-3e1998f8e2cd
type=ethernet
autoconnect-priority=-999
interface-name=ens160
timestamp=1752853607

[ethernet]

[ipv4]
#method=auto
method=manual
addresses=192.168.88.103/24
gateway=192.168.88.2
dns=192.168.88.2,114.114.114.114;

[ipv6]
#addr-gen-mode=eui64
#method=auto
method=disabled

[proxy]
[root@server3 ~]# cat /etc/NetworkManager/system-connections/ens224.nmconnection
[connection]
id=ens224
#uuid=39fc68b0-865c-3bd3-a42d-3e1998f8e2cd
type=ethernet
autoconnect-priority=-999
interface-name=ens160
timestamp=1752853607

[ethernet]

[ipv4]
method=auto

[ipv6]
#addr-gen-mode=eui64
#method=auto
method=disabled

[proxy]
[root@server3 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 23397  bytes 27937946 (26.6 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 12275  bytes 3541901 (3.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens224: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.11.32  netmask 255.255.255.0  broadcast 192.168.11.255
        ether 00:0c:29:e6:d9:8a  txqueuelen 1000  (Ethernet)
        RX packets 3694  bytes 2680395 (2.5 MiB)
        RX errors 0  dropped 180  overruns 0  frame 0
        TX packets 596  bytes 58983 (57.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@server3 ~]#
echo 1 > /proc/sys/net/ipv4/ip_forward

sed -i 's/net.ipv4.ip_forward = 0/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
sysctl -p

iptables -t nat -F

iptables -t nat -A PREROUTING -d 192.168.88.103 -p tcp --dport 80 -j DNAT --to-destination 192.168.11.22:80

iptables -t nat -A POSTROUTING -d 192.168.11.22 -p tcp --dport 80 -j SNAT --to-source 192.168.11.32

iptables -A FORWARD -j ACCEPT
[root@server3 ~]# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             server3              tcp dpt:http to:192.168.11.22:80

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
SNAT       tcp  --  anywhere             192.168.11.22        tcp dpt:http to:192.168.11.32
[root@server3 ~]#
# CentOS/RHEL7及之前
service iptables save
# 或者
iptables-save > /etc/sysconfig/iptables

# 带日期保存
iptables-save > /root/iptables-backup-$(date +%Y%m%d).rules

# 恢复规则
iptables-restore < /etc/sysconfig/iptables
iptables-restore < /root/iptables-backup-日期.rules
iptables-restore < /root/iptables-backup-20260114.rules

企业级高可用组件 Keepalived10.png

企业级高可用组件 Keepalived11.png

企业级高可用组件 Keepalived12.png

企业级高可用组件 Keepalived13.png

企业级高可用组件 Keepalived14.png

拓展操作

[root@server3 ~]# echo 1 > /proc/sys/net/ipv4/ip_forward
[root@server3 ~]# sed -i 's/net.ipv4.ip_forward = 0/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
[root@server3 ~]# sysctl -p
[root@server3 ~]# iptables -t nat -A POSTROUTING -d 192.168.11.0/24 -p tcp --dport 80 -j SNAT --to-source 192.168.11.60
[root@server3 ~]# iptables -t nat -A PREROUTING -d 192.168.88.103 -p tcp --dport 80 -j DNAT --to-destination 192.168.11.29:80
[root@server3 ~]# iptables -t nat -A PREROUTING -d 192.168.88.103 -p tcp --dport 80 -j DNAT --to-destination 192.168.11.198:80
[root@server3 ~]# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             server3              tcp dpt:http to:192.168.11.29:80
DNAT       tcp  --  anywhere             server3              tcp dpt:http to:192.168.11.198:80

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
SNAT       tcp  --  anywhere             192.168.11.0/24      tcp dpt:http to:192.168.11.60
[root@server3 ~]# iptables -t nat -D PREROUTING 1
[root@server3 ~]# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             server3              tcp dpt:http to:192.168.11.198:80

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
SNAT       tcp  --  anywhere             192.168.11.0/24      tcp dpt:http to:192.168.11.60
[root@server3 ~]# iptables -t nat -D POSTROUTING 1
[root@server3 ~]# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             server3              tcp dpt:http to:192.168.11.198:80

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
[root@server3 ~]#

实现 Nginx 服务 HA 高可用(重点)

优化 Linux 操作系统

sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F

HA 目标

  1. 任意一台 nginx 挂 → keepalived 立即降低权重/自杀 → VIP 漂到对端
  2. 两台 nginx 正常时,VIP 默认落在 proxy1(MASTER)
  3. 浏览器访问 http://vip 或域名即可看到后端服务器 192.168.88.101 的内容

HA 环境

  • 两台调度器:proxy1(102) / proxy2(103)
  • VIP:192.168.88.200
  • 后端真实 Web:192.168.88.101:80

企业级高可用组件 Keepalived15.png

特别注意:1.3以后版本要注释vrrp_strict,否则虚拟IP无法ping通

global_defs {
   notification_email {
     acassen@firewall.loc
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc
   smtp_server 192.168.200.163
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   # vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

后端域名解析

把业务域名解析到 VIP 即可,例如

192.168.88.200 www.itheimashop.com

C:\Windows\System32\drivers\etc

vim /etc/hosts

Windows和Linux的hosts文件都要配置域名解析,后续所有用户都访问域名,不再直接写 101。


nginx 层:两台代理服务器配置一模一样

记得两台代理服务器要安装Nginx

选择dnf/yum安装Nginx或者源码安装Nginx都可以

阿里源:

cat >/etc/yum.repos.d/aliyun.repo<<EOF
[baseos]
name=CentOS Stream \$releasever - BaseOS
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[baseos-debug]
name=CentOS Stream \$releasever - BaseOS - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[baseos-source]
name=CentOS Stream \$releasever - BaseOS - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream]
name=CentOS Stream \$releasever - AppStream
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[appstream-debug]
name=CentOS Stream \$releasever - AppStream - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream-source]
name=CentOS Stream \$releasever - AppStream - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb]
name=CentOS Stream \$releasever - CRB
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[crb-debug]
name=CentOS Stream \$releasever - CRB - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb-source]
name=CentOS Stream \$releasever - CRB - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability]
name=CentOS Stream \$releasever - HighAvailability
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[highavailability-debug]
name=CentOS Stream \$releasever - HighAvailability - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability-source]
name=CentOS Stream \$releasever - HighAvailability - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv]
name=CentOS Stream \$releasever - NFV
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[nfv-debug]
name=CentOS Stream \$releasever - NFV - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv-source]
name=CentOS Stream \$releasever - NFV - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt]
name=CentOS Stream \$releasever - RT
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[rt-debug]
name=CentOS Stream \$releasever - RT - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt-source]
name=CentOS Stream \$releasever - RT - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage]
name=CentOS Stream \$releasever - ResilientStorage
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[resilientstorage-debug]
name=CentOS Stream \$releasever - ResilientStorage - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage-source]
name=CentOS Stream \$releasever - ResilientStorage - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[extras-common]
name=CentOS Stream \$releasever - Extras packages
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/\$basearch/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[extras-common-source]
name=CentOS Stream \$releasever - Extras packages - Source
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/source/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
EOF

清华源:

cat >/etc/yum.repos.d/tsinghua.repo<<'EOF'
[baseos]
name=CentOS Stream $releasever - BaseOS
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/BaseOS/$basearch/os
# metalink=https://mirrors.centos.org/metalink?repo=centos-baseos-$stream&arch=$basearch&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[baseos-debuginfo]
name=CentOS Stream $releasever - BaseOS - Debug
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/BaseOS/$basearch/debug/tree/
# metalink=https://mirrors.centos.org/metalink?repo=centos-baseos-debug-$stream&arch=$basearch&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[baseos-source]
name=CentOS Stream $releasever - BaseOS - Source
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/BaseOS/source/tree/
# metalink=https://mirrors.centos.org/metalink?repo=centos-baseos-source-$stream&arch=source&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream]
name=CentOS Stream $releasever - AppStream
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/AppStream/$basearch/os
# metalink=https://mirrors.centos.org/metalink?repo=centos-appstream-$stream&arch=$basearch&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[appstream-debuginfo]
name=CentOS Stream $releasever - AppStream - Debug
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/AppStream/$basearch/debug/tree/
# metalink=https://mirrors.centos.org/metalink?repo=centos-appstream-debug-$stream&arch=$basearch&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream-source]
name=CentOS Stream $releasever - AppStream - Source
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/AppStream/source/tree/
# metalink=https://mirrors.centos.org/metalink?repo=centos-appstream-source-$stream&arch=source&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb]
name=CentOS Stream $releasever - CRB
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/CRB/$basearch/os
# metalink=https://mirrors.centos.org/metalink?repo=centos-crb-$stream&arch=$basearch&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[crb-debuginfo]
name=CentOS Stream $releasever - CRB - Debug
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/CRB/$basearch/debug/tree/
# metalink=https://mirrors.centos.org/metalink?repo=centos-crb-debug-$stream&arch=$basearch&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb-source]
name=CentOS Stream $releasever - CRB - Source
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-stream/$releasever-stream/CRB/source/tree/
# metalink=https://mirrors.centos.org/metalink?repo=centos-crb-source-$stream&arch=source&protocol=https,http
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
EOF
dnf install -y nginx
systemctl start nginx
systemctl enable nginx
systemctl status nginx

企业级高可用组件 Keepalived16.png

企业级高可用组件 Keepalived17.png

dnf/yum安装的nginx:

改 /etc/nginx/nginx.conf 配置

cp /etc/nginx/nginx.conf{,.bak}   # 可选备份

cat > /etc/nginx/nginx.conf <<'EOF'
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.88.101:80 max_fails=2 fail_timeout=3s;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
EOF

nginx -t
nginx -s reload

源码安装的nginx:

改 /usr/local/nginx/conf/nginx.conf 配置

cp /usr/local/nginx/conf/nginx.conf{,.bak}   # 可选备份

cat > /usr/local/nginx/conf/nginx.conf <<'EOF'
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.88.101:80 max_fails=2 fail_timeout=3s;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
EOF

nginx -t
nginx -s reload

对接后端多台服务器(可选)

worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.88.106:80 max_fails=2 fail_timeout=3s;
        server 192.168.88.107:80 max_fails=2 fail_timeout=3s;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

保存后两台都重新加载:

nginx -t   # 先检查语法
nginx -s reload # 重新加载nginx配置

http://192.168.88.200

企业级高可用组件 Keepalived18.png

http://www.itheimashop.com/

企业级高可用组件 Keepalived19.png


keepalived 层:引入 track_script 实时检查 nginx 存活

  1. nginx 检测脚本(两台路径一样,内容一样)

基础版(不结合weight降权)

开发和测试环境,我们使用这个基础版脚本,容易看到效果。

cat > /etc/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
# 如果 nginx 进程消失 或 80 端口不响应,就认为是 DOWN
if ! pgrep -x nginx &>/dev/null || ! ss -lnt | grep -q ':80'; then
    systemctl stop keepalived      # 释放 VIP
fi
EOF
chmod +x /etc/keepalived/check_nginx.sh

优化改进版(结合weight降权)

cat > /etc/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
# exit 0 = 健康,exit 1 = 故障(keepalived 自动降权触发 VRRP 切换)

pgrep -x nginx &>/dev/null \
    && curl -sf --max-time 2 http://127.0.0.1/ &>/dev/null \
    && exit 0

exit 1
EOF
对比项基础版优化改进版
故障处理方式systemctl stop keepalived,强制停止服务exit 1,通知 keepalived 自行处理
VIP 释放方式脚本主动释放keepalived 通过 VRRP 选举自然切换
自动恢复❌ nginx 恢复后需手动启动 keepalived✅ nginx 恢复后 keepalived 自动重新竞选
检测手段pgrep 进程 + ss 端口监听pgrep 进程 + curl HTTP 响应
检测准确性端口监听 ≠ 业务正常实际 HTTP 响应,更贴近真实可用性
结合 weight不支持(直接 stop,无优先级概念)支持,配合 weight -30 实现降权切换

核心区别一句话:基础版是脚本自己动手停服务,优化版是脚本汇报状态、让 keepalived 去决策。

  1. 修改主机配置文件(proxy1 —— MASTER)
cat > /etc/keepalived/keepalived.conf <<'EOF'
global_defs {
    router_id ka102
    # vrrp_strict          # <-- 必须注释
}

vrrp_script check_nginx {
    script       "/etc/keepalived/check_nginx.sh"
    interval     2          # 每 2 秒检测一次
    # weight       -30        # 检测失败时 priority 降低 30
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160
    virtual_router_id 88
    priority 100
    advert_int 1 # 每秒发一次心跳
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        check_nginx
    }
    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
EOF
  1. 修改备机配置文件(proxy2 —— BACKUP)
cat > /etc/keepalived/keepalived.conf <<'EOF'
global_defs {
    router_id ka103
    # vrrp_strict
}

vrrp_script check_nginx {
    script       "/etc/keepalived/check_nginx.sh"
    interval     2
    # weight       -30
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens160
    virtual_router_id 88
    priority 90
    advert_int 1 # 每秒发一次心跳
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        check_nginx
    }
    virtual_ipaddress {
        192.168.88.200/24 dev ens160 label ens160:1
    }
}
EOF
  1. 刷新当前keepalived和nginx的服务配置,严格按照顺序来执行(防止一启动就自杀)
# 两台都执行,先主后备
systemctl stop keepalived
nginx -s reload          # 确保 nginx 已运行  systemctl start nginx
systemctl status nginx
systemctl start keepalived
systemctl enable keepalived
systemctl status keepalived

验证高可用

  1. 浏览器访问 http://192.168.88.200 或者 http://www.itheimashop.com 预计能看到 192.168.88.101 的页面。

  2. 在 proxy1 手动关 nginx:systemctl stop nginx 或者 pkill nginx

    1. 2 秒内 check_nginx.sh 会触发 systemctl stop keepalived
    2. VIP 漂到 proxy2(ifconig 或者 ip a 能看到 ens160:1)
    3. 刷新浏览器,业务不中断
  3. 把 proxy1 的 nginx 重新启动,再启 keepalived,VIP 会回到 priority 更高的 proxy1(MASTER 默认抢占)。

http://192.168.88.200

企业级高可用组件 Keepalived20.png

http://www.itheimashop.com

企业级高可用组件 Keepalived21.png

分别检查主机和备机状态

[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 53194  bytes 71580572 (68.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 24155  bytes 3709381 (3.5 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 2  bytes 168 (168.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 168 (168.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]# systemctl status nginx
● nginx.service - Nginx Web Server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: d>
     Active: active (running) since Fri 2025-12-12 22:00:42 CST; 1h 3min ago
   Main PID: 9291 (nginx)
      Tasks: 3 (limit: 22927)
     Memory: 7.5M
        CPU: 191ms
     CGroup: /system.slice/nginx.service
             ├─9291 "nginx: master process /usr/local/nginx/sbin/nginx -c /usr>
             ├─9462 "nginx: worker process"
             └─9463 "nginx: worker process"

Dec 12 22:00:42 proxy1 systemd[1]: Starting Nginx Web Server...
Dec 12 22:00:42 proxy1 systemd[1]: Started Nginx Web Server.
[root@proxy1 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; pres>
     Active: active (running) since Fri 2025-12-12 22:58:30 CST; 5min ago
   Main PID: 9466 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 4.5M
        CPU: 3.070s
     CGroup: /system.slice/keepalived.service
             ├─9466 /usr/sbin/keepalived --dont-fork -D
             └─9467 /usr/sbin/keepalived --dont-fork -D

Dec 12 22:58:34 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:34 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:34 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:34 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:39 proxy1 Keepalived_vrrp[9467]: (VI_1) Sending/queueing gratuito>
Dec 12 22:58:39 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:39 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:39 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:39 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
Dec 12 22:58:39 proxy1 Keepalived_vrrp[9467]: Sending gratuitous ARP on ens160>
[root@proxy1 ~]#
[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 55455  bytes 71728550 (68.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 17647  bytes 3287463 (3.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]# systemctl status nginx
● nginx.service - Nginx Web Server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: d>
     Active: active (running) since Fri 2025-12-12 22:00:51 CST; 1h 3min ago
   Main PID: 9283 (nginx)
      Tasks: 3 (limit: 22927)
     Memory: 7.6M
        CPU: 91ms
     CGroup: /system.slice/nginx.service
             ├─9283 "nginx: master process /usr/local/nginx/sbin/nginx -c /usr>
             ├─9373 "nginx: worker process"
             └─9374 "nginx: worker process"

Dec 12 22:00:51 proxy2 systemd[1]: Starting Nginx Web Server...
Dec 12 22:00:51 proxy2 systemd[1]: Started Nginx Web Server.
[root@proxy2 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; pres>
     Active: active (running) since Fri 2025-12-12 22:58:42 CST; 5min ago
   Main PID: 9378 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 5.1M
        CPU: 3.315s
     CGroup: /system.slice/keepalived.service
             ├─9378 /usr/sbin/keepalived --dont-fork -D
             └─9379 /usr/sbin/keepalived --dont-fork -D

Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: SECURITY VIOLATION - scripts are>
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: Assigned address 192.168.88.103 >
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: Registering gratuitous ARP share>
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: (VI_1) removing VIPs.
Dec 12 22:58:42 proxy2 Keepalived[9378]: Startup complete
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: (VI_1) removing VIPs.
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: (VI_1) Entering BACKUP STATE (in>
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: VRRP sockpool: [ifindex(  2), fa>
Dec 12 22:58:42 proxy2 systemd[1]: Started LVS and VRRP High Availability Moni>
Dec 12 22:58:42 proxy2 Keepalived_vrrp[9379]: VRRP_Script(check_nginx) succeed>
[root@proxy2 ~]#

模拟故障,主机停掉nginx服务

[root@proxy1 ~]# systemctl stop nginx
[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 55960  bytes 74219443 (70.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 27783  bytes 6339362 (6.0 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 2  bytes 168 (168.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 168 (168.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]# systemctl status keepalived
○ keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; pres>
     Active: inactive (dead) since Fri 2025-12-12 23:05:50 CST; 4s ago
   Duration: 7min 18.109s
    Process: 9466 ExecStart=/usr/sbin/keepalived --dont-fork $KEEPALIVED_OPTIO>
   Main PID: 9466 (code=exited, status=0/SUCCESS)
        CPU: 3.975s

Dec 12 23:05:49 proxy1 systemd[1]: Stopping LVS and VRRP High Availability Mon>
Dec 12 23:05:49 proxy1 Keepalived[9466]: Stopping
Dec 12 23:05:49 proxy1 Keepalived_vrrp[9467]: (VI_1) sent 0 priority
Dec 12 23:05:49 proxy1 Keepalived_vrrp[9467]: (VI_1) removing VIPs.
Dec 12 23:05:50 proxy1 Keepalived_vrrp[9467]: Stopped - used (self/children) 0>
Dec 12 23:05:50 proxy1 Keepalived[9466]: CPU usage (self/children) user: 0.004>
Dec 12 23:05:50 proxy1 Keepalived[9466]: Stopped Keepalived v2.2.8 (04/04,2023>
Dec 12 23:05:50 proxy1 systemd[1]: keepalived.service: Deactivated successfull>
Dec 12 23:05:50 proxy1 systemd[1]: Stopped LVS and VRRP High Availability Moni>
Dec 12 23:05:50 proxy1 systemd[1]: keepalived.service: Consumed 3.975s CPU tim>
[root@proxy1 ~]#

在备机检查服务状态,发现VIP已经漂移到备机上了

[root@proxy2 ~]# systemctl status nginx
● nginx.service - Nginx Web Server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: d>
     Active: active (running) since Fri 2025-12-12 22:00:51 CST; 1h 5min ago
   Main PID: 9283 (nginx)
      Tasks: 3 (limit: 22927)
     Memory: 7.6M
        CPU: 91ms
     CGroup: /system.slice/nginx.service
             ├─9283 "nginx: master process /usr/local/nginx/sbin/nginx -c /usr>
             ├─9373 "nginx: worker process"
             └─9374 "nginx: worker process"

Dec 12 22:00:51 proxy2 systemd[1]: Starting Nginx Web Server...
Dec 12 22:00:51 proxy2 systemd[1]: Started Nginx Web Server.
[root@proxy2 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; pres>
     Active: active (running) since Fri 2025-12-12 22:58:42 CST; 7min ago
   Main PID: 9378 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 5.1M
        CPU: 4.313s
     CGroup: /system.slice/keepalived.service
             ├─9378 /usr/sbin/keepalived --dont-fork -D
             └─9379 /usr/sbin/keepalived --dont-fork -D

Dec 12 23:05:49 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:49 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:49 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:49 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: (VI_1) Sending/queueing gratuito>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 55649  bytes 71742448 (68.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 17794  bytes 3303425 (3.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]#

http://192.168.88.200

企业级高可用组件 Keepalived22.png

CRMEB商城业务不受影响!

抢修主机,发现VIP又漂移到主机上了

[root@proxy1 ~]# systemctl start nginx
[root@proxy1 ~]# systemctl status nginx
● nginx.service - Nginx Web Server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: d>
     Active: active (running) since Fri 2025-12-12 23:08:18 CST; 3s ago
    Process: 10417 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, st>
    Process: 10419 ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/c>
   Main PID: 10420 (nginx)
      Tasks: 3 (limit: 22927)
     Memory: 2.9M
        CPU: 15ms
     CGroup: /system.slice/nginx.service
             ├─10420 "nginx: master process /usr/local/nginx/sbin/nginx -c /us>
             ├─10421 "nginx: worker process"
             └─10422 "nginx: worker process"

Dec 12 23:08:18 proxy1 systemd[1]: Starting Nginx Web Server...
Dec 12 23:08:18 proxy1 systemd[1]: Started Nginx Web Server.
[root@proxy1 ~]# systemctl start keepalived
[root@proxy1 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; pres>
     Active: active (running) since Fri 2025-12-12 23:08:27 CST; 4s ago
   Main PID: 10427 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 2.2M
        CPU: 61ms
     CGroup: /system.slice/keepalived.service
             ├─10427 /usr/sbin/keepalived --dont-fork -D
             └─10428 /usr/sbin/keepalived --dont-fork -D

Dec 12 23:08:29 proxy1 Keepalived_vrrp[10428]: (VI_1) received lower priority >
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: (VI_1) Receive advertisement ti>
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: (VI_1) Entering MASTER STATE
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: (VI_1) setting VIPs.
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: (VI_1) Sending/queueing gratuit>
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: Sending gratuitous ARP on ens16>
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: Sending gratuitous ARP on ens16>
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: Sending gratuitous ARP on ens16>
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: Sending gratuitous ARP on ens16>
Dec 12 23:08:30 proxy1 Keepalived_vrrp[10428]: Sending gratuitous ARP on ens16>
[root@proxy1 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.102  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)
        RX packets 56169  bytes 74237055 (70.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 27920  bytes 6356664 (6.0 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.200  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:50:56:3e:be:ee  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 2  bytes 168 (168.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 168 (168.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy1 ~]#

备机已释放VIP

[root@proxy2 ~]# systemctl status nginx
● nginx.service - Nginx Web Server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: d>
     Active: active (running) since Fri 2025-12-12 22:00:51 CST; 1h 8min ago
   Main PID: 9283 (nginx)
      Tasks: 3 (limit: 22927)
     Memory: 7.6M
        CPU: 91ms
     CGroup: /system.slice/nginx.service
             ├─9283 "nginx: master process /usr/local/nginx/sbin/nginx -c /usr>
             ├─9373 "nginx: worker process"
             └─9374 "nginx: worker process"

Dec 12 22:00:51 proxy2 systemd[1]: Starting Nginx Web Server...
Dec 12 22:00:51 proxy2 systemd[1]: Started Nginx Web Server.
[root@proxy2 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
     Loaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; pres>
     Active: active (running) since Fri 2025-12-12 22:58:42 CST; 10min ago
   Main PID: 9378 (keepalived)
      Tasks: 2 (limit: 22927)
     Memory: 5.1M
        CPU: 6.171s
     CGroup: /system.slice/keepalived.service
             ├─9378 /usr/sbin/keepalived --dont-fork -D
             └─9379 /usr/sbin/keepalived --dont-fork -D

Dec 12 23:05:49 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: (VI_1) Sending/queueing gratuito>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:05:54 proxy2 Keepalived_vrrp[9379]: Sending gratuitous ARP on ens160>
Dec 12 23:08:30 proxy2 Keepalived_vrrp[9379]: (VI_1) Master received advert fr>
Dec 12 23:08:30 proxy2 Keepalived_vrrp[9379]: (VI_1) Entering BACKUP STATE
Dec 12 23:08:30 proxy2 Keepalived_vrrp[9379]: (VI_1) removing VIPs.
[root@proxy2 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 55792  bytes 71753169 (68.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 18009  bytes 3320995 (3.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@proxy2 ~]#

http://192.168.88.200

企业级高可用组件 Keepalived23.png

CRMEB商城业务不受影响!


拓展测试

对接同一个教室里的 Web 服务

[root@proxy1 nginx]# vim nginx.conf
[root@proxy1 nginx]# cat nginx.conf
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.11.29:80 max_fails=2 fail_timeout=3s;
        server 192.168.11.187:80 max_fails=2 fail_timeout=3s;
        server 192.168.11.198:80 max_fails=2 fail_timeout=3s;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
[root@proxy1 nginx]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@proxy1 nginx]# nginx -s reload

[root@proxy2 nginx]# vim nginx.conf
[root@proxy2 nginx]# cat nginx.conf
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.11.29:80 max_fails=2 fail_timeout=3s;
        server 192.168.11.187:80 max_fails=2 fail_timeout=3s;
        server 192.168.11.198:80 max_fails=2 fail_timeout=3s;

    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
[root@proxy2 nginx]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@proxy2 nginx]# nginx -s reload

企业级高可用组件 Keepalived24.png

企业级高可用组件 Keepalived25.png

企业级高可用组件 Keepalived26.png

企业级高可用组件 Keepalived27.png

企业级高可用组件 Keepalived28.png

企业级高可用组件 Keepalived29.png

企业级高可用组件 Keepalived30.png

iptables网络代理转发

[root@proxy1 nginx]# vim nginx.conf
[root@proxy1 nginx]# cat nginx.conf
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.88.103:80 max_fails=2 fail_timeout=3s;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
[root@proxy1 nginx]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@proxy1 nginx]# nginx -s reload

[root@proxy2 nginx]# cat nginx.conf
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    upstream web_pool {
        server 192.168.88.103:80 max_fails=2 fail_timeout=3s;

    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://web_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
[root@proxy2 nginx]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@proxy2 nginx]# nginx -s reload
[root@server3 ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.88.103  netmask 255.255.255.0  broadcast 192.168.88.255
        ether 00:50:56:36:7d:75  txqueuelen 1000  (Ethernet)
        RX packets 91554  bytes 93244053 (88.9 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 53207  bytes 17528569 (16.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens224: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.11.60  netmask 255.255.255.0  broadcast 192.168.11.255
        ether 00:0c:29:e6:d9:8a  txqueuelen 1000  (Ethernet)
        RX packets 41754  bytes 10073211 (9.6 MiB)
        RX errors 0  dropped 893  overruns 0  frame 0
        TX packets 3890  bytes 411878 (402.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 72  bytes 24386 (23.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 72  bytes 24386 (23.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@server3 ~]#
echo 1 > /proc/sys/net/ipv4/ip_forward

sed -i 's/net.ipv4.ip_forward = 0/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
sysctl -p

iptables -t nat -F

iptables -t nat -A PREROUTING -d 192.168.88.103 -p tcp --dport 80 -j DNAT --to-destination 192.168.11.29:80

iptables -t nat -A POSTROUTING -d 192.168.11.29 -p tcp --dport 80 -j SNAT --to-source 192.168.11.60

iptables -A FORWARD -j ACCEPT
[root@server3 ~]# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             server3              tcp dpt:http to:192.168.11.29:80

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
SNAT       tcp  --  anywhere             192.168.11.29        tcp dpt:http to:192.168.11.60
[root@server3 ~]#

企业级高可用组件 Keepalived31.png

企业级高可用组件 Keepalived32.png

经典 iptables NAT 实战

企业级高可用组件 Keepalived33.png

echo 1 > /proc/sys/net/ipv4/ip_forward

sed -i 's/net.ipv4.ip_forward = 0/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
sysctl -p

iptables -t nat -F

iptables -t nat -A PREROUTING -d 192.168.88.100 -p tcp --dport 80 -j DNAT --to-destination 192.168.19.99:80

iptables -t nat -A POSTROUTING -d 192.168.19.99 -p tcp --dport 80 -j SNAT --to-source 192.168.88.100

iptables -A FORWARD -j ACCEPT

企业级高可用组件 Keepalived34.png

企业级高可用组件 Keepalived35.png

企业级高可用组件 Keepalived36.png

常用排错命令

journalctl -u keepalived -f          # 实时看漂移日志
ss -lnt | grep :80                   # 确认本机 80 监听
ip a show ens160                     # 看 VIP 在哪台
systemctl status nginx keepalived

至此,“nginx+keepalived” 高可用 就完整落地了!

  • 硬件故障 → VIP 漂
  • nginx 服务故障 → 检测脚本立即降权/自杀 → VIP 漂
  • 用户始终通过域名/VIP 访问,后端真实 Web 服务器对客户端完全透明。

Keepalived 的配置补充(了解)

非抢占模式

为了提升用户访问体验,避免因 **VIP(虚拟 IP)**频繁切换导致的服务短暂不可用或 HTTP 请求异常,我们采用 非抢占式(nopreempt)模式 来控制 VIP 的归属,确保 VIP 不会因节点恢复而发生不必要的切换。

第一步:在 vrrp_instance 块下两个节点各增加了 nopreempt 指令,表示不争抢 VIP

vrrp_instance VI_1 {
        virtual_router_id 51
        nopreempt
        priority 100
}

第二步:节点的state都为BACKUP

vrrp_instance VI_1 {
        state BACKUP
}

两个keepalived节点都启动后,默认都是BACKUP状态,双方在发送组播信息后,会根据优先级来选举一个MASTER出来。由于两者都配置了nopreempt,所以MASTER从故障中恢复后,不会抢占vip。这样会避免VIP切换可能造成的服务延迟。

非抢占模式,就是为了用户体验更加。服务恢复服务之后,不要抢占VIP。

nopreempt

empt 在拉丁文中是“拿走”的意思。

pre 表示“预先的,提前的”。比如 prepare 准备,preview 预览;

preempt 表示“提前拿走,抢占”。nopreempt 表示 “非抢占”。

VIP 脑裂

脑裂(split-brain):指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏。(MASTER-BACKUP,两台机器上同时拥有VIP)

主要原因:

1、网络通信故障;

2、防火墙未关闭或防火墙规则配置不正确;

查看日志:

# tail -f /var/log/messages

使用tcpdump抓取vrrp数据包:

# yum install tcpdump -y
# tcpdump -i ens160 vrrp -n
-i:interface 指定网卡
-n:number 以数字形式显示IP地址

VIP所在主机 => 发送vrrp数据包 => 组播地址 => 224.0.0.18

企业级高可用组件 Keepalived37.png

防火墙配置:

# 允许VRRP协议
firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'

# 添加组播地址
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="224.0.0.18" accept'

# 允许Keepalived服务
firewall-cmd --permanent --add-service=high-availability

# 重新加载防火墙
firewall-cmd --reload

firewall-cmd命令
    --permanent:永久
    --add-rich-rule:添加防火墙规则
rule family="ipv4" destination address="224.0.0.18" accept:针对组播地址224.0.0.18
    --add-service:根据名称设置防火墙规则,high-availability防火墙预定服务名,是包括keepalived在内的所有高可用软件统称
    --add-port:根据端口号设置防火墙规则
firewall-cmd --reload:刷新防火墙,让规则立即生效

总结

什么是脑裂?

答:在一个集群中,可能出现两个VIP

产生脑裂根本原因?

答:集群中的各个节点可能由于网络通信故障导致连接不上主节点

脑裂影响?

答:因为有两个VIP,所有请求会出现争抢情况,严重可能会造成数据损坏!

解决方案:

恢复网络通信(防火墙、SELinux、配置文件)

单播模式

广播 > 组播 > 单播

广播:类似屏幕广播,把信息发送广播地址(本网段的.255)

组播:范围略小于广播,只有同组节点才能收到组播信息(keepalived默认情况 => 224.0.0.18)

单播:范围最小,仅局限于点对点传输信息(master => backup)

一方面,keepalived在组播模式下所有的信息都会向224.0.0.18的组播地址发送,产生众多的无用信息,并且会产生干扰和冲突。另一方面,在某些特定环境(云服务器)禁止了组播方式,造成没办法获取到MASTER的数据包信息。

所以需要将其组播的模式改为单播。这是一种安全的方法,避免局域网内有大量的keepalived造成虚拟路由id的冲突(VIP脑裂)。 单播模式需要关闭vrrp_strict这个选项。单播需要在VIP实例配置段加入单播的源地址和目标地址

单播模式的配置,是需要在keepalived1.2.10(含)以上版本

vrrp_instance VI_1 {
    unicast_src_ip 192.168.88.128 ##(本地IP地址)
    unicast_peer {
       192.168.88.129                          ##(对端IP地址)此地址一定不能忘记,支持多台机器
    }
}

注:
针对于web01 unicast_src_ip 就是192.168.88.101   unicast_peer 192.168.88.103
针对于web02 unicast_src_ip 就是192.168.88.103   unicast_peer 192.168.88.101

第一步:需要使用高一点版本的keepalived(1.2.11)

# cd /root/soft
# tar xvf keepalived-1.2.11.tar.gz
# cd keepalived-1.2.11
# ./configure --prefix=/usr/local/keepalived
# make && make install

第二步:查看目录,配置文件和启动文件

注意如果使用的是1.3以上版本的话,注意注释模式:

删除vrrp_strict

# /usr/local/keepalived/sbin/keepalived -f /usr/local/keepalived/etc/keepalived/keepalived.conf -D

master:keepalived.conf

vrrp_instance VI_1 {
        unicast_src_ip 192.168.88.101
    unicast_peer {
       192.168.88.103
    }
}

注:
针对于web01 unicast_src_ip 就是192.168.88.101   unicast_peer 192.168.88.103
针对于web02 unicast_src_ip 就是192.168.88.103   unicast_peer 192.168.88.101

backup:keepalived.conf

vrrp_instance VI_1 {
        unicast_src_ip 192.168.88.103
    unicast_peer {
       192.168.88.101
    }
}

注:
针对于web01 unicast_src_ip 就是192.168.88.101   unicast_peer 192.168.88.103
针对于web02 unicast_src_ip 就是192.168.88.103   unicast_peer 192.168.88.101

裂:vip出现在了多台机器上。网络不通畅,禁用了数据包,主备服务器没法通讯,造成备服务器认为主服务器不可用,绑定VIP,主服务器VIP不会释放。

① 双备或者多备模式 BACKUP 通过priority权重来区分谁的优先级更高

② 单播的方式

③ 时间不同步 服务器时间校时

删库跑路?

企业级高可用组件 Keepalived38.jpg
企业级高可用组件 Keepalived39.jpg
企业级高可用组件 Keepalived40.png

备份:

mkdir /backup
mysqldump -uroot -pMySQL@666 --all-databases --single-transaction --routines --triggers --events > /backup/all_$(date +%Y%m%d_%H%M%S).sql
mysqldump \
  -uroot \                          # 指定用户名
  -pMySQL@666 \                     # 指定密码
  --all-databases \                 # 备份所有数据库(含建库语句)
  --single-transaction \            # 开启事务快照,备份期间不锁表
  --routines \                      # 备份存储过程和函数
  --triggers \                      # 备份触发器
  --events \                        # 备份事件调度器
  > /backup/all_$(date +%Y%m%d_%H%M%S).sql   # 输出到带时间戳的文件

企业级高可用组件 Keepalived41.png

企业级高可用组件 Keepalived42.png

企业级高可用组件 Keepalived43.png

恢复:

mysql -uroot -pMySQL@666 < /backup/all_20260526_164149.sql

企业级高可用组件 Keepalived44.png

后端 MySQL 的主从架构升级(拓展)

主从同步(复制)原理 :

主服务器开启bin-log(记录了写操作) 从服务器获取到主服务器的bin-log 记录到relay-log中

从服务器在通过异步的线程方式,对于relay-log进行重放操作

IO线程去主服务器binlog日志拷贝 => 写入到从服务器的relay log中

SQL线程根据relay log变化,自动去执行拷贝过来DML语句,重演

主服务器: bin-log

从服务器: relay-log

DML => MASTER SQL => binlog日志中

SLAVE SQL => 监听MASTER binlog日志变化,一旦监测到主服务器发生变化 => 通过网络IO线程复制到SLAVE从服务器中,放入relaylog中继日志 => SLAVE使用SQL线程重演MASTER SQL操作

从库 MySQL 安装

上传安装脚本和软件包从库服务器

[root@slave ~]# cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
port=3306
log-error=/export/server/mysql/slave.err
log-bin=/export/server/mysql/data/binlog
relay-log=/export/server/mysql/data/relaylog
server-id=20 
character_set_server=utf8mb4
gtid_mode=on  
enforce_gtid_consistency=on
log_slave_updates=1
read-only=on
EOF
[root@slave ~]# systemctl restart mysqld
[root@slave ~]# systemctl status mysqld --no-pager

修改 MySQL 配置

主库停止服务
[root@master ~]# systemctl stop mysqld # 可选
[root@master ~]# cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
sql_mode=NO_ENGINE_SUBSTITUTION
port=3306
log-error=/export/server/mysql/master.err
log-bin=/export/server/mysql/data/binlog
server-id=10
character_set_server=utf8mb4
gtid_mode=on
enforce_gtid_consistency=on
log_slave_updates=1
EOF
[root@master ~]# systemctl restart mysqld
[root@master ~]# systemctl status mysqld --no-pager
    
数据同步

逻辑导出(适合中小型库)

# 主库执行
[root@master ~]# mysqldump -u root -p --all-databases --single-transaction --master-data=2 --set-gtid-purged=ON > /tmp/full.sql

解释说明:
# MySQL 主库全量一致性热备份(主从复制专用)
mysqldump \
-u root \                  # 指定MySQL登录用户 root
-p \                       # 提示输入密码,不显示明文
--all-databases \          # 备份所有数据库
--single-transaction \     # InnoDB 一致性热备份,不锁表、不影响业务
--master-data=2 \          # 【关键】将binlog位置写入备份文件
                          # =1:会生成可执行的 CHANGE MASTER TO 语句(导入时自动执行)
                          # =2:会生成注释形式的 CHANGE MASTER TO 语句(仅记录、不自动执行,更安全)
--set-gtid-purged=ON \     # 记录GTID事务号,主从自动识别同步起点
> /tmp/full.sql            # 备份输出到 /tmp/full.sql

# 拷贝到从库
[root@master ~]# scp /tmp/full.sql root@192.168.88.133:/tmp/

# 从库执行
[root@slave ~]# mysql -u root -pMySQL@666 < /tmp/full.sql

主库授权
[root@master ~]# systemctl start mysqld
[root@master ~]# mysql -u root -pMySQL@666

mysql> CREATE USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY 'MySQL@666';
mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
mysql> flush privileges;

从库 Slave 服务器进行主从同步

[root@slave ~]# systemctl start mysqld
[root@slave ~]# mysql -uroot -pMySQL@666

mysql> change replication source to
  source_host='192.168.88.101',
  source_port=3306,
  source_user='slave',
  source_password='MySQL@666',
  source_auto_position=1;
  
mysql> start replica;
mysql> show slave status\G

企业级高可用组件 Keepalived45.png

企业级高可用组件 Keepalived46.png

企业级高可用组件 Keepalived47.png

企业级高可用组件 Keepalived48.png

企业级高可用组件 Keepalived49.png

拓展:更改数据库地址

企业级高可用组件 Keepalived50.png

企业级高可用组件 Keepalived51.png

企业级高可用组件 Keepalived52.png

企业级高可用组件 Keepalived53.png