Shell脚本如何检测主备服务状态

wen 实用脚本 1

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

目录导读

  1. 核心概念:主备服务与状态检测的必要性
  2. 基础检测方法:端口、进程与心跳报文
  3. 实战脚本:基于TCP端口的双机状态检测
  4. 进阶方案:通过API与数据库健康检查验证
  5. 常见问题解答(FAQ)
  6. 总结与最佳实践建议

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 心跳包检测(主备间专用)

在主备节点之间通过自定义心跳脚本或工具(如keepalivedheartbeat)发送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 addarping),或更新DNS记录,建议使用成熟的工具如KeepalivedHeartbeat,Shell脚本仅作为补充触发条件。

Q4:脚本运行报错“Permission denied”怎么办? A:检查脚本是否为可执行文件(chmod +x file),且执行用户对日志目录有写入权限,若涉及sudo操作(如修改VIP),需在/etc/sudoers中配置免密执行。

Q5:如何避免检测脚本自身影响主备性能? A:设置合理的超时时间(建议3-5秒),避免使用高频率cron(如每分钟一次对大多数服务足够),若检测量大,可改用Go或Python编写非阻塞检测程序。


总结与最佳实践建议

Shell脚本检测主备服务状态的核心在于多维度交叉验证,避免单一手段的盲区,以下是生产环境中的关键要点:

  1. 优先使用/dev/tcp而非nc/telnet:bash内置方式更轻量,且避免额外命令依赖。
  2. 结合业务定制健康检查:如数据库服务需检测只读状态(备机通常为只读),Web服务需检查200响应码
  3. 日志与告警分离:状态变化写入日志,异常时通过mailslack企业微信机器人发送告警。
  4. 避免“脑裂”陷阱:主备同时认为自己是主时,需引入仲裁机制(如第三方存储锁或至少三节点投票)。
  5. 测试脚本的鲁棒性:在备机宕机、网络分区、连接超时、主备角色翻转等场景下,验证脚本输出是否符合预期。

通过上述方法论,您已能编写一个贴合实际业务的主备状态检测Shell脚本。监控的目的是预防,而非事后补救,定期检查并优化检测策略是实现高可用的基石。

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