Keepalived 主备—双主案例

Keepalived 笔记

适用版本:Keepalived 2.x | 系统:CentOS 8 / Ubuntu 22.04


目录

  1. 简介
  2. 安装
  3. 配置文件结构
  4. VRRP 核心概念
  5. 基础实战:从零搭建主备高可用
  6. 主备模式完整配置
  7. 双主模式实战
  8. 健康检查脚本
  9. 通知脚本
  10. 与 HAProxy 联动
  11. 常用命令
  12. 故障排查

1. 简介

Keepalived 基于 VRRP 协议(Virtual Router Redundancy Protocol)实现服务器高可用,核心功能是管理一个虚拟 IP(VIP),在多台服务器之间自动漂移,对外始终提供同一个 IP 地址。

典型用途:

  • HAProxy / Nginx 双机热备
  • LVS 高可用
  • 任意服务的 IP 层冗余

工作原理:

正常状态:
  VIP 192.168.1.100 → MASTER(优先级高)
  BACKUP 静默监听心跳

主节点故障:
  BACKUP 超时未收到心跳 → 接管 VIP(约 3~6 秒)

主节点恢复:
  MASTER 重新广播 → 抢占 VIP(可配置是否抢占)

2. 安装

# CentOS / RHEL
dnf install keepalived -y

# Ubuntu / Debian
apt install keepalived -y

# 验证版本
keepalived --version

# 启用并启动
systemctl enable --now keepalived

3. 配置文件结构

主配置文件:/etc/keepalived/keepalived.conf

keepalived.conf
├── global_defs        # 全局参数
├── vrrp_script        # 健康检查脚本(可多个)
└── vrrp_instance      # VRRP 实例(可多个,对应多个 VIP)

4. VRRP 核心概念

概念 说明
virtual_router_id VRRP 组 ID,同组主备必须一致,取值 1~255
priority 优先级,越高越优先成为 MASTER,取值 1~254
state 初始角色:MASTERBACKUP
advert_int 心跳发送间隔(秒),默认 1 秒
virtual_ipaddress VIP 地址,可配置多个
interface 绑定的网卡名称
preempt / nopreempt 是否抢占,默认抢占
authentication 同组节点认证,防止误加入

优先级与选举规则:

  • 优先级高的节点成为 MASTER
  • 优先级相同时,IP 地址大的优先
  • weight 配合 vrrp_script 动态调整优先级

5. 基础实战:从零搭建主备高可用

本节以两台 CentOS 8 虚拟机为例,手把手完成一次完整的 Keepalived 主备部署,涵盖环境准备、安装、配置、验证全流程。

5.1 实验环境

角色 IP 主机名 说明
MASTER 192.168.1.1 node1 主节点,正常持有 VIP
BACKUP 192.168.1.2 node2 备节点,主节点故障时接管
VIP 192.168.1.100 对外服务的虚拟 IP
网卡 eth0 两台机器同网段

两台机器需在同一局域网/VLAN,能互相 ping 通。


5.2 第一步:环境准备

两台机器都执行:

# 设置主机名(node1 上执行)
hostnamectl set-hostname node1

# 设置主机名(node2 上执行)
hostnamectl set-hostname node2

# 互相添加 hosts 解析(两台都执行)
cat >> /etc/hosts << EOF
192.168.1.1  node1
192.168.1.2  node2
EOF

# 验证互通
ping -c 3 node2    # 在 node1 上执行
ping -c 3 node1    # 在 node2 上执行

查看本机网卡名(后续配置需要):

ip addr
# 找到对应网段的网卡名,如 eth0 / ens33 / enp0s3

放行 VRRP 协议(两台都执行):

# CentOS / firewalld
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload
firewall-cmd --list-all | grep vrrp   # 确认已生效

# Ubuntu / ufw
iptables -A INPUT -p 112 -j ACCEPT
iptables -A OUTPUT -p 112 -j ACCEPT

5.3 第二步:安装 Keepalived

两台机器都执行:

# CentOS 8
dnf install keepalived -y

# Ubuntu
apt install keepalived -y

# 验证安装
keepalived --version

5.4 第三步:编写健康检查脚本

本实验用 Nginx 作为被守护的服务。

两台机器都执行:

# 安装 Nginx
dnf install nginx -y       # CentOS
# apt install nginx -y     # Ubuntu

# 启动 Nginx
systemctl enable --now nginx

# 确认 Nginx 正常
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1
# 输出 200 表示正常

创建健康检查脚本(两台都执行):

cat > /etc/keepalived/check_nginx.sh << 'EOF'
#!/bin/bash
# 检查 Nginx 是否存活,失败时尝试重启

if systemctl is-active nginx > /dev/null 2>&1; then
    exit 0
else
    systemctl restart nginx
    sleep 2
    if systemctl is-active nginx > /dev/null 2>&1; then
        exit 0
    else
        exit 1   # 重启失败 → 触发 VIP 漂移
    fi
fi
EOF

chmod +x /etc/keepalived/check_nginx.sh

# 手动测试脚本
bash /etc/keepalived/check_nginx.sh
echo "exit code: $?"   # 0 表示正常

5.5 第四步:配置 Keepalived

node1(主节点):

cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
    router_id node1
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0              # 替换为你的网卡名
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass Keepalived123
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}
EOF

node2(备节点):

cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
    router_id node2
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP                # 备节点
    interface eth0              # 替换为你的网卡名
    virtual_router_id 51        # 与主节点一致
    priority 90                 # 低于主节点
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass Keepalived123  # 与主节点一致
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}
EOF

创建通知脚本(两台都执行):

cat > /etc/keepalived/notify.sh << 'EOF'
#!/bin/bash
STATE=$1
HOSTNAME=$(hostname)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] $HOSTNAME → $STATE" >> /var/log/keepalived-state.log
EOF

chmod +x /etc/keepalived/notify.sh

5.6 第五步:启动并验证

两台机器都执行:

# 检查配置语法
keepalived --config-test -f /etc/keepalived/keepalived.conf

# 启动
systemctl enable --now keepalived

# 查看状态
systemctl status keepalived

验证 VIP 在主节点:

# 在 node1 上执行,应能看到 192.168.1.100
ip addr show eth0 | grep 192.168.1.100

# 在 node2 上执行,不应有 VIP
ip addr show eth0 | grep 192.168.1.100

访问 VIP 确认服务正常:

curl http://192.168.1.100
# 应返回 Nginx 欢迎页

5.7 第六步:模拟故障切换

测试一:停止主节点 Keepalived

# node1 上执行
systemctl stop keepalived

# 等待约 6 秒后,node2 上检查 VIP
ip addr show eth0 | grep 192.168.1.100
# → 应出现 192.168.1.100

# 访问 VIP 仍然正常
curl http://192.168.1.100

# 恢复主节点
systemctl start keepalived

# 约 6 秒后 VIP 漂回 node1
ip addr show eth0 | grep 192.168.1.100

测试二:停止 Nginx 触发健康检查

⚠️ 直接 systemctl stop nginx 不管用:check_nginx.sh 检测到故障会立刻重启,
Nginx 永远停不下来。需要用以下任一方式绕过。

方法一:屏蔽 Nginx,让脚本重启也失败(推荐)

# node1 上执行
# mask 后 systemctl restart nginx 也会失败,脚本无法拉起
systemctl mask nginx
systemctl stop nginx

# 此时脚本检测到 Nginx 停止 → 尝试 restart → 失败(被 mask)→ exit 1
# 观察 Keepalived 日志
journalctl -u keepalived -f

# 同时另开终端,node2 上等待 VIP 出现(约 6~10 秒)
ip addr show eth0 | grep 192.168.1.100

# 查看状态切换日志
cat /var/log/keepalived-state.log

# 恢复:解除 mask,VIP 会漂回 node1
systemctl unmask nginx
systemctl start nginx

方法二:修改脚本让它直接返回失败(不重启)

# node1 上临时替换脚本内容,让它直接返回失败
cat > /etc/keepalived/check_nginx.sh << 'EOF'
#!/bin/bash
exit 1   # 强制返回失败,模拟服务宕机
EOF

# 等待 fall 3 次触发(约 6 秒),观察 VIP 漂移
journalctl -u keepalived -f

# node2 查看是否接管 VIP
ip addr show eth0 | grep 192.168.1.100

# 恢复:还原脚本,VIP 漂回 node1
cat > /etc/keepalived/check_nginx.sh << 'EOF'
#!/bin/bash
if systemctl is-active nginx > /dev/null 2>&1; then
    exit 0
else
    systemctl restart nginx
    sleep 2
    systemctl is-active nginx > /dev/null 2>&1 && exit 0 || exit 1
fi
EOF

方法三:kill 掉 Nginx 主进程,速度比脚本检测快

# 连续执行,让脚本来不及在 fall 计数归零前重拉起来
# 先记录 pid,循环 kill
while true; do
    PID=$(pgrep -x nginx | head -1)
    [ -n "$PID" ] && kill -9 $PID
    sleep 0.5
done
# Ctrl+C 停止循环后 Nginx 才能稳定存活

实际生产中方法一最常用,systemctl mask 也是模拟"服务彻底崩溃无法恢复"场景的标准做法。

预期结果:

node1 check_nginx.sh 返回非0 × 3次(fall 3)
→ node1 优先级 100 - 20 = 80
→ 低于 node2 的 90
→ VIP 漂移到 node2

恢复后:
node1 check_nginx.sh 返回0 × 2次(rise 2)
→ node1 优先级恢复 100
→ VIP 漂回 node1

5.8 实验总结

步骤 关键点
环境准备 同网段互通,防火墙放行 VRRP(112协议)
健康检查脚本 退出码 0=正常,非0=异常,脚本需有执行权限
主备配置差异 statepriorityrouter_id 三处不同,其余完全一致
virtual_router_id 主备必须相同,同网络内不能重复
auth_pass 主备必须相同,防止其他节点误加入
故障切换时间 advert_int × fall ≈ 漂移时间,默认约 3 秒

6. 主备模式完整配置

环境:

VIP    : 192.168.1.100
MASTER : 192.168.1.1(priority 100)
BACKUP : 192.168.1.2(priority 90)
网卡   : eth0

主节点配置

/etc/keepalived/keepalived.conf

global_defs {
    router_id MASTER_NODE        # 节点标识,每台唯一
    script_user root
    enable_script_security
}

vrrp_script check_service {
    script "/etc/keepalived/check_service.sh"
    interval 2      # 每 2 秒执行一次
    weight -20      # 脚本失败时优先级 -20
    fall 3          # 连续 3 次失败才触发降权
    rise 2          # 连续 2 次成功才恢复
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51         # 主备必须一致
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass YourPassword123
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_service
    }
}

备节点配置

仅修改两处,其余完全相同:

global_defs {
    router_id BACKUP_NODE        # 改为不同标识
}

vrrp_instance VI_1 {
    state BACKUP                 # MASTER → BACKUP
    priority 90                  # 低于主节点
    # 其余参数完全相同
}

验证 VIP

# 主节点查看 VIP
ip addr show eth0 | grep 192.168.1.100

# 停止主节点 Keepalived,观察备节点是否接管
systemctl stop keepalived

# 备节点应出现 VIP
ip addr show eth0 | grep 192.168.1.100

7. 双主模式实战

两台服务器各持有一个 VIP,互为对方的备节点,两台机器同时承载流量,充分利用硬件资源。

7.1 实验环境

角色 IP 持有 VIP 业务说明
Node1 192.168.1.1 VIP1: 192.168.1.100 Web 流量入口
Node2 192.168.1.2 VIP2: 192.168.1.101 API 流量入口
网卡 eth0 两台同网段

正常状态:

Web 流量  → VIP1(192.168.1.100) → Node1
API 流量  → VIP2(192.168.1.101) → Node2

Node1 故障时:

Web 流量  → VIP1(192.168.1.100) → Node2(接管)
API 流量  → VIP2(192.168.1.101) → Node2(不变)

7.2 第一步:安装依赖

两台机器都执行:

# 安装 Keepalived
dnf install keepalived -y       # CentOS
# apt install keepalived -y     # Ubuntu

# 安装邮件发送工具
dnf install mailx postfix -y    # CentOS
# apt install mailutils postfix -y  # Ubuntu

#安装Postfix 登录邮箱用的组件

yum install -y cyrus-sasl cyrus-sasl-plain cyrus-sasl-lib

# 放行 VRRP 协议
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload

7.3 第二步:配置邮件告警

使用外部 SMTP(以 QQ邮箱为例)发送告警邮件。

两台机器都执行:

# 配置 Postfix 使用外部 SMTP 中继
cat > /etc/postfix/main.cf << 'EOF'

# SMTP 中继配置
relayhost = [smtp.qq.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = yes
smtp_tls_wrappermode = yes
smtp_tls_security_level = encrypt
# 强制关闭IPv6!!
inet_protocols = ipv4       

EOF

# 配置 SMTP 认证信息
cat > /etc/postfix/sasl_passwd << 'EOF'
[smtp.qq.com]:587  28329954**@qq.com:sfjoh**rgftudcdc
EOF

# 生成哈希文件并设置权限
postmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

# 重启 Postfix
systemctl enable --now postfix
systemctl restart postfix

测试邮件是否能发出:

echo "Keepalived 邮件告警测试" | mail -s "测试邮件" -r 28329954**@qq.com 28329954**@qq.com

echo "正文" | mail -s "标题" -r 发件人 收件人

# 查看发送日志
tail -f /var/log/maillog        # CentOS
# tail -f /var/log/mail.log     # Ubuntu

常见问题:163/QQ 邮箱需要在网页端开启「SMTP 服务」并生成授权码,sasl_passwd 里填授权码而非登录密码。

编写邮件告警脚本(两台机器都执行):

cat > /etc/keepalived/notify_email.sh << 'EOF'
#!/bin/bash

# ====== 配置区 ======
FROM="28329954**@qq.com"       # 发件人
TO="28329954**@qq.com"      # 收件人,多个用空格分隔
# ====================

INSTANCE=$1    # VRRP 实例名,如 VI_1
STATE=$2       # 状态:MASTER / BACKUP / FAULT
HOSTNAME=$(hostname)
IP=$(hostname -I | awk '{print $1}')
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# 状态说明
case $STATE in
    MASTER)  DESC="【接管】本机成为 MASTER,已获取 VIP" ;;
    BACKUP)  DESC="【释放】本机降为 BACKUP,已释放 VIP" ;;
    FAULT)   DESC="【故障】健康检查失败,服务异常!" ;;
    *)       DESC="状态变更:$STATE" ;;
esac

SUBJECT="[Keepalived] $HOSTNAME $INSTANCE → $STATE"

BODY=$(cat << MAIL
Keepalived 状态变更通知
=======================
主机名   : $HOSTNAME
IP 地址  : $IP
实例     : $INSTANCE
新状态   : $STATE
说明     : $DESC
时间     : $TIMESTAMP
=======================
请登录服务器确认当前 VIP 分布:
  ip addr show eth0 | grep -E '100|101'
MAIL
)

# 写入本地日志
echo "[$TIMESTAMP] $HOSTNAME $INSTANCE → $STATE" >> /var/log/keepalived-state.log

# 发送邮件
echo "$BODY" | mail -s "$SUBJECT" -r "$FROM" $TO

EOF

chmod +x /etc/keepalived/notify_email.sh

# 手动测试告警脚本
bash /etc/keepalived/notify_email.sh VI_1 MASTER
# 检查收件箱是否收到邮件

7.4 第三步:编写健康检查脚本

两台机器都执行:

cat > /etc/keepalived/check_nginx.sh << 'EOF'
#!/bin/bash
if systemctl is-active nginx > /dev/null 2>&1; then
    exit 0
else
    systemctl restart nginx
    sleep 2
    systemctl is-active nginx > /dev/null 2>&1 && exit 0 || exit 1
fi
EOF

chmod +x /etc/keepalived/check_nginx.sh

7.5 第四步:配置 Keepalived

Node1 配置 /etc/keepalived/keepalived.conf

global_defs {
    router_id node1
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

# ===== 实例1:Node1 为 MASTER,持有 VIP1 =====
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass VIPass51
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify_email.sh VI_1 MASTER"
    notify_backup "/etc/keepalived/notify_email.sh VI_1 BACKUP"
    notify_fault  "/etc/keepalived/notify_email.sh VI_1 FAULT"
}

# ===== 实例2:Node1 为 BACKUP,备份 VIP2 =====
vrrp_instance VI_2 {
    state BACKUP
    interface eth0
    virtual_router_id 52
    priority 90
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass VIPass52
    }

    virtual_ipaddress {
        192.168.1.101/24
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify_email.sh VI_2 MASTER"
    notify_backup "/etc/keepalived/notify_email.sh VI_2 BACKUP"
    notify_fault  "/etc/keepalived/notify_email.sh VI_2 FAULT"
}

Node2 配置 /etc/keepalived/keepalived.conf

global_defs {
    router_id node2
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

# ===== 实例1:Node2 为 BACKUP,备份 VIP1 =====
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 90
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass VIPass51
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify_email.sh VI_1 MASTER"
    notify_backup "/etc/keepalived/notify_email.sh VI_1 BACKUP"
    notify_fault  "/etc/keepalived/notify_email.sh VI_1 FAULT"
}

# ===== 实例2:Node2 为 MASTER,持有 VIP2 =====
vrrp_instance VI_2 {
    state MASTER
    interface eth0
    virtual_router_id 52
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass VIPass52
    }

    virtual_ipaddress {
        192.168.1.101/24
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify_email.sh VI_2 MASTER"
    notify_backup "/etc/keepalived/notify_email.sh VI_2 BACKUP"
    notify_fault  "/etc/keepalived/notify_email.sh VI_2 FAULT"
}

7.6 第五步:启动并验证初始状态

# 两台都执行:检查语法
keepalived --config-test -f /etc/keepalived/keepalived.conf

# 两台都执行:启动
systemctl enable --now keepalived

# Node1 上应持有 VIP1,不持有 VIP2
ip addr show eth0 | grep -E '100|101'
# 预期:192.168.1.100 ✅   192.168.1.101 ❌

# Node2 上应持有 VIP2,不持有 VIP1
ip addr show eth0 | grep -E '100|101'
# 预期:192.168.1.100 ❌   192.168.1.101 ✅

7.7 第六步:故障切换验证

测试一:Node1 整机故障(停止 Keepalived)

# Node1 上执行
systemctl stop keepalived

# Node2 上约 6 秒后应同时持有两个 VIP
ip addr show eth0 | grep -E '100|101'
# 预期:192.168.1.100 ✅   192.168.1.101 ✅

# 同时检查收件箱,应收到两封告警邮件:
# [Keepalived] node2 VI_1 → MASTER
# [Keepalived] node2 VI_2 → (不变,本来就是 MASTER)

# 恢复 Node1
systemctl start keepalived

# 约 6 秒后 VIP1 漂回 Node1,收到恢复邮件
ip addr show eth0 | grep -E '100|101'

测试二:Node1 Nginx 故障(用 mask 防止脚本重拉)

# Node1 上执行
systemctl mask nginx

创建软链接 /etc/systemd/system/nginx.service → /dev/null
永久锁定禁用服务,禁止任何方式启动(手动 / 开机自启 / 别的程序拉起全都无效)
systemctl stop nginx

# 观察 Keepalived 日志(等待 fall 3 × interval 2 = 6 秒)
journalctl -u keepalived -f

# Node1 优先级:100 - 20 = 80 < Node2 的 90
# VIP1 应漂移到 Node2
ip addr show eth0 | grep 192.168.1.100   # 在 Node2 上执行

# 检查邮件告警
cat /var/log/keepalived-state.log

# 恢复
systemctl unmask nginx
systemctl start nginx
# VIP1 漂回 Node1,再次收到恢复邮件

7.8 VIP 分布速查

# 两台机器同时检查,一目了然
echo "=== Node1 ===" && ssh node1 "ip addr show eth0 | grep -E '100|101'"
echo "=== Node2 ===" && ssh node2 "ip addr show eth0 | grep -E '100|101'"

# 查看状态切换历史
cat /var/log/keepalived-state.log

7.9 双主实验总结

配置项 VI_1 VI_2
Node1 角色 MASTER(priority 100) BACKUP(priority 90)
Node2 角色 BACKUP(priority 90) MASTER(priority 100)
virtual_router_id 51 52
auth_pass VIPass51 VIPass52
VIP 192.168.1.100 192.168.1.101

两个实例的 virtual_router_idauth_pass 必须各自不同,否则两个实例会互相干扰。


8. 健康检查脚本

脚本退出码:0 = 健康,非 0 = 不健康(触发优先级降低)。

检查任意进程

#!/bin/bash
# /etc/keepalived/check_service.sh

SERVICE="haproxy"

if systemctl is-active $SERVICE > /dev/null 2>&1; then
    exit 0
else
    # 尝试重启
    systemctl restart $SERVICE
    sleep 2
    systemctl is-active $SERVICE > /dev/null 2>&1 && exit 0 || exit 1
fi

检查端口是否监听

#!/bin/bash
# /etc/keepalived/check_port.sh

PORT=80

if ss -tlnp | grep -q ":$PORT "; then
    exit 0
else
    exit 1
fi

检查 HTTP 接口

#!/bin/bash
# /etc/keepalived/check_http.sh

URL="http://127.0.0.1/health"

HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 $URL)

if [ "$HTTP_CODE" = "200" ]; then
    exit 0
else
    exit 1
fi
# 脚本授权
chmod +x /etc/keepalived/check_*.sh

9. 通知脚本

状态切换时自动执行脚本,常用于发送告警通知。

vrrp_instance VI_1 {
    ...
    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}

/etc/keepalived/notify.sh

#!/bin/bash

STATE=$1
HOSTNAME=$(hostname)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

log() {
    echo "[$TIMESTAMP] [$HOSTNAME] Keepalived state: $1" >> /var/log/keepalived-notify.log
}

case $STATE in
    MASTER)
        log "Became MASTER - VIP acquired"
        # 可在此发送钉钉/企微/邮件告警
        ;;
    BACKUP)
        log "Became BACKUP - VIP released"
        ;;
    FAULT)
        log "FAULT - health check failed"
        ;;
esac
chmod +x /etc/keepalived/notify.sh

10. 与 HAProxy 联动

Keepalived 监控 HAProxy 进程,HAProxy 故障时触发 VIP 漂移。

global_defs {
    router_id HAPROXY_MASTER
    script_user root
    enable_script_security
}

vrrp_script check_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_HAProxy {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass HAPasswd123
    }

    virtual_ipaddress {
        192.168.1.100/24
    }

    track_script {
        check_haproxy
    }

    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}
#!/bin/bash
# /etc/keepalived/check_haproxy.sh
if systemctl is-active haproxy > /dev/null 2>&1; then
    exit 0
else
    systemctl restart haproxy
    sleep 2
    systemctl is-active haproxy > /dev/null 2>&1 && exit 0 || exit 1
fi

11. 常用命令

# 启动 / 停止 / 重启 / 重载
systemctl start keepalived
systemctl stop keepalived
systemctl restart keepalived
systemctl reload keepalived      # 重载配置,不中断 VIP

# 查看运行状态
systemctl status keepalived

# 查看日志
journalctl -u keepalived -f
journalctl -u keepalived --since "1 hour ago"

# 查看当前 VIP 是否在本机
ip addr show eth0 | grep <VIP>

# 查看 VRRP 组播(抓包确认心跳正常)
tcpdump -i eth0 -nn vrrp

# 手动触发主备切换(临时降低优先级)
# 主节点执行,让备节点接管
systemctl stop keepalived

12. 故障排查

常见问题

现象 原因 解决方案
两台都成为 MASTER(脑裂) 网络不通 / 认证不一致 检查防火墙放行 VRRP(112协议),核对 auth_pass
VIP 不漂移 virtual_router_id 不一致 确保主备该值完全相同
频繁主备切换 健康检查脚本不稳定 增大 fall 值,检查脚本执行时间
主节点恢复后不抢占 配置了 nopreempt 确认是否需要抢占,去掉或保留 nopreempt
脚本无效果 脚本无执行权限 chmod +x 并检查 script_user 配置

排查步骤

# 1. 检查配置文件语法
keepalived --config-test -f /etc/keepalived/keepalived.conf

# 2. 查看实时日志
journalctl -u keepalived -f

# 3. 确认防火墙放行 VRRP 协议(必须)
# CentOS
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload
# Ubuntu
iptables -A INPUT -p 112 -j ACCEPT

# 4. 抓包验证心跳
tcpdump -i eth0 -nn vrrp

# 5. 手动测试健康检查脚本
bash /etc/keepalived/check_haproxy.sh
echo "exit code: $?"

# 6. 查看当前 VIP 持有情况
ip addr | grep -E 'eth0|VIP地址'

防火墙配置(必做)

Keepalived 使用 VRRP 协议(IP 协议号 112) 和多播地址 224.0.0.18 通信,必须放行:

# CentOS / firewalld
firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
firewall-cmd --reload

# Ubuntu / ufw
ufw allow proto ah
# 或直接用 iptables
iptables -A INPUT -p 112 -j ACCEPT
iptables -A OUTPUT -p 112 -j ACCEPT

Keepalived 官方文档:https://www.keepalived.org/doc/

posted @ 2026-06-02 11:06  陪你去流浪  阅读(39)  评论(0)    收藏  举报