Shell脚本如何检测主备服务状态:高可用架构中的自动化监控实践
目录导读

核心概念:主备服务与状态检测的必要性
在分布式系统或高可用集群中,主备服务 通常指两台或多台服务器承担相同角色:一台处于“主”(Active)状态处理请求,其余处于“备”(Standby)状态等待接管,当主节点宕机、网络中断或服务异常时,自动或手动触发切换,备机接管成为新的主节点。
为什么需要Shell脚本检测?
- 实时性:Shell脚本可结合cron定时任务或systemd timer,实现秒级到分钟级的周期性检查。
- 轻量级:相比重量级监控平台(如Zabbix、Prometheus),Shell脚本无需额外依赖,适合嵌入到已有运维体系。
- 灵活定制:可根据业务特征自定义检测逻辑(如检查端口、进程、心跳文件、数据库查询结果等)。
检测的核心目标:快速判断当前“主”节点是否存活(Alive)且功能正常(Healthy),从而触发故障转移或告警。
基础检测方法:端口、进程与心跳报文
1 端口连通性检测(最常用)
通过 nc(netcat)、telnet 或 /dev/tcp 特性检查服务端口是否响应。
# 使用bash内置的/dev/tcp(更高效,无需额外命令)
if timeout 3 bash -c "echo > /dev/tcp/192.168.1.10/3306" 2>/dev/null; then
echo "MySQL主节点端口可达"
fi
注意:端口可达不代表服务正常(如端口未关闭但已死锁),需配合后续方法验证。
2 进程PID文件检测
许多服务会生成PID文件(如 /var/run/nginx.pid),检测PID文件存在且进程活跃:
pid=$(cat /var/run/service.pid 2>/dev/null) && kill -0 $pid 2>/dev/null && echo "进程运行中"
局限性:无法检测服务是否响应请求,仅代表进程未崩溃。
3 心跳包检测(主备间专用)
在主备节点之间通过自定义心跳脚本或工具(如keepalived、heartbeat)发送UDP/TCP心跳报文,例如利用ping检测对端IP可达性:
if ping -c 1 -W 2 192.168.1.11 &>/dev/null; then
echo "备机网络可达"
fi
实战脚本:基于TCP端口的双机状态检测
以下是一个完整的Shell脚本示例,用于检测主备两台服务器的Nginx服务状态,并输出日志:
#!/bin/bash
# 主备检测脚本:check_ha_status.sh
# 检测目标:主节点 192.168.1.10:80,备节点 192.168.1.11:80
PRIMARY_IP="192.168.1.10"
BACKUP_IP="192.168.1.11"
PORT="80"
LOG_FILE="/var/log/ha_health.log"
# 函数:检测单节点TCP端口是否通畅
check_port() {
local ip=$1
local port=$2
timeout 2 bash -c "echo > /dev/tcp/$ip/$port" 2>/dev/null && return 0 || return 1
}
# 主节点状态检测
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始检测主备服务状态..." >> $LOG_FILE
if check_port $PRIMARY_IP $PORT; then
echo "主节点 $PRIMARY_IP:$PORT 端口可达,服务运行正常。" >> $LOG_FILE
# 此处可添加更细粒度检测:如请求主页获取返回码
# curl -s -o /dev/null -w "%{http_code}" http://$PRIMARY_IP | grep -q 200 && echo "HTTP状态正常" >> $LOG_FILE
else
echo "主节点 $PRIMARY_IP:$PORT 端口不可达,可能宕机!" >> $LOG_FILE
# 触发备升主逻辑(示例:发送告警邮件并尝试切换)
if check_port $BACKUP_IP $PORT; then
echo "备节点 $BACKUP_IP:$PORT 端口可达,建议执行主备切换。" >> $LOG_FILE
# 调用切换脚本,如:bash switch_over.sh
else
echo "备节点 $BACKUP_IP:$PORT 同样不可达,需人工介入!" >> $LOG_FILE
fi
fi
执行方式:通过crontab每分钟运行 * * * * * /path/to/check_ha_status.sh。
进阶方案:通过API与数据库健康检查验证
仅靠端口检测可能忽略“假死”情况(端口占住但业务不可用),可增加业务层面验证:
1 HTTP API健康检查(专用于Web应用)
# 请求主节点的健康检查接口(如 /healthz)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://$PRIMARY_IP/healthz)
if [ "$HTTP_CODE" != "200" ]; then
echo "主节点HTTP服务返回非200状态码,服务异常!"
fi
2 数据库主从状态检测(MySQL示例)
# 登录主节点检测MySQL可写性(需配置无密码登录或使用mysql_config_editor)
mysql -h $PRIMARY_IP -u monitor -e "SELECT 1 FROM dual;" 2>/dev/null
if [ $? -ne 0 ]; then
echo "主节点MySQL数据库连接失败!"
fi
3 复合检测策略(推荐)
在单个脚本中组合多项检测,使用加权或优先级判定:
# 伪代码:先端口,再API,最后写入测试
if ! check_port $PRIMARY_IP 80; then
echo "端口通?否 → 直接判定主节点宕机"
elif ! curl_check $PRIMARY_IP; then
echo "端口通,但API失败 → 服务异常,考虑切换"
else
echo "一切正常"
fi
常见问题解答(FAQ)
Q1:为什么不能只用ping检测主备?
A:ping仅验证网络连通性,无法判断目标服务进程是否存活,服务器网络正常但Nginx已崩溃,ping仍返回成功,导致误判。
Q2:脚本中timeout命令不存在怎么办?
A:可以使用gtimeout(macOS)或通过&后台进程配合kill模拟超时,更轻量的方式:利用bash内置的/dev/tcp配合重定向的等待时间限制(如timeout 2 bash -c "echo > /dev/tcp/ip/port")。
Q3:检测到主节点故障后,自动切换如何做?
A:切换通常涉及修改虚拟IP(VIP)漂移(如通过ip addr add和arping),或更新DNS记录,建议使用成熟的工具如Keepalived或Heartbeat,Shell脚本仅作为补充触发条件。
Q4:脚本运行报错“Permission denied”怎么办?
A:检查脚本是否为可执行文件(chmod +x file),且执行用户对日志目录有写入权限,若涉及sudo操作(如修改VIP),需在/etc/sudoers中配置免密执行。
Q5:如何避免检测脚本自身影响主备性能? A:设置合理的超时时间(建议3-5秒),避免使用高频率cron(如每分钟一次对大多数服务足够),若检测量大,可改用Go或Python编写非阻塞检测程序。
总结与最佳实践建议
Shell脚本检测主备服务状态的核心在于多维度交叉验证,避免单一手段的盲区,以下是生产环境中的关键要点:
- 优先使用
/dev/tcp而非nc/telnet:bash内置方式更轻量,且避免额外命令依赖。 - 结合业务定制健康检查:如数据库服务需检测只读状态(备机通常为只读),Web服务需检查200响应码。
- 日志与告警分离:状态变化写入日志,异常时通过
mail、slack或企业微信机器人发送告警。 - 避免“脑裂”陷阱:主备同时认为自己是主时,需引入仲裁机制(如第三方存储锁或至少三节点投票)。
- 测试脚本的鲁棒性:在备机宕机、网络分区、连接超时、主备角色翻转等场景下,验证脚本输出是否符合预期。
通过上述方法论,您已能编写一个贴合实际业务的主备状态检测Shell脚本。监控的目的是预防,而非事后补救,定期检查并优化检测策略是实现高可用的基石。