1. 为什么你需要一个轻量级Shell监控系统
如果你负责过线上服务的运维,肯定经历过半夜被电话吵醒的噩梦。服务器挂了、应用进程莫名其妙消失、日志里突然出现大量错误,这些突发状况总是让人措手不及。市面上的监控系统,比如Zabbix、Prometheus,功能确实强大,但它们就像瑞士军刀,功能多但部署复杂,学习成本高。对于中小团队或者个人项目来说,有时候我们需要的只是一把简单好用的“水果刀”。
我自己就踩过不少坑。早期项目规模小,觉得手动登录服务器看看就行,结果有一次因为一个核心进程被误杀,服务停了半天才发现,损失不小。后来尝试用脚本写监控,但告警通知又成了问题,邮件经常被忽略,短信成本太高。直到我把Shell脚本的灵活性和钉钉机器人的即时通知结合起来,才真正找到了一个既轻量又高效的解决方案。
这个方案的核心思想很简单:用最熟悉的Shell命令来采集数据,用最常用的办公软件来接收告警。你不需要学习复杂的监控指标配置,也不需要搭建庞大的告警平台。只要你会写基本的Shell命令,知道怎么在钉钉群里添加一个机器人,就能在半小时内搭建起一套属于自己的监控系统。它特别适合监控那些“非标准”的东西,比如一个自定义脚本的输出、一个特定日志关键词的出现频率,或者某个后台进程的存活状态。接下来,我就带你从零开始,一步步把它构建出来。
2. 系统核心架构与设计思路
在动手写代码之前,我们先得想清楚这个系统到底该怎么工作。一个好的设计能让后续的开发事半功倍,也能避免很多潜在的坑。
2.1 轻量级监控的核心流程
整个系统的运行流程可以概括为“配置、调度、执行、告警”四个步骤,形成一个闭环。首先,你通过一个简单的Web界面(或者配置文件)告诉系统:“我想监控服务器A上的Nginx进程,每5分钟检查一次,如果进程不存在,就发消息到钉钉的运维群。” 系统会把这个任务保存下来。
接下来,一个后台的定时任务调度器开始工作。它就像是一个严格的监工,每分钟都会检查一遍数据库,看看有哪些监控任务到了该执行的时间。这里有个关键点,如果系统部署在多台机器上(集群模式),我们还得确保同一个任务不会被重复执行,这就需要用到分布式锁,比如用Redis或者数据库的行锁来实现,防止“监工们”互相抢活干。
任务被触发后,系统会通过SSH连接到目标服务器,执行你预先配置好的Shell命令。比如检查进程的命令就是 ps -ef | grep ‘nginx’ | grep -v grep。命令执行后,会返回结果。系统拿到结果后,会和你设定的阈值进行比较。如果发现进程不存在,或者日志错误关键词超过了5次,就判定为异常。
最后一步就是告警。系统会把异常信息、发生时间、服务器IP等关键数据,组装成一条格式清晰的消息,通过HTTP请求调用钉钉机器人的Webhook接口。钉钉群里的所有人就能立刻收到一条@所有人的告警消息,提醒他们赶紧处理。
2.2 技术选型:为什么是Spring Boot + Shell + 钉钉?
你可能会有疑问,实现监控有很多种技术路线,为什么我选择了这个组合?这完全是从“简单、实用、好集成”的角度出发的。
首先,Spring Boot 是Java领域快速开发的事实标准。它内嵌了Tomcat服务器,打包后就是一个独立的Jar包,真正做到“一键启动”。它强大的生态让我们能轻松集成数据库访问(JPA)、定时任务(Scheduled)和Web管理界面。用Java写核心调度逻辑,稳定性和性能都有保障。
其次,Shell命令 是我们监控的“眼睛”和“耳朵”。几乎所有的服务器状态信息,都能通过Shell命令获取。CPU使用率可以用 top 或 vmstat,内存可以用 free,磁盘空间用 df,日志分析用 grep 和 awk。利用Shell,我们无需在目标服务器上安装任何额外的Agent(代理程序),实现了真正的“无侵入式监控”。这对于那些权限严格或者环境复杂的服务器来说,简直是福音。
最后,钉钉机器人 解决了告警通知“最后一公里”的问题。它提供了非常简单易用的Webhook API,只需要一个安全的访问令牌(Token),就能把消息推送到任何一个钉钉群。消息格式支持Markdown,可以高亮关键信息,还能@特定同事,确保告警能被及时看到。相比邮件和短信,它在移动端的到达率和即时性要高得多。
这个架构的巧妙之处在于,它将复杂的监控逻辑拆解成了几个职责明确的模块,每个模块都用最合适的技术来实现,最终组合成一个灵活、稳固的整体。
3. 从零开始:环境准备与快速启动
理论讲完了,我们开始动手。放心,整个过程就像搭积木一样,我会把每一步都拆解清楚,你跟着做就行。
3.1 基础环境搭建
这套系统运行只需要一个基础条件:一台安装了Java的服务器。这台服务器将作为我们的监控中心。我推荐使用JDK 8或以上版本,稳定性最好。
# 1. 检查Java是否安装
java -version
# 2. 如果未安装,在Ubuntu/CentOS上可以快速安装
# Ubuntu/Debian
sudo apt update
sudo apt install openjdk-11-jdk
# CentOS/RHEL
sudo yum install java-11-openjdk-devel
除了Java,我们还需要Maven来构建项目。如果你打算直接从GitHub拉取我已经打包好的代码,那么连Maven都可以省略,直接用我提供的Jar包。但为了让你更了解全貌,我还是展示一下从源码构建的过程。
3.2 获取与部署监控系统
首先,我们把项目的代码从GitHub上克隆下来。这个项目是开源的,你可以放心使用。
# 1. 克隆代码仓库
git clone https://github.com/chentiefeng/lite-monitor.git
cd lite-monitor
# 2. 使用Maven进行打包(跳过测试以加快速度)
mvn -Dmaven.test.skip=true clean package
执行完上面的命令后,你会在 target 目录下看到一个名为 lite-monitor-0.0.1-SNAPSHOT.jar 的文件。这就是我们监控系统的全部。接下来,我们为它创建一个独立的运行目录,这样便于管理日志和后续升级。
# 3. 创建部署目录并复制Jar包
mkdir -p ~/lite-monitor-server
cp target/lite-monitor-*.jar ~/lite-monitor-server/
# 4. 进入目录并启动应用
cd ~/lite-monitor-server
nohup java -jar lite-monitor-0.0.1-SNAPSHOT.jar 2>&1 &
最后一条命令中的 nohup 和 & 是为了让程序在后台运行,即使你关闭了SSH连接也不会中断。启动成功后,系统的日志会输出到 ~/lite-monitor-server/logs/m.log,你可以用 tail -f 命令查看启动是否正常。
现在,打开你的浏览器,访问 http://你的服务器IP:10003。如果看到监控系统的Web管理界面,恭喜你,监控中心已经成功启动了!默认端口是10003,如果你想修改,可以稍后编辑配置文件。这个界面就是我们后续所有监控任务的配置中心,非常简洁直观。
4. 实战配置:两种最常用的监控场景
系统跑起来了,空荡荡的,我们得给它布置点“工作”。监控任务主要分为两大类:进程存活监控和日志关键字监控。这两种几乎覆盖了80%的日常运维需求,我们一个一个来配置。
4.1 进程存活监控:守护你的核心服务
想象一下,你的网站依赖一个叫 payment-service 的Java支付服务。如果它因为内存溢出(OOM)崩溃了,整个支付流程就会中断。进程监控就是为了防止这种情况发生。
在Web管理界面点击“新增监控”,选择监控类型为“进程”。关键配置项如下:
- IP地址:填写运行
payment-service的那台服务器的IP。 - 关键字:这里要填的是能唯一标识该进程的字符串。不要填
java,因为可能有很多Java进程。最好填包含服务名的唯一字符串,比如payment-service。系统底层执行的命令是ps -ef | grep ‘payment-service’ | grep -v grep,这个grep -v grep是为了过滤掉 grep 命令自身,是个很实用的小技巧。 - 监控频率:设置成
0/5 * * * * ?,这表示每5分钟检查一次。Cron表达式一开始可能有点难,网上有很多可视化生成工具可以帮忙。 - 钉钉机器人Token:这是最重要的部分。你需要先在钉钉群里添加一个自定义机器人,拿到它的Webhook地址中的access_token参数,填在这里。
配置完成后,点击“立即执行”测试一下。如果进程存在,你会看到执行成功的提示;如果不存在(你可以手动杀掉进程测试),钉钉群里就会立刻收到一条告警消息:“【进程监控告警】服务器 192.168.1.10 上的进程 payment-service 不存在!”。效果立竿见影。
4.2 日志关键字监控:提前发现系统异常
很多时候,服务进程还在,但已经在报错了。比如数据库连接失败、第三方接口调用超时。这些错误都会打印在日志文件里。日志监控就是定时去扫描日志,统计特定错误关键词出现的次数,一旦在短时间内频繁出现,就发出告警。
再新增一个监控,这次类型选择“日志”。配置项会比进程监控多一些:
- 日志文件:填写目标服务器上日志文件的绝对路径,例如
/opt/app/payment-service/logs/error.log。 - 统计范围:单位是秒。比如设为300,意味着每次检查时,会分析最近5分钟内的日志。
- 阈值:这是触发告警的临界值。比如设为10,表示如果最近5分钟内,“Connection refused”这个关键词出现了10次以上,就告警。
- 展示条数:告警时,在钉钉消息里附带最近几条具体的错误日志,方便快速定位问题,建议填5-10条。
- 关键字:填写你想监控的错误信息,比如
ERROR、Exception或者更具体的Timeout。
系统会自动生成一个非常精准的 grep 命令,它只会抓取统计时间范围内的日志行,然后过滤出关键词。你甚至可以在“命令”栏后追加管道命令,进行更复杂的分析。例如,监控计算服务的QPS(每秒查询率):
grep ‘计算结束’ | awk ‘{sum += 1} END {if(sum>60){print “近1分钟服务请求数量:” sum; print “近1分钟服务QPS:” sum/60}}’
这样,如果QPS异常升高,你也能第一时间知道。
5. 高级技巧与生产环境优化
基本的监控跑起来后,我们可以让它变得更聪明、更可靠。这部分内容能帮你把系统从“能用”提升到“好用且放心”。
5.1 告警优化:避免“狼来了”效应
最怕的就是告警风暴。比如网络抖动一下,瞬间产生几十条告警,然后又迅速恢复。这种“噪音”会严重消耗团队的注意力,导致真正的告警被忽略。我们需要给告警增加一些“缓冲”和“智能”。
策略一:设置告警延迟与恢复通知。 不要一检测到异常就告警。可以在系统逻辑里加一个简单的判断:连续2次检测都失败,才发送告警。同时,当异常服务恢复时,发送一条“恢复通知”:“【服务恢复】服务器 192.168.1.10 上的 payment-service 进程已恢复正常。” 这能让团队成员清楚地知道问题已经解决,形成闭环。
策略二:分级告警与通知不同的人。 不是所有告警都需要@所有人。你可以利用钉钉机器人支持@特定手机号的功能。比如,磁盘使用率超过80%告警,只@运维负责人;超过95%的严重告警,再@全体成员。在监控配置的“钉钉@人员”栏里,填入对应的手机号即可,用英文逗号分隔。
5.2 集群化部署与高可用保障
单机部署虽然简单,但如果监控中心本身所在的服务器宕机了,那所有监控就都失效了。对于核心业务,我们可以采用集群部署,让多台机器共同承担监控任务,互为备份。
修改项目中的 application.yml 配置文件是关键。你需要将默认的H2文件数据库,切换为MySQL这类共享数据库。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://你的MySQL地址:3306/lite_monitor?useUnicode=true&characterEncoding=utf-8
username: 你的用户名
password: 你的密码
monitor:
cluster: true # 开启集群模式
hostState: online # 本机状态为在线
distributed-lock-type: db # 使用数据库分布式锁,防止任务重复执行
distribute-task-type: http # 集群内通过HTTP通信分发任务
然后,在另一台服务器上,用同样的方式启动另一个监控实例(连接同一个MySQL)。它们会自动在数据库里注册自己,并协同工作。即使一台机器宕机,另一台也会自动接管所有监控任务,实现高可用。
5.3 自定义监控:发挥Shell的无限可能
这才是Shell监控系统的精髓所在——无限扩展。任何你能用命令行获取的信息,都能被监控。
场景一:监控磁盘空间。 你可以写一个Shell脚本 check_disk.sh,内容如下:
#!/bin/bash
usage=$(df / | awk ‘NR==2 {print $5}’ | sed ‘s/%//’)
if [ $usage -gt 90 ]; then
echo “CRITICAL: 根分区使用率 ${usage}%”
else
echo “OK: 根分区使用率 ${usage}%”
fi
然后在日志监控里,配置执行这个脚本的命令,并监控输出中是否包含“CRITICAL”关键词。
场景二:监控API响应时间。 用 curl 命令的 -w 参数可以获取详细的耗时信息。
curl -o /dev/null -s -w ‘时间总计: %{time_total}s\n’ https://api.yourservice.com/health
通过解析这个时间值,你就能监控API的性能是否达标。
把这些自定义命令配置到系统中,你就拥有了一个高度定制化的监控面板。它可能没有商业监控软件漂亮的图表,但绝对是最贴合你业务需求的利器。我在实际项目中,就用它监控过消息队列的堆积情况、证书的到期时间,甚至是一些内部数据报表的生成状态,效果非常好。
152



被折叠的 条评论
为什么被折叠?



