☰
Linux多进程监控实战:从脚本到systemd与Zabbix
2026/10/8 23:56:13 网站建设 项目流程

1. 监控方案设计与需求拆解

1.1 什么场景下需要进程监控

先聊一个我实际遇到的场景。之前负责的一台应用服务器上跑了 Java 服务、Nginx、定时任务脚本、还有两个消息队列的 Worker 进程,加起来七八个常驻进程。一开始大家觉得进程崩了重启就行,结果真有两次半夜服务悄悄挂掉,直到第二天用户反馈才发觉。一次是 Java 进程因为 OOM 被系统杀掉,另一次是 Worker 进程死锁卡住,CPU 占用率直接打到 100%,其他服务跟着遭殃。

这个场景其实非常普遍:一个业务系统由多个进程协作完成,任何一个挂了或者状态异常都会影响整体可用性。所以监控多个进程的存活和 CPU、内存占用,不是"没事找事",而是线上稳定性的基本底线。核心需求有三个:第一,快速发现进程是否存活;第二,感知 CPU 和内存是否存在异常波动;第三,异常发生时能及时告警,甚至自动拉起服务。

1.2 监控方案的三条技术路线

针对这个需求,业界常见的思路有三条:轻量脚本方案、系统级工具方案、重量级监控平台方案。各自的定位不同,选错了要么过度设计给自己造轮子,要么监控力度不够关键时刻掉链子。

方案类型代表工具优点缺点适合场景
轻量脚本Shell + systemd + cron部署简单、零依赖、可控性强功能单一、告警方式简陋小型服务器、进程数量少
系统级工具systemd 服务管理、htop、 glances与系统深度集成、自带守护能力仅限单机、告警能力弱单机多进程、需要自动拉起
监控平台Zabbix、Prometheus + Grafana集中管理、历史数据、丰富告警、可视化部署成本高、维护复杂多台服务器、集群环境

先说结论:如果你的服务器数量不超过三五台,我建议直接用脚本加 systemd,后续有需要再平滑迁移到 Zabbix 或 Prometheus。如果一开始就上重型平台,光安装配置加调试告警规则就得一两天,而脚本方案十分钟就能跑起来。

2. 从 Linux 系统层面理解进程状态

2.1 进程存在状态与"假死"的区分

很多新手做进程监控,第一反应就是检查进程存不存在——用ps -ef | grep xxx或者在脚本里pgrep -f xxx判断有没有输出。但只做到这一步是不够的,因为进程存在不代表进程健康。

Linux 下进程状态分为 R(运行)、S(睡眠)、D(不可中断睡眠)、Z(僵尸)、T(停止)等几种。最坑的是这两种情况:一种是进程还在,但已经变成僵尸进程(Z 状态),它不消耗 CPU 和内存,但说明父进程没有正确回收它,如果长期堆积会耗尽 PID 资源;另一种是进程状态是 S(睡眠),看起来在,实际可能卡在等待某个资源上,比如死锁、等待网络 IO 超时,表现就是接口无响应但进程还在。

所以在设计存活监控时,不能只看"进程在不在",还要结合 CPU 占用率、内存占用率、甚至业务层面的探活信号做综合判断。比如 Java 服务可以暴露 health check 接口,通过 HTTP 请求返回状态来判断进程是否真正健康。脚本方案里,我一般会先检查进程存在,再检查 CPU 占用是否持续异常偏高,两个条件同时判断才触发告警。

2.2 ps 和 top 背后究竟读的什么数据

ps aux看到的 CPU 和内存占用数据,其实来自/proc文件系统。这里简单说明一下原理,对后面写脚本会非常有帮助。Linux 内核会把每个进程的信息以文件形式暴露在/proc/<pid>/目录下。

  • /proc/<pid>/stat:进程状态、CPU 时间、内存等核心字段,其中 utime 和 stime 分别代表用户态和内核态的 CPU 时间(单位是 clock tick,通常 1/100 秒)。
  • /proc/<pid>/status:可读性更好的状态文件,包含 VmRSS(物理内存占用)、State(进程状态)、Name(进程名)等。
  • /proc/meminfo:系统级内存信息,MemTotal、MemFree、MemAvailable 等字段。

理解了数据来源,你在写监控脚本的时候就不会被ps命令的格式差异坑到。比如有的系统ps aux的 %CPU 是进程自启动以来的平均 CPU 使用率,不是实时的瞬间值。如果你拿这个值去判断"当前 CPU 是否飙升",往往不够灵敏。更好的做法是读取/proc/<pid>/stat里的 CPU 时间,间隔 1 秒采样两次,算出差值除以间隔时间,这才是实时的瞬时 CPU 占用率。这个我在第三部分会给出可直接使用的脚本。

2.3 内存占用到底看哪个指标

进程内存的监控也有讲究。ps aux里 RSS(Resident Set Size,常驻物理内存)是进程实际占用的物理内存,这个通常是大家关心的指标。但是要注意共享内存页的问题,多个进程可能共享同一份动态库代码,每个进程的 RSS 里都算了一遍,加起来会超过实际物理内存总量。真正常用的业务判断指标还有两个,一个是 VIRT(虚拟内存),这个值通常很大,不能直接用来判断物理内存压力;另一个是 PSS(Proportional Set Size),它按比例分摊了共享内存,是相对真实的内存占用值,不过需要专门工具如smem才能读取。

我个人的建议是:日常监控看 RSS 就够了,只要别超过物理内存一定比例就行。如果发现进程把你机器内存吃光了,再用 PSS 去细查到底是哪个进程贡献最大。系统层面还要注意MemAvailable这个字段,它比MemFree更真实,因为 Linux 有 page cache 机制,MemFree很低并不代表内存不够用,MemAvailable才会把可回收的 cache 算进去。

3. 进程监控脚本核心实现

3.1 用 pgrep 正确识别目标进程

写进程监控脚本第一个问题就是:怎么准确判断目标进程是否在跑。简单粗暴的做法是pgrep -f "进程关键字",比如pgrep -f "java"。但这里有几个坑:

第一,-f参数是匹配完整命令行,如果 Java 进程命令很长,里面带有业务参数,用-f才能准确识别,但要注意关键字不能太宽泛。比如你搜java,可能把其他 Java 工具进程也搜出来了。我一般用启动命令里的独特参数来匹配,比如pgrep -f "my-service.jar"。

第二,pgrep 默认不匹配自身进程,但你的监控脚本如果用 Python 或者 Shell 循环,脚本名包含关键字,可能会把脚本自己匹配进去。解决方法是给脚本进程名加一个特殊标识,然后在匹配结果里排除。

第三,pidof 命令在某些场景更合适,它精确匹配进程名,但不支持命令行参数匹配。所以我的建议是:进程名固定且有唯一性的用pidof,需要匹配命令行参数的用pgrep -f,两者结合使用最稳妥。

3.2 获取进程瞬时 CPU 使用率的正确计算方法

前面讲过,要拿实时 CPU 使用率不能直接用ps aux的平均值。正确的做法是从/proc里两次采样。这里给出一段 Python 脚本,比 Shell 写起来更顺手,也能处理浮点数计算:

#!/usr/bin/env python3 import os import sys import time import subprocess def get_process_info(pid): """读取 /proc/pid/stat 获取 CPU 时间和状态""" try: with open(f"/proc/{pid}/stat", "r") as f: fields = f.read().split() # 注意:进程名可能带括号和空格,comm 字段在 stat 文件里是第 2 个字段 # 但实际解析时 comm 里的空格会导致字段偏移,稳妥做法是找到最后一个 ')' state = fields[2] # 从文件末尾往前数更准确,简化处理 utime = int(fields[11]) stime = int(fields[12]) cutime = int(fields[13]) cstime = int(fields[14]) rss_pages = int(fields[22]) return state, utime + stime + cutime + cstime, rss_pages except FileNotFoundError: return None, 0, 0 def read_system_cpu_time(): """读取 /proc/stat 第一行,获取系统总 CPU 时间""" with open("/proc/stat", "r") as f: fields = f.readline().split()[1:] # 跳过 "cpu" 前缀 return sum(int(x) for x in fields) def main(): target = sys.argv[1] interval = float(sys.argv[2]) if len(sys.argv) > 2 else 1.0 result = subprocess.run(["pgrep", "-f", target], capture_output=True, text=True) pids = result.stdout.strip().split() if not pids: print(f"PROCESS_NOT_FOUND: {target} 未在运行") sys.exit(2) for pid in pids: pid = int(pid) state1, cpu_time1, rss_pages1 = get_process_info(pid) total_cpu_time1 = read_system_cpu_time() time.sleep(interval) state2, cpu_time2, rss_pages2 = get_process_info(pid) total_cpu_time2 = read_system_cpu_time() if state2 is None: print(f"PID {pid}: 进程已退出") continue cpu_delta = cpu_time2 - cpu_time1 total_delta = total_cpu_time2 - total_cpu_time1 cpu_percent = (cpu_delta / total_delta) * 100 if total_delta > 0 else 0 rss_mb = rss_pages2 * os.sysconf("SC_PAGE_SIZE") / 1024 / 1024 print(f"PID {pid}: CPU {cpu_percent:.1f}%, RSS {rss_mb:.1f} MB, 状态: {state2}") if __name__ == "__main__": main()

这个脚本的核心逻辑就是两次采样。第一次记录 CPU 时间和系统总 CPU 时间,睡 1 秒,第二次再记录,差值的比例就是这 1 秒内该进程对 CPU 的使用率。注意这里有个细节,stat 文件的字段解析有个知名坑点:如果进程名里带空格或括号,直接按空格切片会把字段位置搞乱。我上面的写法是简化版,实际生产环境中建议用rfind(")")来定位 comm 字段的结束位置,后面的字段从那个位置再切。

3.3 存活检测与自动拉起脚本

监控发现问题之后,自动拉起是个很自然的诉求。在 systemd 已经广泛使用的今天,如果目标进程本身就是 systemd 管理的服务,直接把 Restart=always 配上,守护的事交给 systemd 做。但如果你要监控的是手动启动的进程,或者用 nohup 拉起来的一些临时任务,就需要自己写个看护脚本配合 cron 或者循环执行。

#!/bin/bash # watch_process.sh PROCESS_KEYWORD="my-service.jar" RESTART_CMD="/opt/apps/my-service/start.sh" LOG_FILE="/var/log/process_watch.log" check_and_restart() { if pgrep -f "$PROCESS_KEYWORD" > /dev/null 2>&1; then echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 进程 $PROCESS_KEYWORD 存活" >> "$LOG_FILE" else echo "$(date '+%Y-%m-%d %H:%M:%S') [WARN] 进程 $PROCESS_KEYWORD 不存在,正在拉起重启..." >> "$LOG_FILE" $RESTART_CMD >> "$LOG_FILE" 2>&1 sleep 3 if pgrep -f "$PROCESS_KEYWORD" > /dev/null 2>&1; then echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 拉起重启成功" >> "$LOG_FILE" else echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] 拉起重启失败,请人工介入" >> "$LOG_FILE" fi fi } # 主循环,每 30 秒检查一次 while true; do check_and_restart sleep 30 done

这个脚本配合nohup bash watch_process.sh &跑起来,每 30 秒检查一次,进程不在就执行启动命令,然后二次确认。注意启动命令里要处理好日志重定向,防止脚本挂死或者输出刷屏。我见过有人把启动脚本直接放到 while 循环里不带任何保护,结果启动命令本身抛异常把脚本也带崩了。稳妥的做法是先确认重启脚本本身能够独立正常运行,再接入看护逻辑。

3.4 监控文件的数字签名

这个标题当天系统运维了一批监控服务器。运维人员发现,在/etc/nolsp.exe目录下(普通目录,非系统lsp)程序文件大小不对,且启动时占用资源异常。但运维不清楚该程序是否被修改过。

### 3.5 对题目中"监控多个进程"的架构拆解 这里的"多个",在监控脚本的设计上有两种理解:一是对同一类进程(比如多个同名的 Worker 实例)做批量监控,二是对不同业务进程分别监控。我的脚本示例里,`pgrep -f "my-service.jar"` 会返回所有匹配的 PID,然后一个个计算占用率,这就是批量监控的思路。如果你的服务器上进程类型很多,建议用一个配置文件记录每个进程的关键字和告警阈值,脚本读取配置文件循环检查,而不是在每个进程的监控脚本里硬编码。 ```bash # 配置示例 process_list.conf # 格式: 进程名|匹配关键字|CPU告警阈值%|内存告警阈值MB worker|/opt/apps/worker/worker.py|80|2048 nginx|nginx: worker process|50|512 java|app-server.jar|90|4096

脚本主体用一个 for 循环读取这个文件,逐行检查。这样新增一个监控进程只需要改配置文件,不用改代码。这个思路也是后面所有监控平台做"监控项"配置的雏形,先把这个思想建立起来,后面用 Zabbix 时也能平滑衔接。

4. systemd 和守护进程的正确用法

4.1 用 systemd 服务单元管理托管进程

如果你的进程还没有一套成熟的管理方式,强烈建议先迁到 systemd 下面。除了自带的自动重启,systemd 还提供 CPU 和内存的占用限制,以及精细化的启动依赖管理。配置一个服务单元非常简单:

[Unit] Description=My Application Service After=network.target mysql.service Wants=mysql.service [Service] Type=simple User=appuser WorkingDirectory=/opt/apps/myapp ExecStart=/usr/bin/java -Xmx2g -jar myapp.jar Restart=always RestartSec=5 StartLimitIntervalSec=60 StartLimitBurst=3 MemoryMax=3G CPUQuota=200% [Install] WantedBy=multi-user.target

把文件放到/etc/systemd/system/myapp.service后,执行systemctl daemon-reload && systemctl enable --now myapp即可。几个关键参数的作用:

  • Restart=always:只要进程非正常退出就拉起,无论退出码是什么。RestartSec=5表示拉起前等 5 秒,避免疯狂重启打满 CPU。
  • StartLimitIntervalSec=60和StartLimitBurst=3:60 秒内最多重启 3 次,超过则放弃。这个保护很重要,防止代码启动即崩溃导致的死循环,同时避免健康检查一直失败。
  • MemoryMax=3G:限制进程最多使用 3GB 物理内存,超出后 systemd 会杀掉进程并触发重启。这比你在 Java 里配-Xmx更兜底,因为有些 native 内存(比如 JNI 分配的)不受 JVM 堆大小控制。
  • CPUQuota=200%:限制进程最多使用 2 个核心的 CPU 时间,防止某个 Bug 导致 CPU 打满影响同机其他服务。

systemd 本身就是一个进程管理器,systemctl status能看到进程实时的 CPU 和内存占用情况,systemd-cgtop可以按控制组查看每个服务单元的资源使用。如果你的所有进程都迁到了 systemd 管理,那"监控多个进程"这件事,systemd 本身已经承担了接近八成的存活保护和资源观测功能,我们只需要在外面加一层告警脚本就齐活了。

4.2 守护进程与wait命令的关系

进程在 shell 脚本里退出后,有时会出现监控脚本无法感知的情况。这和 shell 的wait命令机制有关。当你在一个 shell 脚本里用&把子进程放到后台,然后执行wait,shell 会阻塞等待该子进程退出。在编写监控看护脚本的时候,理解这个机制能帮你写出更可靠的守护逻辑。

其实很多单机守护脚本都可以用一个简短的 while 循环加 wait 来完成。最经典的模式是:

while true; do my_service & wait $! echo "进程退出了,5 秒后重启..." sleep 5 done

这里细节在于wait $!,$!是上一个后台进程的 PID。wait会等待这个 PID 对应的进程结束,进程一旦退出,wait 立刻返回,然后脚本进入重启逻辑。有个要注意的点是:wait的返回值是子进程的退出码,如果子进程是被信号杀掉的,退出码是 128 加信号编号。可以在 wait 后面根据退出码判断是正常退出还是异常被杀,做不同的处理。

4.3 systemd 和脚本方案的取舍

很多长期跑在服务器上的服务,原本都是nohup方式拉起来的,既没有自动重试,也没有资源限制。我遇到不少维护这类服务的同学,都在"要不要迁到 systemd"上纠结。我的建议很简单:如果是长期稳定的生产服务,迁过去没有坏处,唯一的成本是迁移后的启动路径变了,配置文件路径、环境变量、日志输出位置需要重新梳理一遍。

如果要保留 nohup 方式配合人工排查,那重点要排查下面这些进程的相关状态。比如标题里提到的msedgewebview2.exe这类 Windows 辅助进程,资源占用异常;或者thunderplatform这类程序在后台悄悄驻留。这类问题本质上是进程的"生命周期管理"没做好:进程被拉起来之后没有可观测的状态出口、没有统一的资源配额、没有退出时的通知机制。用 systemd 来管 Linux 端的进程,很多问题都能从根源上消解。

5. 用 Zabbix 搭建更完整的进程监控中心

5.1 Zabbix 监控项怎么设计

前面讲的脚本方案适合小规模,但如果你的机器超过十台,或者需要历史趋势图、多维度告警通知,建议直接上 Zabbix。Zabbix 监控进程存活和资源占用,本质是通过 zabbix-agent 在客户端采集指标,然后上报到 server 端。这里给出核心的配置思路。

在 Zabbix 前端创建主机后,需要添加监控项(items)。进程监控相关的监控项有:

  • proc.num[进程名]:统计匹配进程的数量。值为 0 表示进程不存在,配合触发器last()=0即可实现存活告警。
  • system.cpu.util[,cpu]:CPU 总使用率。
  • proc.mem[进程名]:进程的内存使用量(RSS),单位是字节,需要在监控项设置单位里配成 B 并自定义显示格式。
  • system.uptime:系统运行时间,用来判断服务器是否发生过重启。

触发器是 Zabbix 告警的核心。比如要配置"进程数量小于 1 且持续 2 分钟"才告警,避免短时间抖动误报,可以设置表达式:last(/主机名/proc.num[myapp])=0 and nodata(/主机名/proc.num[myapp],120)=0。这里nodata函数的作用是确认数据流没有中断,防止 agent 本身挂了误判为进程消失。

5.2 agent 端 proc 监控的准确性问题

Zabbix 的proc.num[]在 Linux 上也是通过读取/proc实现的,它有一些细节会影响准确性。首先是进程名匹配的机制,proc.num[java]只精确匹配进程的 comm 字段(通常是可执行文件名),如果你用java -jar app.jar启动,comm 字段是java,没问题;但如果你用自定义脚本启动了一个名字不同的进程,proc.num 就统计不到。

这种情况下可以用proc.num[进程名,,,命令行匹配正则]的三个参数,第一个参数填进程名,第四个参数填正则。比如proc.num[all,,,app-server],意思是不限定进程名,匹配完整命令行里包含 app-server 的进程。实测下来正则参数在某些 Zabbix 版本里对中文支持不好,建议排查时先用命令行手动执行ps -ef确认 agent 能正常识别目标进程。

5.3 从脚本方案平滑迁移到 Zabbix

如果你正在用我前面写的脚本方案,迁移到 Zabbix 的思路是:先部署 zabbix-agent 采集系统基础指标,再把脚本里的检测逻辑逐步替换成 Zabbix 的 proc.num 和 proc.mem 监控项,触发通知走 Zabbix 的媒体类型(邮件、企业微信、钉钉等)。脚本本身可以作为 agent 的 UserParameter 保留一部分个性化逻辑,比如监控某个特定服务的健康检查接口。这种方式兼顾了平台化集中管理和业务的个性化需求。

我这里给出一个 UserParameter 的示例,把自定义监控键添加到/etc/zabbix/zabbix_agentd.d/user_parameter_myservice.conf:

UserParameter=myapp.health,curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/health UserParameter=myapp.cpu,/opt/scripts/cpu_monitor.py myapp.jar

第一个键监控健康检查接口的 HTTP 返回码,第二个键调用自定义脚本获取进程实时 CPU 率。在 Zabbix 前端添加监控项时填入键值myapp.health和myapp.cpu即可。有了这些基础,后续加告警、加图形、加聚合报表都只是前端操作了。

6. 常见问题与排查技巧实录

6.1 进程 CPU 飙升怎么定位

最后的告警只是手段,真正要解决的是问题本身。这里分享一个排查 CPU 飙升的经验流程。当你的监控发出 CPU 高占用告警时,不要着急 kill 进程重启,先按顺序做这几件事:

第一步,用top -Hp <pid>查看进程内部的线程 CPU 占用,能看到是哪个线程在疯狂消耗 CPU。如果 Java 服务飙高,把线程 ID 转成十六进制,再用jstackdump 线程栈,就能定位到具体代码行。这是整条链路里最有含金量的一步,绝大多数 CPU 飙升都是某个死循环或者锁竞争导致的。

第二步,查看飙升的持续时间。如果是瞬间峰值(比如秒杀活动)、周期性任务(比如定时全量同步)、或者正常流量上涨,需要结合业务判断是否需要告警阈值调整。很多误报就是这么来的。建议告警里带上"持续时间超过 5 分钟"等条件,减少无意义打扰。

第三步,检查系统日志和进程日志。journalctl -u myapp或者tail -f /var/log/myapp/error.log,看崩溃或者异常发生前有没有报错。这一步通常能找到蛛丝马迹,比如内存溢出案例里,Java 日志会在进程被杀前输出异常栈。

6.2 内存泄漏的判断方法与处理周期

内存泄漏是进程监控里另一个高频问题。RSS 持续增长但业务量并没有同步增加,基本可以判断为泄漏。脚本方案中可以把ps aux的 RSS 按小时记录到文件,用awk对比最近两次采样值,增长率超过阈值就触发告警。Zabbix 里同样可以对 proc.mem 的监控项设置趋势触发器:last() > avg(1h)*1.2,表示当前内存比一小时均值高 20%,很有参考价值。

处理内存泄漏的周期不固定。轻则几个星期内存缓慢增长但撑得住,重则一两天就能 OOM 崩溃。一旦确认泄漏,临时手段是给 systemd 配MemoryMax强制重启止损,长期手段是通过 dump 分析定位。Java 进程用jmap -dump:format=b,file=heap.bin <pid>抓堆快照,再用 MAT 分析哪些对象占了大头;Python 进程可以用tracemalloc模块或者 objgraph 工具分析对象引用链。

6.3 进程监控误报的典型原因

监控搭建后最让人头大的就是误报。我在这里把踩过的坑整理成一张速查表:

现象可能原因解决办法
进程存在但告警"进程不存在"pgrep 关键字匹配不到,进程名和命令行不匹配用ps -ef确认启动命令,调整关键字或使用 pidof
CPU 告警频繁但实际没问题用了 ps aux 的平均值判断实时占用改为 /proc 两次采样计算的瞬时值
内存监控数值比预期大很多RSS 包含共享内存页,没按 PSS 分摊改用smem或者接受 RSS 在一定范围内的虚高
进程重启后又告警,处于崩溃循环启动脚本本身有问题,或者依赖的服务没起来看日志确认拉起失败原因,检查 systemd 的StartLimitBurst限制
Zabbix 里 proc.num 统计不到进程comm 字段和进程名不一致用第四个参数命令行正则匹配

还有一个容易被忽略的问题:很多监控脚本把告警输出直接写到 stdout,然后通过 cron 执行,cron 默认会把输出发邮件。时间一长邮箱塞满了告警邮件,反而没人看。解决方法是脚本里用日志文件替代 stdout,告警阈值也定义清楚,做到"量少而准"。

6.4 进程监控数字签名与文件完整性检查的补充

这部分补充一个容易被忽略但很重要的小技巧:有些常驻进程会加载配置文件、插件、脚本文件。如果某个监控目标进程的配置文件被意外修改,可能进程照常运行但是行为异常。运维排查时,建议顺手把关键配置文件的 SHA256 值记录在案,每次巡检时比对一下。一条命令即可:

sha256sum /etc/myapp/config.ini > /var/lib/monitor/config.sha256

巡检时执行sha256sum -c即可判断配置文件是否被改动过。这种方式不属于传统进程监控范畴,但结合进程异常使用率排查时非常有效,我自己的经验是很多"莫名其妙 CPU 高"的案例,追根到底都是配置被改动导致的。

7. 关于采集频率和数据存储的经验

监控脚本里我用了 1 秒的采样间隔,这是方便演示计算的。实际生产环境不建议全量高频采集,会带来额外开销。不同的数据有自己的合理周期:

  • 进程存活状态:10-30 秒一次足够,进程崩了重启通常秒级完成,30 秒发现已经算及时。
  • CPU 瞬时占用:1 秒内瞬时波动很大,5 秒采一次并计算平均值,既能反映趋势又不会太敏感。
  • 内存占用:内存变化相对平缓,可以 1-5 分钟采一次,写历史数据时还能减小存储压力。
  • 历史数据保留:Zabbix 默认的趋势数据可以保留 90 天,如果自己写脚本存日志,建议按天滚动,保留 30-60 天,超过就压缩归档。

这里有个核心思想:监控系统不是"采集越多越好",而是"在能发现问题的前提下尽量降低采集开销"。我之前见过有同事写了个脚本每秒采集一次 ps aux 把整台机器的 /proc 都遍历一遍,结果监控脚本自己占了 5% 的 CPU,这不是监控,这是自找负担。合理设置采集频率,才是可持续的方案。

另外,自己写脚本做监控,日志文件一定要做切割,否则一年跑下来一个文件几十 GB 都有可能。最好的做法是把历史数据存到时序数据库里,如果觉得引入库还是太重,至少每天用 logrotate 切割并压缩旧日志。

8. 实操心得与踩坑总结

最后说一些我实际运维监控系统过程中的体会。

第一,监控这个事,投入产出比最高的是"先把进程管起来"。很多服务器的进程是 nohup 裸奔的,一旦没人盯着就处于失控状态。先把 systemd 管到位,把Restart=always都配上,把关键进程的 CPU 和内存告警配置好,业务的稳定性就能上一个台阶,这几步成本很低但回报立竿见影。

第二,监控优先级分清楚。不要一开始就追求完美的可视化大屏,先把"进程挂掉能通知、资源异常能预警"这两件事做好。我在实际工作中见过很多团队,监控图表做得花团锦簇,但真正进程挂了三四个小时没人发现,因为告警配置是空的。工具再漂亮,不如一条及时准确的告警有价值。

第三,每个告警信号背后,都要能对应到一个可执行的动作。收到"CPU 高"的告警,你要知道自己下一步该执行哪几条命令、排查哪几个日志文件;收到"进程不存在"的告警,你要知道它该被谁拉起、拉起失败时该找哪份记录。监控和运维手册要一起建设,不然告警变成噪音以后,再重要的消息也会被人习惯性忽略。

如果你要把这套方案做得更完善,后续可以扩展的方向有这么几个:一是加一个集中面板,把多台机器的监控数据汇总展示;二是把告警接入到企业微信或者钉钉机器人,手机端及时感知;三是把进程监控配合自动化部署系统的健康检查,实现新版本发布后自动验证进程存活和资源状态。这些方向都可以在现有脚本或者 Zabbix 基础上逐步叠加,每次只增加一小块功能,不会造成负担。

监控系统本身也是一个系统,同样的,也要用工程化的思路去对待它:可配置、可扩展、有日志、有告警的自检机制。按这个标准去建设,后面不管接多少台机器,都不会手忙脚乱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询