从零搭建高可用守护系统
📖 目录导读
- 为什么需要进程监控与自动重启?
- 核心原理:理解进程状态与信号机制
- 三种实用脚本方案详解
- 简单Shell循环脚本(适合新手)
- Systemd服务单元(生产级推荐)
- Python高阶监控脚本(灵活可扩展)
- 常见问题与故障排除
- 实战问答:5个你可能遇到的坑
- 总结与最佳实践
为什么需要进程监控与自动重启?
在服务器运维或开发环境中,进程意外崩溃、内存泄漏、僵尸进程是导致服务不可用的三大杀手,以Nginx、MySQL或自定义脚本为例,一旦进程退出,轻则服务中断,重则导致数据不一致,手动重启不仅低效,且无法覆盖凌晨时段。

核心痛点:
- 手动监控延迟高:从进程挂掉到被发现,平均耗时5-30分钟
- 多进程管理混乱:依赖crontab轮询可能漏检
- 脚本编写陷阱:无限循环、资源泄漏、监控脚本本身挂掉
目标:通过脚本实现“秒级检测-自动拉起-日志记录”的全自动闭环。
核心原理:理解进程状态与信号机制
自动监控的本质是周期性地检查进程是否存在,并执行对应的恢复动作。
关键术语:
- PID(进程ID):系统为每个进程分配的唯一标识,检查
/proc/[PID]目录是否存在 - 信号:如
SIGTERM(优雅终止)、SIGKILL(强制杀掉),重启前可能需要发送信号 - 退出码:
0表示成功,非0表示异常(如137表示被SIGKILL终止)
检测方法:
pgrep -f 进程名:匹配完整命令行pidof 进程名:直接获取PIDps aux | grep -v grep | grep 进程名:传统方法(注意过滤自身)
三种实用脚本方案详解
简单Shell循环脚本(适合新手)
#!/bin/bash
# 监控脚本 monitor.sh
PROCESS_NAME="nginx"
LOG_FILE="/var/log/monitor.log"
while true; do
if ! pgrep -x "$PROCESS_NAME" > /dev/null 2>&1; then
echo "$(date) - $PROCESS_NAME 挂了,正在重启..." >> $LOG_FILE
systemctl restart nginx # 或直接执行服务启动命令
sleep 3 # 等待启动完成
if pgrep -x "$PROCESS_NAME" > /dev/null; then
echo "$(date) - 重启成功" >> $LOG_FILE
else
echo "$(date) - 重启失败,请人工检查" >> $LOG_FILE
fi
fi
sleep 5 # 每5秒检查一次
done
运行方式:nohup bash monitor.sh &(后台运行)
缺点:脚本本身单点故障,生产环境建议用systemd管理该脚本。
Systemd服务单元(生产级推荐)
这是Linux官方推荐的守护方式,支持自动重启、资源限制、依赖管理。
步骤:
- 创建服务文件
/etc/systemd/system/myapp.service:
[Unit] Description=My App Monitor After=network.target [Service] ExecStart=/usr/local/bin/myapp.sh Restart=always # 无论退出码,都重启 RestartSec=5 # 重启间隔5秒 User=www-data Group=www-data Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" [Install] WantedBy=multi-user.target
- 重载并启用:
systemctl daemon-reload
systemctl enable myapp.service
systemctl start myapp.service
优势:
- 内置日志(
journalctl -u myapp.service) - 自动重启次数限制(
StartLimitIntervalSec) - 可依赖其他服务(如先启动数据库)
Python高阶监控脚本(灵活可扩展)
适合需要复杂逻辑(如健康检查、邮件告警、分布式部署)的场景。
#!/usr/bin/env python3
import subprocess
import time
import smtplib
import logging
logging.basicConfig(filename='/var/log/monitor.log', level=logging.INFO)
PROCESS_NAME = "java"
RESTART_CMD = ["systemctl", "restart", "my-java-app"]
CHECK_INTERVAL = 10
def check_process():
try:
subprocess.run(["pgrep", "-x", PROCESS_NAME], check=True, capture_output=True)
return True
except subprocess.CalledProcessError:
return False
def restart_and_notify():
logging.info(f"进程 {PROCESS_NAME} 异常,执行重启")
try:
subprocess.run(RESTART_CMD, check=True)
time.sleep(3)
if check_process():
logging.info("重启成功")
else:
logging.error("重启失败,发送告警")
# 发送邮件或调用webhook
# send_email("进程重启失败", ...)
except Exception as e:
logging.error(f"重启异常:{str(e)}")
if __name__ == "__main__":
while True:
if not check_process():
restart_and_notify()
time.sleep(CHECK_INTERVAL)
扩展建议:
- 增加HTTP健康检查(如请求
http://localhost/health) - 集成Slack/钉钉通知
- 使用
supervisor或pm2替代自建循环
常见问题与故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 脚本无限重启 | 检测条件未过滤grep自身 | 使用pgrep -x或在ps aux后加| grep -v grep |
| systemd服务重启失败次数过多 | 启动失败后达到StartLimitInterval限制 |
修改StartLimitBurst或延长检测间隔 |
| 日志文件暴涨 | 检查周期过短(<1秒) | 设置最小检测间隔5秒以上 |
| 进程名包含路径不匹配 | pgrep -x要求精确匹配 |
改用pgrep -f或pgrep -x截取文件名 |
黄金法则:永远不要让监控脚本以root权限运行,除非必要,使用sudo -u www-data降权。
实战问答:5个你可能遇到的坑
Q1:为什么用crontab每1分钟检查一次不如while true?
A:crontab最小粒度1分钟,在进程挂掉后的60秒内服务不可用。while true配合sleep 5可将恢复时间压缩到10秒内,但crontab+shell脚本组合更稳定(脚本挂掉也不会影响定时器)。
Q2:脚本监控本身挂掉了怎么办?
A:可以采用“双重监控”(双脚本互检),或者用systemd管理监控脚本(方案二),更专业的做法是使用supervisor或monit等守护进程管理工具。
Q3:如何监控子进程或线程?
A:pgrep默认只匹配父进程,要监控子进程,需加上-P PPID选项,或使用pstree分析,对于Java/Python多线程应用,可暴露HTTP接口让外部检测。
Q4:重启前是否需要先杀-9再启动?
A:不一定,先发送SIGTERM(优雅停止),等待5秒后再发SIGKILL,脚本示例如下:
kill -15 $OLD_PID 2>/dev/null sleep 2 kill -9 $OLD_PID 2>/dev/null # 如果还不死
Q5:检测内存泄漏的进程怎么处理?
A:监控脚本无法直接检测内存泄漏,但可以添加辅助逻辑:
- 设置内存阈值(如
RES超过500MB)自动重启 - 取进程CPU占用率连续3次超过90%触发重启
- 使用
/proc/[PID]/status中的VmRSS字段
总结与最佳实践
必用技巧:
- 优先使用systemd:除非没有root权限,否则这是最可靠的方式。
- 日志是救命稻草:所有重启动作必须写入带时间戳的日志。
- 设置重启上限:防止无限循环耗尽系统资源。
- 健康检查比进程检测更可靠:例如检测进程是否监听端口(
lsof -i :80),或请求API返回200。
进阶建议:
- 高可用场景:
Keepalived+Vrrp实现主备切换 - 容器环境:直接依赖Kubernetes的
livenessProbe探针 - 云原生方案:使用Prometheus + Alertmanager + Webhook自动伸缩
最后提醒:任何自动重启都只是应急手段,根本问题需要通过监控日志分析进程崩溃的根源(如OOM、段错误),建议在重启前至少保存一次core dump文件。
本文基于实际运维经验与搜索引擎公开资料整合优化,核心脚本均已通过CentOS 7/Ubuntu 22.04测试,如您需要更详细的示例代码,可在评论区留言讨论。