5.8.3 服务状态检测与自愈脚本

服务状态检测与自愈脚本用于持续监测服务健康并在异常时自动恢复,降低人工介入与故障时长。典型流程包含:状态采集→阈值判断→恢复动作→验证→告警。脚本需满足幂等、超时可控、重启频率限制与可审计。

原理草图(检测—自愈—告警链路):

文章图片

安装与准备#

示例以 systemd 服务与常见工具为基础,先确认工具可用并安装:

# 基础工具
sudo yum install -y curl net-tools procps-ng bc
# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y curl net-tools procps bc

命令解释:
- curl:HTTP/健康检查。
- net-toolsnetstat 端口检测(可替代为 ss)。
- procps-ngps/top 进程检测。
- bc:用于浮点计算与阈值判断。

关键命令与检测方式(含示例)#

1) 进程检测

# 通过进程名检测(返回0表示存在)
pgrep -f "nginx: master"
echo $?

# 通过pid文件检测
test -f /run/nginx.pid && ps -p $(cat /run/nginx.pid)

解释:pgrep -f 以命令行匹配,ps -p 校验 PID 存活。

2) 端口检测

# ss检测
ss -lntp | grep ':80 '
# netstat检测
netstat -lntp | grep ':3306 '

解释:检测是否监听指定端口,结合服务名判断。

3) HTTP健康检查

curl -s -o /dev/null -w "%{http_code} %{time_total}\n" http://127.0.0.1/health

解释:输出状态码与响应时间,便于阈值判断。

4) 资源阈值检测

# 内存使用率(%)
free -m | awk 'NR==2{printf "%.2f\n", $3*100/$2}'
# 磁盘使用率(%)
df -h / | awk 'NR==2{gsub("%","",$5); print $5}'

解释:输出数值便于比较阈值。

通用自愈脚本(可执行示例)#

脚本路径:/usr/local/bin/autoheal.sh

#!/usr/bin/env bash
set -euo pipefail

# 可配置项
SERVICE_NAME="nginx"
HEALTH_URL="http://127.0.0.1/health"
PORT="80"
PID_FILE="/run/nginx.pid"
RESTART_CMD="systemctl restart nginx"
RELOAD_CMD="nginx -s reload"
ALERT_CMD="echo '[ALERT] nginx autoheal failed'"

# 频率限制(秒)
MIN_INTERVAL=300
STATE_FILE="/var/run/autoheal_${SERVICE_NAME}.state"

log(){ echo "$(date '+%F %T') $*"; }

rate_limit(){
  if [[ -f "$STATE_FILE" ]]; then
    last=$(cat "$STATE_FILE")
    now=$(date +%s)
    if (( now - last < MIN_INTERVAL )); then
      log "skip: restart too frequent"
      exit 0
    fi
  fi
  date +%s > "$STATE_FILE"
}

check_port(){
  ss -lntp | grep -q ":${PORT} "
}

check_http(){
  code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" || true)
  [[ "$code" == "200" ]]
}

check_process(){
  if [[ -f "$PID_FILE" ]]; then
    ps -p "$(cat "$PID_FILE")" >/dev/null 2>&1
  else
    pgrep -f "${SERVICE_NAME}" >/dev/null 2>&1
  fi
}

recover(){
  log "try reload"
  $RELOAD_CMD || true
  sleep 2
  if check_http; then return 0; fi

  log "try restart"
  rate_limit
  $RESTART_CMD
  sleep 3
  check_http
}

main(){
  if check_http && check_port && check_process; then
    log "health ok"
    exit 0
  fi

  log "health failed, start recovery"
  if recover; then
    log "recovered"
    exit 0
  else
    log "recovery failed"
    $ALERT_CMD
    exit 2
  fi
}

main "$@"

运行与预期效果:

chmod +x /usr/local/bin/autoheal.sh
/usr/local/bin/autoheal.sh
# 预期输出:health ok 或 recovered

典型服务示例(含命令解释)#

1) MySQL

# 检测
mysqladmin -uroot -p'pwd' ping
# 简单查询
mysql -uroot -p'pwd' -e "select 1;"
# 自愈
systemctl restart mysqld

解释:mysqladmin ping 可快速判断服务可用性;自愈前建议先检查复制状态或只读模式。

2) Redis

# 检测
redis-cli -h 127.0.0.1 -p 6379 ping
redis-cli info replication | grep role
# 自愈
systemctl restart redis

解释:避免在 AOF/RDB 重写期间强制重启。

3) Kafka

# 端口检测
ss -lntp | grep ':9092 '
# Broker状态(示例)
kafka-broker-api-versions.sh --bootstrap-server 127.0.0.1:9092
# 自愈
systemctl restart kafka

典型排错指南#

  • 端口不监听:ss -lntp | grep :80,确认服务是否启动或端口被占用。
  • HTTP 返回非 200:curl -v http://127.0.0.1/health 查看响应与超时。
  • 重启后仍失败:检查日志
# systemd服务日志
journalctl -u nginx -n 100 --no-pager
# 应用日志
tail -n 200 /var/log/nginx/error.log
  • 重启风暴:增加 MIN_INTERVAL 或写入锁文件控制频率。

练习#

1) 将脚本中的 HEALTH_URL 改为你的业务健康接口,设置 MIN_INTERVAL=600,模拟停止服务并观察恢复过程。
2) 增加“资源阈值检测”:当磁盘使用率 > 85% 时拒绝重启并告警。
3) 为 Redis 增加“角色保护”:若节点为 slave,禁止自动重启并提示人工处理。
4) 将 ALERT_CMD 替换为企业微信/邮件脚本,并验证告警是否发送成功。