当前位置:首页>Linux>再也不怕半夜被叫醒!Linux服务宕机自动恢复脚本(nginx/mysql通用)

再也不怕半夜被叫醒!Linux服务宕机自动恢复脚本(nginx/mysql通用)

  • 2026-10-11 08:27:49
再也不怕半夜被叫醒!Linux服务宕机自动恢复脚本(nginx/mysql通用)

凌晨1点,公司老板突然打来电话——网站打不开了,你睡眼朦胧登录服务器,发现nginx进程莫名其妙挂了;mysql服务也崩了,业务数据库无法连接,损失惨重……作为运维,最头疼的就是服务突发宕机,尤其是非工作时间,既影响业务,又熬人。

其实,我们不用实时盯守服务器——一个简单的Shell脚本,就能实现nginx、mysql、redis等核心服务的存活监控,服务挂了自动重启,重启失败还能及时发送告警,更有防无限重启的保护机制,减少“半夜起来排错”的烦恼。

本文全程以新手视角,从“服务存活判断方法”到“完整脚本编写”,再到“实战落地”,一步步教你实现服务自动恢复,所有代码可直接复制使用,看完就能上手,完美解决服务宕机无人值守的痛点。

一、核心前提:如何判断Linux服务是否正常运行?

监控脚本的核心,是“精准判断服务是否存活”——只靠“进程是否存在”不够严谨(比如进程在但端口未监听,服务仍无法使用),推荐3种判断方式,结合使用更可靠,新手可根据自身场景选择。

1.1 方式1:进程判断(最基础,快速排查)

通过pgrep或ps命令,判断服务进程是否存在,适合简单场景(如无端口的后台服务)。

# 示例1:判断nginx进程是否存在(返回进程ID则存活,为空则宕机)
pgrep nginx

# 示例2:判断mysql进程(mysql进程名通常为mysqld)
pgrep mysqld

# 示例3:判断redis进程(redis进程名通常为redis-server)
pgrep redis-server

解读:执行命令后,若输出一串数字(进程ID),说明服务正常;若无任何输出,说明服务已宕机。

缺点:无法判断服务是否能正常提供服务(比如进程在,但端口被占用,服务无法响应)。

1.2 方式2:端口判断(更严谨,验证服务可访问)

服务正常运行时,会监听指定端口(如nginx监听80/443,mysql监听3306,redis监听6379),通过ss或netstat命令判断端口是否处于监听状态。

# 示例1:判断nginx的80端口是否监听(推荐用ss,比netstat更高效)
ss -tln | grep 80

# 示例2:判断mysql的3306端口是否监听
ss -tln | grep 3306

# 示例3:判断redis的6379端口是否监听
ss -tln | grep 6379

解读:若输出包含对应端口的监听信息(如LISTEN 0      128          *:80),说明服务正常;若无输出,说明服务宕机或端口未监听。

优点:比进程判断更严谨,能初步验证服务可访问。

1.3 方式3:响应判断(最可靠,模拟用户访问)

通过模拟用户请求,判断服务是否能正常响应(比如访问nginx首页、连接mysql数据库),适合核心业务服务,确保服务真正可用。

# 示例1:判断nginx是否正常响应(访问本地首页,返回200状态码则正常)
curl -s -o /dev/null -w "%{http_code}" http://localhost | grep 200

# 示例2:判断mysql是否正常响应(本地连接数据库,无报错则正常,需提前配置免密)
mysql -u root -e "select 1" 2>/dev/null

# 示例3:判断redis是否正常响应(发送ping命令,返回PONG则正常)
redis-cli ping | grep PONG

解读:

  • nginx:执行后输出200,说明服务正常响应;
  • mysql:执行后无报错(仅输出1),说明数据库可正常连接;
  • redis:执行后输出PONG,说明服务正常。

优点:最贴近实际使用场景,能精准判断服务是否可用;

注意:mysql需配置免密登录(下文会讲),避免脚本中明文写密码。

1.4 三种判断方式对比

判断方式
命令示例
优点
缺点
适用场景
进程判断
pgrep nginx
简单、快速,资源消耗低
无法验证服务可访问
临时排查、非核心服务
端口判断
ss -tln
grep 80
严谨,能验证端口监听
无法判断服务内部异常(如端口监听但服务卡死)
响应判断
curl http://localhost
最可靠,模拟用户访问
资源消耗稍高,需额外配置(如免密)
核心业务服务(nginx、mysql)

实战建议:脚本中结合“端口判断+响应判断”,既保证效率,又确保服务真正可用。

二、核心实战:服务自动重启脚本编写

先编写一个通用脚本框架,包含“服务判断→自动重启→告警通知→防无限重启”四大核心功能,后续可直接适配nginx、mysql、redis等不同服务,新手可直接复制修改。

2.1 脚本核心逻辑

  1. 定义服务相关参数(服务名、端口、重启命令、响应判断命令);
  2. 结合“端口+响应”双重判断,检测服务是否存活;
  3. 若服务宕机,自动执行重启命令;
  4. 重启后再次检测,若重启失败,发送告警通知(企业微信/邮件);
  5. 加入防无限重启机制(如5分钟内重启3次则停止,避免脚本bug导致服务反复重启);
  6. 记录脚本运行日志,便于后续排查问题。

2.2 通用版完整脚本(命名为service_monitor.sh)

#!/bin/bash
# Linux服务存活监控与自动重启脚本
# 作者:运维新手教程
# 核心功能:监控服务存活,宕机自动重启,重启失败告警,防无限重启
# 适配服务:nginx、mysql、redis(可修改参数适配其他服务)

##############################################################################
# 第一步:修改以下参数(根据监控的服务调整,必改!)
##############################################################################
SERVICE_NAME="nginx"# 服务名(如nginx、mysqld、redis-server)
SERVICE_PORT="80"# 服务监听端口(如nginx=80,mysql=3306,redis=6379)
RESTART_CMD="systemctl restart $SERVICE_NAME"# 服务重启命令
# 响应判断命令(根据服务调整,执行成功则返回0,失败则返回非0)
CHECK_RESPONSE_CMD="curl -s -o /dev/null -w "%{http_code}" http://localhost | grep 200"

# 告警配置(二选一或同时启用,按需修改)
# 企业微信机器人webhook(替换为自己的,参考上一篇教程获取)
WECHAT_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your_webhook_key"
# 邮件收件人(替换为自己的邮箱)
EMAIL_RECIPIENT="your_email@163.com"

# 防无限重启配置(核心保护机制)
MAX_RESTART_COUNT=3           # 最大重启次数(如5分钟内超过此次数,停止重启)
RESTART_INTERVAL=300          # 重启间隔时间(单位:秒,5分钟=300秒)
RESTART_LOG="/var/log/service_restart.log"# 重启日志路径(记录重启时间和次数)

##############################################################################
# 第二步:初始化日志文件(若不存在则创建)
##############################################################################
if [ ! -f "$RESTART_LOG" ]; then
    touch "$RESTART_LOG"
    chmod 644 "$RESTART_LOG"
fi

##############################################################################
# 第三步:定义核心函数(无需修改)
##############################################################################
# 1. 日志记录函数(格式:时间 服务名 日志内容)
log() {
echo"[$(date "+%Y-%m-%d %H:%M:%S")] [$SERVICE_NAME] $1" >> "$RESTART_LOG"
}

# 2. 服务存活判断函数(返回0=存活,1=宕机)
check_service() {
# 方式1:端口判断(检测端口是否监听)
    port_check=$(ss -tln | grep "$SERVICE_PORT")
# 方式2:响应判断(检测服务是否能正常响应)
    response_check=$(eval"$CHECK_RESPONSE_CMD")

# 双重判断:端口监听且响应正常,才算服务存活
if [ -n "$port_check" ] && [ -n "$response_check" ]; then
return 0  # 服务存活
else
return 1  # 服务宕机
fi
}

# 3. 告警发送函数(支持企业微信、邮件)
send_alarm() {
local alarm_msg=$1
local current_time=$(date "+%Y-%m-%d %H:%M:%S")
local server_ip=$(hostname -I | awk '{print $1}')

# 企业微信告警(推荐)
if [ -n "$WECHAT_WEBHOOK" ]; then
        wechat_msg='{
            "msgtype": "text",
            "text": {
                "content": "【Linux服务告警】\n告警时间:'
$current_time'\n服务器IP:'$server_ip'\n告警服务:'$SERVICE_NAME'\n告警内容:'$alarm_msg'"
            }
        }'

        curl -H "Content-Type: application/json" -X POST -d "$wechat_msg""$WECHAT_WEBHOOK" 2>/dev/null
fi

# 邮件告警
if [ -n "$EMAIL_RECIPIENT" ]; then
        email_subject="【Linux服务告警】$SERVICE_NAME 服务异常(服务器:$server_ip)"
echo"$alarm_msg" | mail -s "$email_subject""$EMAIL_RECIPIENT" 2>/dev/null
fi

# 记录告警日志
log"告警发送成功:$alarm_msg"
}

# 4. 防无限重启判断函数(返回0=可重启,1=禁止重启)
check_restart_limit() {
# 统计最近RESTART_INTERVAL秒内的重启次数
    recent_restart_count=$(grep "$(date -d "$RESTART_INTERVAL seconds ago""+%Y-%m-%d %H:%M:%S")""$RESTART_LOG" | grep "重启服务" | wc -l)

if [ "$recent_restart_count" -ge "$MAX_RESTART_COUNT" ]; then
return 1  # 超过最大重启次数,禁止重启
else
return 0  # 可重启
fi
}

##############################################################################
# 第四步:核心逻辑执行(无需修改)
##############################################################################
log"开始执行服务监控..."

# 1. 检查服务是否存活
if check_service; then
log"服务运行正常(端口:$SERVICE_PORT,响应正常)"
else
log"服务宕机,准备执行重启..."

# 2. 检查是否超过最大重启次数
if check_restart_limit; then
# 3. 执行重启命令
eval"$RESTART_CMD"
        sleep 5  # 重启后等待5秒,再检测服务是否恢复

# 4. 重启后再次检查服务状态
if check_service; then
            restart_msg="服务重启成功(端口:$SERVICE_PORT,响应正常)"
log"$restart_msg"
            send_alarm "$restart_msg"# 可选:重启成功也发送通知
else
            alarm_msg="服务重启失败!请手动排查(服务名:$SERVICE_NAME,端口:$SERVICE_PORT)"
log"$alarm_msg"
            send_alarm "$alarm_msg"# 重启失败,必须发送告警
fi
else
# 超过最大重启次数,停止重启,发送告警
        alarm_msg="服务宕机,且5分钟内重启次数超过$MAX_RESTART_COUNT次,已停止自动重启,请手动排查!"
log"$alarm_msg"
        send_alarm "$alarm_msg"
fi
fi

log"监控执行完毕"
echo"监控执行完毕,日志路径:$RESTART_LOG"

2.3 脚本使用注意事项

  1. 给脚本添加执行权限(否则无法执行):
    chmod +x /root/service_monitor.sh
  2. 脚本中第一步的参数必须修改,根据监控的服务(nginx/mysql/redis)调整SERVICE_NAME、SERVICE_PORT、CHECK_RESPONSE_CMD;
  3. 告警配置(企业微信/邮件)需替换为自己的信息,否则告警无法发送;
  4. 防无限重启机制默认“5分钟内最多重启3次”,可根据需求调整MAX_RESTART_COUNT和RESTART_INTERVAL;
  5. 测试脚本:可手动停止服务(如systemctl stop nginx),执行脚本,验证是否能自动重启。

三、实战适配:分别监控nginx、mysql、redis服务

通用脚本修改参数后,即可适配不同服务,下面给出3个实战案例,直接复制修改参数即可使用,无需修改脚本核心逻辑。

3.1 案例1:监控nginx服务

核心参数修改(替换通用脚本第一步的参数)

SERVICE_NAME="nginx"# 服务名(nginx默认进程名就是nginx)
SERVICE_PORT="80"# nginx默认监听80端口(https用443)
RESTART_CMD="systemctl restart $SERVICE_NAME"# nginx重启命令
# 响应判断:访问本地首页,返回200状态码则正常(若有自定义首页,修改url即可)
CHECK_RESPONSE_CMD="curl -s -o /dev/null -w "%{http_code}" http://localhost | grep 200"

额外配置(可选,提升可靠性)

若nginx配置了https(443端口),修改SERVICE_PORT="443",响应判断命令改为:

# https响应判断(忽略证书验证,避免curl报错)
CHECK_RESPONSE_CMD="curl -s -o /dev/null -w "%{http_code}" https://localhost --insecure | grep 200"

3.2 案例2:监控mysql服务(重点,需配置免密)

mysql的响应判断需要连接数据库,为了避免脚本中明文写密码,需先配置mysql免密登录(生产环境推荐)。

第一步:配置mysql免密登录(必做)

# 1. 创建mysql配置文件(仅当前用户可访问,避免密码泄露)
vi ~/.my.cnf

# 2. 写入以下内容(替换root密码为自己的mysql密码)
[client]
user=root
password="your_mysql_password"

# 3. 修改配置文件权限(关键,避免其他用户读取)
chmod 600 ~/.my.cnf

第二步:修改脚本核心参数

SERVICE_NAME="mysqld"# mysql进程名(CentOS),Ubuntu为"mysql"
SERVICE_PORT="3306"# mysql默认监听3306端口
RESTART_CMD="systemctl restart $SERVICE_NAME"# mysql重启命令
# 响应判断:连接mysql,执行select 1,无报错则正常(依赖免密配置)
CHECK_RESPONSE_CMD="mysql -u root -e 'select 1' 2>/dev/null"

注意事项

  • Ubuntu系统:将SERVICE_NAME改为mysql,重启命令不变;
  • 若mysql端口修改过(非3306),需同步修改SERVICE_PORT和响应判断命令(添加-P 新端口):
    CHECK_RESPONSE_CMD="mysql -u root -P 3307 -e 'select 1' 2>/dev/null"

3.3 案例3:监控redis服务

核心参数修改

SERVICE_NAME="redis-server"# redis进程名
SERVICE_PORT="6379"# redis默认监听6379端口
RESTART_CMD="systemctl restart $SERVICE_NAME"# redis重启命令
# 响应判断:发送ping命令,返回PONG则正常(若redis设了密码,需添加-a 密码)
CHECK_RESPONSE_CMD="redis-cli ping | grep PONG"

额外配置(若redis设了密码)

若redis配置了密码,修改响应判断命令,添加密码参数:

# 替换your_redis_password为自己的redis密码
CHECK_RESPONSE_CMD="redis-cli -a 'your_redis_password' ping | grep PONG"

3.4 多服务批量监控(进阶技巧)

若需要同时监控nginx、mysql、redis三个服务,无需编写三个脚本,可将三个服务的参数整合,编写一个批量监控脚本(简化版):

#!/bin/bash
# 多服务批量监控脚本(nginx+mysql+redis)
# 分别执行三个服务的监控脚本(需先编写好每个服务的单独脚本)

# 定义服务列表(脚本路径替换为自己的)
SERVICE_SCRIPTS=(
"/root/nginx_monitor.sh"
"/root/mysql_monitor.sh"
"/root/redis_monitor.sh"
)

# 循环执行每个服务的监控脚本
for script in"${SERVICE_SCRIPTS[@]}"; do
if [ -f "$script" ] && [ -x "$script" ]; then
echo"开始监控:$(basename $script)"
$script
echo"------------------------"
else
echo"脚本不存在或无执行权限:$script"
fi
done

四、配置定时任务:让脚本自动执行(核心步骤)

脚本编写完成后,需要配置Linux定时任务(crontab),让脚本每隔1分钟执行一次,实现“实时监控、自动重启”,无需手动执行。

4.1 配置定时任务步骤

# 1. 编辑crontab配置文件
crontab -e

# 2. 在文件末尾添加定时任务(每1分钟执行一次,推荐)
* * * * * /root/service_monitor.sh  # 替换为自己的脚本绝对路径

# 3. 保存退出(按Esc,输入:wq)

# 4. 重启crontab服务,确保定时任务生效
# CentOS系统
systemctl restart crond
systemctl enable crond

# Ubuntu系统
systemctl restart cron
systemctl enable cron

4.2 定时任务解读(新手必懂)

  • * * * * *
    :代表每分钟执行一次(crontab格式:分 时 日 月 周);
  • 若觉得每分钟执行太频繁,可改为每5分钟执行一次:
    */5 * * * * /root/service_monitor.sh
  • 必须写脚本的绝对路径(如/root/service_monitor.sh),否则定时任务会执行失败。

4.3 验证定时任务是否生效

# 查看定时任务日志(CentOS)
grep service_monitor.sh /var/log/cron

# 查看脚本监控日志(确认是否正常执行)
cat /var/log/service_restart.log

若日志中有“开始执行服务监控”“服务运行正常”等记录,说明定时任务配置成功。

五、常见问题与避坑指南

很多新手配置完脚本后,会遇到“脚本不执行”“重启失败”“告警收不到”等问题,这里总结6个最常见的坑,以及对应的解决方法,帮你少走弯路。

问题1:脚本执行报错“ss: command not found”或“curl: command not found”

  • 原因:系统缺少ss(端口查看工具)或curl(响应测试工具);
  • 解决:安装对应工具:
    # CentOS系统
    yum install -y iproute curl

    # Ubuntu系统
    apt install -y iproute2 curl

问题2:mysql响应判断报错“Access denied for user 'root'@'localhost'”

  • 原因:未配置mysql免密登录,或免密配置文件权限错误;
  • 解决:
    • 重新配置免密登录(参考3.2节);
    • 确保免密配置文件权限为600(chmod 600 ~/.my.cnf)。

问题3:服务能重启,但告警收不到

  • 原因1:企业微信webhook地址错误(如之前遇到的errcode:93000,就是webhook无效);

解决:重新获取企业微信机器人webhook,确保地址完整、无拼写错误,不要泄露webhook密钥。

  • 原因2:邮件发送功能未配置(如未安装sendmail);

解决:安装sendmail并启动(参考上一篇教程4.2节)。

问题4:脚本执行正常,但定时任务不执行

  • 原因1:脚本无执行权限(忘记chmod +x);

解决:chmod +x /root/service_monitor.sh。

  • 原因2:定时任务中写了相对路径(如service_monitor.sh),而非绝对路径;

解决:将crontab中的脚本路径改为绝对路径(如/root/service_monitor.sh)。

  • 原因3:crontab服务未启动;

解决:重启crontab服务(参考4.1节)。

问题5:服务无限重启,无法停止

  • 原因:防无限重启机制未生效,或RESTART_INTERVAL、MAX_RESTART_COUNT配置不合理;
  • 解决:
    • 手动停止脚本的定时任务(crontab -e删除对应行);
    • 检查RESTART_LOG路径是否正确,确保日志能正常写入;
    • 调整MAX_RESTART_COUNT(如改为2)和RESTART_INTERVAL(如改为600秒)。

问题6:重启命令执行失败(如“Failed to restart nginx.service”)

  • 原因:重启命令错误,或服务未安装为systemd服务;
  • 解决:
    • 手动执行重启命令(如systemctl restart nginx),排查命令是否正确;
    • 若服务未用systemd管理(如源码安装的nginx),修改RESTART_CMD为对应重启命令(如/usr/local/nginx/sbin/nginx -s reload)。

六、实用技巧与进阶优化

结合生产环境实战经验,补充5个实用技巧,让监控脚本更完善、更省心,适配更多场景。

1. 脚本日志优化(便于排查)

在脚本中添加“服务状态详情”,比如记录服务进程ID、端口监听情况,后续排查问题更高效:

# 在check_service函数中添加日志(示例)
if [ -n "$port_check" ] && [ -n "$response_check" ]; then
log"服务运行正常(进程ID:$(pgrep $SERVICE_NAME),端口:$SERVICE_PORT)"
return 0
else
log"服务宕机(进程ID:$(pgrep $SERVICE_NAME),端口监听:$port_check)"
return 1
fi

2. 重启成功/失败区分告警

默认脚本重启成功会发送告警,可根据需求关闭(注释掉重启成功的send_alarm命令),只保留“重启失败”告警,避免告警骚扰。

3. 多端口监控(适合多端口服务)

若服务监听多个端口(如nginx同时监听80和443),修改端口判断逻辑,同时检测多个端口:

# 多端口判断(示例:同时检测80和443端口)
port_check=$(ss -tln | grep -E "80|443")

4. 脚本自启动(避免服务器重启后脚本失效)

将定时任务配置为开机自启,同时确保脚本日志目录存在,避免服务器重启后监控失效:

# 确保日志目录存在(添加到脚本开头)
if [ ! -d "$(dirname $RESTART_LOG)" ]; then
    mkdir -p "$(dirname $RESTART_LOG)"
fi

5. 结合logrotate管理脚本日志

脚本日志会不断增长,可结合logrotate工具(参考上一篇教程),配置日志轮转,避免日志占满磁盘:

# 创建logrotate配置文件
vi /etc/logrotate.d/service_monitor

# 写入以下内容(日志保留7天,每天轮转,压缩)
/var/log/service_restart.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
}

如果你觉得本文对你有帮助,欢迎点赞、推荐、转发,关注我,后续会分享更多Linux入门干货!

文 / 零距技术仓
记录每一次真实的折腾 (#^.^#)

🚀 想看到更多实用折腾技巧?
👉 先关注
💬 评论区说说你的经历或想看的内容
👍 点赞表示支持
🔁 顺手分享给也在折腾的人,让大家都少踩坑 😎

最新文章

随机文章