Shell脚本如何自动重启故障服务:从原理到实战的完整指南
目录导读
为什么需要自动重启故障服务?
在生产环境中,服务可能因为内存泄漏、网络波动、并发过高或第三方依赖故障而意外崩溃,手动重启不仅响应慢,还可能因人为疏忽导致长时间宕机,Shell脚本自动重启机制能够:

- 缩短故障恢复时间:从分钟级降至秒级。
- 减少人工干预:尤其适合凌晨或节假日无值守场景。
- 记录故障日志:便于后续排查根本原因。
重要提示:自动重启是应急手段,不能替代代码层面的稳定性优化,建议同时搭配监控告警(如Prometheus + Alertmanager)做二级防护。
Shell脚本自动重启的核心原理
一个标准的自动重启脚本包含三个环节:
1 服务状态检测
常用方法包括:
- 检查进程是否存在:
ps -ef | grep <服务名> | grep -v grep - 尝试端口连通性:
nc -zv <IP> <端口>或curl -I http://localhost:端口 - 检查进程返回值:
systemctl is-active <服务名>
2 故障判定逻辑
- 若检测失败,立即执行重启命令。
- 为防止“启动中误判”,可加入等待确认:重启后sleep 3-5秒,再检测一次。
- 设置重启次数上限(如3次/小时),避免不停重启损坏系统资源。
3 重启与日志记录
- 使用
systemctl restart或service restart。 - 将时间戳、检测结果、重启动作追加到
/var/log/auto-restart.log。
实战:编写一个高效的故障检测与重启脚本
以下脚本综合了进程检测、端口检测、重启计数和日志记录,适用于多数Linux环境。
#!/bin/bash
# 自动检测并重启故障服务 - 版本1.0
# 适用系统:CentOS 7+/Ubuntu 18+/Debian 10+
SERVICE_NAME="your-service-name" # 请替换为实际服务名
SERVICE_PORT=8080 # 请替换为实际端口
LOG_FILE="/var/log/auto-restart-${SERVICE_NAME}.log"
MAX_RESTART_PER_HOUR=3
TEMP_FILE="/tmp/restart_count_${SERVICE_NAME}"
CURRENT_HOUR=$(date +%H)
# 记录日志函数
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}
# 检查服务进程是否存活
check_process() {
if pgrep -x "$SERVICE_NAME" > /dev/null 2>&1; then
return 0
else
return 1
fi
}
# 检查端口是否监听(需安装netcat)
check_port() {
if nc -z -w2 localhost $SERVICE_PORT 2>/dev/null; then
return 0
else
return 1
fi
}
# 获取当前小时内的重启次数
get_restart_count() {
if [ -f "$TEMP_FILE" ]; then
LAST_HOUR=$(cat $TEMP_FILE | cut -d',' -f1)
COUNT=$(cat $TEMP_FILE | cut -d',' -f2)
if [ "$LAST_HOUR" != "$CURRENT_HOUR" ]; then
echo 0
else
echo $COUNT
fi
else
echo 0
fi
}
# 主检测与重启逻辑
restart_service() {
local count=$(get_restart_count)
if [ "$count" -ge "$MAX_RESTART_PER_HOUR" ]; then
log_message "WARNING: 已超过每小时最大重启次数 ($MAX_RESTART_PER_HOUR),跳过本次重启"
exit 1
fi
log_message "检测到服务异常,尝试重启..."
systemctl restart $SERVICE_NAME
if [ $? -eq 0 ]; then
sleep 3
if check_process && check_port; then
log_message "重启成功!服务已恢复"
echo "$CURRENT_HOUR,$((count+1))" > $TEMP_FILE
else
log_message "重启后检测仍然失败,可能需要人工介入"
fi
else
log_message "重启命令执行失败,请检查systemctl状态"
fi
}
# --- 主循环 ---
if ! check_process && ! check_port; then
log_message "服务异常:进程不存在或端口未监听"
restart_service
fi
使用说明:
- 将
SERVICE_NAME和SERVICE_PORT替换为实际值。 - 添加执行权限:
chmod +x /path/to/script.sh - 添加到crontab每分钟执行:
*/1 * * * * /path/to/script.sh
常见问题与解决方案(Q&A)
Q1:如何避免脚本在服务启动过程中误判为“故障”?
A:可在脚本开头检查 /var/lock/ 或创建一个临时标记文件,在系统服务启动完成后写入 /tmp/service_started.ok,脚本检测到该文件存在才执行故障判断,或者使用 systemctl is-active 的 --wait 参数。
Q2:如果服务有多个进程,如何准确检测?
A:建议组合检测:
- 使用
pgrep -f <完整程序名>替代-x(精确匹配)。 - 或者检查最小进程数:
if [ $(ps aux | grep -c <name>) -lt 2 ]。
Q3:重启脚本挂掉了,怎么办?
A:使用 supervisord 或 systemd timer 来守护脚本本身,或者将脚本放入 /etc/cron.minutely,由cron兜底。
Q4:日志文件过大如何处理?
A:添加logrotate配置,或脚本内限制日志行数:
tail -n 1000 $LOG_FILE > $LOG_FILE.tmp && mv $LOG_FILE.tmp $LOG_FILE
Q5:如何发告警通知?
A:在 restart_service 函数内嵌入调用发送命令:
# 使用钉钉机器人
curl -X POST -H 'Content-Type: application/json' \
-d '{"msgtype":"text","text":{"content":"服务重启通知"}}' \
'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN'
(实际使用时请替换为真实域名,示例中为统一修改)
优化与扩展:让脚本更稳定、更智能
1 增加重试间隔
不要立即重启,可增加5秒冷却时间,防止循环重启消耗CPU。
2 区分“真实故障”与“临时抖动”
连续失败3次才认为真故障,可避免网络波动导致的不必要重启:
RETRY=0
while [ $RETRY -lt 3 ]; do
sleep 2
if check_process && check_port; then break; fi
((RETRY++))
done
if [ $RETRY -ge 3 ]; then restart_service; fi
3 支持多种服务管理器
兼容 systemctl、service、docker restart:
restart_cmd() {
if command -v systemctl &>/dev/null; then systemctl restart $1
elif command -v service &>/dev/null; then service $1 restart
elif command -v docker &>/dev/null; then docker restart $1
else log_message "未找到合适的重启命令"; exit 1
fi
}
4 集成到监控系统
将日志中“重启成功/失败”关键字通过 rsyslog 或 fluentd 转发到集中日志平台,方便回溯。
通过Shell脚本自动重启故障服务,能显著提升系统可用性,关键在于:
- 检测要准确(进程+端口双重验证)
- 重启要克制(次数限制+等待确认)
- 日志要完整(便于定位根因)
上述脚本已包含安全防护机制和实际可用的代码,可直接在生产环境验证,自动重启是治标,代码健壮性和资源限流才是治本,建议将本篇与故障根因分析流程结合使用,形成完整的SRE闭环。