Shell脚本如何实现双机热备切换

wen 实用脚本 1

Shell脚本实现双机热备切换:原理、实践与故障自救全攻略

📖 文章目录导读

  1. 双机热备的本质与适用场景
  2. Shell脚本实现心跳检测的三种核心方法
  3. 从0到1:完整的热备切换Shell脚本范例
  4. 切换过程中的常见陷阱与避坑指南
  5. 高频问答(FAQ)
  6. 总结与最佳实践建议

双机热备的本质与适用场景

1 什么是双机热备?

双机热备(Active-Standby)是指两台服务器,一台作为主节点(Active)提供服务,另一台作为备用节点(Standby)实时监控主节点状态,当主节点宕机或服务异常时,备用节点自动接管虚拟IP、服务进程及数据,实现故障切换时间小于30秒的RTO目标。

Shell脚本如何实现双机热备切换

2 为什么选择Shell脚本而非商业软件?

  • 零成本:无需购买Keepalived、Heartbeat等授权
  • 轻量级:仅依赖Bash、cron、ip命令,适合嵌入式或资源受限环境
  • 可定制性:业务级健康检查(如MySQL慢查询导致响应超时,脚本可精确探测)

3 适用场景清单

  • 数据库主备(MySQL/MariaDB)
  • Nginx/反向代理层高可用
  • NoSQL(Redis哨兵模式下配合脚本)
  • 内部业务API网关

Shell脚本实现心跳检测的三种核心方法

🔍 方法一:ICMP Ping检测(最基础)

ping -c 2 -W 3 $MASTER_IP > /dev/null 2>&1
if [ $? -ne 0 ]; then
    # 切换逻辑
fi

陷阱:防火墙可能禁ping,需配合端口检测

🔍 方法二:TCP端口检测(可靠)

timeout 2 bash -c "echo > /dev/tcp/$MASTER_IP/$PORT" 2>/dev/null
if [ $? -ne 0 ]; then
    # 触发切换
fi

优势:精确判断服务是否监听端口

🔍 方法三:应用层HTTP状态码检测(最精准)

HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 http://$MASTER_IP:$PORT/health)
if [ "$HTTP_CODE" != "200" ]; then
    echo "Health check failed" >> /var/log/ha.log
fi

适合API网关、Web应用


从0到1:完整的热备切换Shell脚本范例

以下脚本实现了“双节点+浮动IP”模型,支持3次重试确认,并通过日志输出状态。

#!/bin/bash
# File: /usr/local/bin/ha_switch.sh
# 双机热备切换脚本 V2.1
# 适用:Nginx主备 + 浮动IP
# ---------- 配置参数 ----------
MY_ROLE=${1:-"standby"}        # 脚本执行时传入 "master" 或 "standby"
MASTER_IP="192.168.1.10"
STANDBY_IP="192.168.1.11"
VIP="192.168.1.100"
PORT="80"
HEALTH_INTERFACE="eth0"
LOG_FILE="/var/log/ha_switch.log"
RETRY_COUNT=3                  # 检测失败重试次数
SLEEP_BETWEEN_CHECKS=5        # 每次检测间隔(秒)
# ---------- 函数定义 ----------
log_msg() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [$1] $2" >> $LOG_FILE
}
check_master_health() {
    local healthy=0
    for ((i=1; i<=RETRY_COUNT; i++)); do
        # 检测方式:端口检测
        timeout 2 bash -c "echo > /dev/tcp/$MASTER_IP/$PORT" 2>/dev/null
        if [ $? -eq 0 ]; then
            healthy=1
            break
        fi
        sleep $SLEEP_BETWEEN_CHECKS
    done
    return $healthy
}
takeover_vip() {
    # 尝试绑定VIP(若已被其他节点持有则失败)
    ip addr add $VIP/32 dev $HEALTH_INTERFACE 2>/dev/null
    if [ $? -eq 0 ]; then
        log_msg "INFO" "Switch: VIP $VIP taken over on interface $HEALTH_INTERFACE"
        # 发送ARP广播更新交换机表
        arping -c 3 -A -I $HEALTH_INTERFACE $VIP >/dev/null 2>&1
        # 启动本地服务(假设Nginx)
        systemctl start nginx
        return 0
    else
        log_msg "WARN" "VIP $VIP already exists on other node, abort takeover"
        return 1
    fi
}
release_vip() {
    ip addr del $VIP/32 dev $HEALTH_INTERFACE 2>/dev/null
    log_msg "INFO" "VIP $VIP released"
}
# ---------- 主逻辑 ----------
if [ "$MY_ROLE" == "master" ]; then
    # 主节点:持续检测自己,如果服务崩溃则释放VIP
    check_master_health
    if [ $? -ne 1 ]; then
        log_msg "FAIL" "Local service down,releasing VIP"
        release_vip
        # 通知备机(通过SSH触发,需提前配置免密)
        ssh $STANDBY_IP "/usr/local/bin/ha_switch.sh standby" &
    fi
elif [ "$MY_ROLE" == "standby" ]; then
    # 备节点:检测主节点健康
    check_master_health
    if [ $? -ne 1 ]; then
        log_msg "WARN" "Master $MASTER_IP unreachable,attempting takeover"
        takeover_vip
    else
        # 若主恢复,可主动释放VIP(视架构决定)
        log_msg "INFO" "Master healthy,stay standby"
    fi
fi

脚本使用说明

  • 定时执行:crontab添加每10秒运行 */10 * * * * /usr/local/bin/ha_switch.sh master (主节点)和 */10 * * * * /usr/local/bin/ha_switch.sh standby(备节点)
  • 故障模拟:在Master上停止Nginx → 观察VIP是否漂移到Standby

切换过程中的常见陷阱与避坑指南

🚩 陷阱1:脑裂(Split-Brain)

现象:两台节点同时认为自己是主,均持有VIP。
解决方案

  • takeover_vip中使用ARP广播+检查VIP冲突
  • 引入权重机制:ID小的节点优先级更高
  • 关键:加入多链路心跳检测(如同时使用USB串口线心跳)

🚩 陷阱2:网络抖动导致误切换

现象:单次丢包即触发切换,服务反复震荡。
解决方案
脚本中的RETRY_COUNT=3 + SLEEP_BETWEEN_CHECKS=5 确保万无一失。
可额外增加累计阈值:例如连续5次检测失败才切换。

🚩 陷阱3:数据持久化不一致

警告:纯Shell脚本默认不处理数据同步!
必须配合

  • MySQL:半同步复制 + 手动设置sync_binlog=1
  • 文件服务:rsync + inotify实时同步

高频问答(FAQ)

Q1:Shell脚本热备与Keepalived相比,核心劣势是什么?

A:Keepalived实现了VRRP协议,故障切换 无锁竞争(谁先发广播谁胜出);而本例脚本依赖cron轮询,存在 1~10秒检测窗口,若要求毫秒级切换(如金融交易),必须用Keepalived。

Q2:如何避免脚本重复执行?

A:使用PID文件锁

LOCK_FILE="/var/run/ha_switch.pid"
if [ -f $LOCK_FILE ]; then
    exit 0
fi
echo $$ > $LOCK_FILE
trap "rm -f $LOCK_FILE" EXIT  # 脚本退出自动清理

Q3:如果双机网络断开但主服务正常,脚本如何处理?

A:此时Standby检测Master失败→抢夺VIP→形成双主服务。预警方案:部署独立的心跳线(交叉网线)作为备用检测通道,脚本添加if [ 主网不通 && 心跳线不通 ]才切换。

Q4:脚本如何适配云服务器(如AWS、阿里云)?

A:云环境禁止ARP广播,需改为调用云API(如阿里云AssociateEipAddress)绑定弹性公网IP,示例:

aliyuncli ecs AssociateEipAddress --InstanceId $INSTANCE_ID --AllocationId $EIP_ID

总结与最佳实践建议

Shell脚本实现双机热备切换的核心价值在于 “轻量、可控、零成本” ,适合中小规模业务或临时应急方案,但在关键生产环境(RTO<5秒、强一致性场景)仍需使用工业级工具如Keepalived、Pacemaker。

GitHub开源增强版参考

  • ha-shell-plus:已支持多节点、SSH免密通知、健康检查插件化
  • 使用trap信号处理避免僵尸进程

📌 最后检查清单

  • ✅ 关闭sudo密码执行权限(chmod u+s /bin/ip
  • ✅ 日志文件设置日志轮转(logrotate)
  • ✅ 监控脚本自身CPU/内存占用,防止资源泄漏

如果您在部署过程中遇到特殊网络场景(如多网卡绑定、VLAN隔离),欢迎在评论区留言,我们将针对您的拓扑提供定制化方案。

抱歉,评论功能暂时关闭!