Linux 系统监控与告警方案

1次阅读

systemd监控服务比脚本更可靠,应关注意图状态而非进程快照;用restart=always、substate检查、execstartpost健康检测、socket激活替代netstat;内存告警看memavailable,cpu看load和procs_running;journalctl需结构化、持久化并去重。

Linux 系统监控与告警方案

systemd 监控服务状态比写脚本更可靠

很多用户一上来就想用 ps + grep 检查进程是否存在,但这种做法在容器化或 fork 多次的进程里极易误判。真正该盯的是服务的“意图状态”,不是“进程快照”。

systemd 自带健康检查能力,只要服务单元配置了 Restart=alwaysRestartSec=10,它就会自动拉起崩溃的服务;再配合 StartLimitIntervalSecStartLimitBurst,还能防住反复崩溃打爆日志。

  • 别手动轮询 systemctl is-active <service></service> —— 它返回快,但不反映真实启动结果;改用 systemctl show -p SubState <service></service> 看子状态(比如 runningfailedstart-pre
  • 若服务启动后需额外就绪检查(如端口监听、http 健康接口),在单元文件里加 ExecStartPost=/usr/bin/curl -f http://localhost:8080/health || exit 1,失败则标记为 failed
  • 注意:启用 Restart=on-failure 时,exit 0 不触发重启,非零退出才触发 —— 很多人在健康检查脚本里忘了 set -e 或漏写 exit 1

netstat 已弃用,监控端口请用 ss + systemd-socket

netstat 在多数新发行版里已不预装,且扫描全端口慢、权限要求高。更轻量、更准确的方式是直接查 ss,或者——更进一步,把监听行为交给 systemd 管理。

比如想确保 redis6379 端口始终可连,与其定时跑 ss -tln | grep :6379,不如把 redis 改成 socket 激活模式:

redis.socket 文件里写: [Socket] ListenStream=6379 Accept=false

这样端口由 systemd 统一 bind,一旦被占用或关闭,systemctl status redis.socket 就能立刻暴露问题。

  • ss -tlnp | grep :6379netstat -tulnp 快 3–5 倍,且不依赖 /proc/net 全量读取
  • ss -tlnp 时注意:没权限时看不到 PID/Program name 列,别只看端口存在就认为服务活着
  • 如果服务本身不支持 socket 激活(如老版本 nginx),至少用 ss -tlnp 替代 netstat,并过滤掉 LISTEN 状态而非 ESTABLISHED

告警阈值不能只看 CPU / 内存百分比

CPU 使用率 95% 不一定代表有问题,内存 85% 却可能已触发 OOM killer —— 因为 linux 的内存回收机制和 CPU 调度逻辑完全不同。监控必须分层:看指标,更要看行为。

真正关键的是 /proc/meminfo 里的 MemAvailable(可用内存,含可回收缓存),不是 Free;是 /proc/stat 里的 procs_running(运行队列长度),不是单核 %us

  • 告警内存时,优先用 MemAvailable ,而不是 <code>MemUsed% > 90% —— 后者在大量 page cache 场景下会频繁误报
  • CPU 告警应结合 load averageprocs_running:若 1 分钟 load > CPU 核数 × 2 且 procs_running > 10,才说明有真实调度压力
  • 磁盘 IO 要看 iostat -x 1%utilawait:前者接近 100% 只说明设备忙,后者持续 > 50ms 才表明响应延迟异常

journalctl 日志告警容易漏掉关键上下文

直接用 journalctl -u nginx --since "1 hour ago" | grep "Error" 做告警,会丢掉错误前后的请求链路、系统状态甚至同一时间其他服务的日志。日志不是孤立事件,是时间线上的切片

正确做法是用 --output=json 输出结构化日志,再用 jq 提取 __REALTIME_TIMESTAMPSYSLOG_IDENTIFIER,按时间窗口聚合分析。

  • 别用 tail -f /var/log/syslog | grep —— journal 日志默认不落盘到 syslog,且 tail 无法跨 journal 文件滚动
  • journalctl -u nginx -o json --since "2 minutes ago" 获取原始 JSON,再 pipe 给 jq 'select(.PRIORITY == "3")' 过滤 error 级别
  • 重要:journalctl 默认只保留最近 1–2 周日志,生产环境务必配 SystemMaxUse=1GMaxRetentionSec=3month,否则告警时根本查不到历史上下文

最常被忽略的一点:所有基于日志的告警都必须带时间戳对齐和去重逻辑,否则一次内核 panic 可能触发几百条重复告警 —— 不是监控太灵敏,是没处理好日志的爆发性输出特性。

text=ZqhQzanResources