凌晨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
解读:
- mysql:执行后无报错(仅输出
1),说明数据库可正常连接;
优点:最贴近实际使用场景,能精准判断服务是否可用;
注意:mysql需配置免密登录(下文会讲),避免脚本中明文写密码。
1.4 三种判断方式对比
实战建议:脚本中结合“端口判断+响应判断”,既保证效率,又确保服务真正可用。
二、核心实战:服务自动重启脚本编写
先编写一个通用脚本框架,包含“服务判断→自动重启→告警通知→防无限重启”四大核心功能,后续可直接适配nginx、mysql、redis等不同服务,新手可直接复制修改。
2.1 脚本核心逻辑
- 定义服务相关参数(服务名、端口、重启命令、响应判断命令);
- 重启后再次检测,若重启失败,发送告警通知(企业微信/邮件);
- 加入防无限重启机制(如5分钟内重启3次则停止,避免脚本bug导致服务反复重启);
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 脚本使用注意事项
- 给脚本添加执行权限(否则无法执行):
chmod +x /root/service_monitor.sh
- 脚本中
第一步的参数必须修改,根据监控的服务(nginx/mysql/redis)调整SERVICE_NAME、SERVICE_PORT、CHECK_RESPONSE_CMD; - 告警配置(企业微信/邮件)需替换为自己的信息,否则告警无法发送;
- 防无限重启机制默认“5分钟内最多重启3次”,可根据需求调整
MAX_RESTART_COUNT和RESTART_INTERVAL; - 测试脚本:可手动停止服务(如
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免密登录,或免密配置文件权限错误;
- 确保免密配置文件权限为600(
chmod 600 ~/.my.cnf)。
问题3:服务能重启,但告警收不到
- 原因1:企业微信webhook地址错误(如之前遇到的
errcode:93000,就是webhook无效);
解决:重新获取企业微信机器人webhook,确保地址完整、无拼写错误,不要泄露webhook密钥。
- 原因2:邮件发送功能未配置(如未安装sendmail);
解决:安装sendmail并启动(参考上一篇教程4.2节)。
问题4:脚本执行正常,但定时任务不执行
解决:chmod +x /root/service_monitor.sh。
- 原因2:定时任务中写了相对路径(如
service_monitor.sh),而非绝对路径;
解决:将crontab中的脚本路径改为绝对路径(如/root/service_monitor.sh)。
解决:重启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入门干货!
文 / 零距技术仓
记录每一次真实的折腾 (#^.^#)
🚀 想看到更多实用折腾技巧?
👉 先关注
💬 评论区说说你的经历或想看的内容
👍 点赞表示支持
🔁 顺手分享给也在折腾的人,让大家都少踩坑 😎