Shell脚本实现双机热备切换:原理、实践与故障自救全攻略
📖 文章目录导读
- 双机热备的本质与适用场景
- Shell脚本实现心跳检测的三种核心方法
- 从0到1:完整的热备切换Shell脚本范例
- 切换过程中的常见陷阱与避坑指南
- 高频问答(FAQ)
- 总结与最佳实践建议
双机热备的本质与适用场景
1 什么是双机热备?
双机热备(Active-Standby)是指两台服务器,一台作为主节点(Active)提供服务,另一台作为备用节点(Standby)实时监控主节点状态,当主节点宕机或服务异常时,备用节点自动接管虚拟IP、服务进程及数据,实现故障切换时间小于30秒的RTO目标。

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隔离),欢迎在评论区留言,我们将针对您的拓扑提供定制化方案。