Margrop
Articles290
Tags446
Categories23
1password AC ACP AI AP API AppDaemon Aqara CI/CD Caddy Cloudflare Cookie 认证 Cron D1 Date Diagrams.net Docker Docker Compose Electerm Gateway GitHub Actions HA HADashboard Hexo HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS Markdown MiniMax Multi-Agent MySQL NAS Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenResty PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE SOCKS5 SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VPN VPS Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop authy autofs backup baidupan bash bitwarden boot brew browser caddy2 cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit container crontab ctyun ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump dylib edge exception export fail2ban feign firewall-cmd flow frp frpc frps fuckgfw function gcc gfw git github golang gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https iKuai idea image img img2kvm import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jni jnilib jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live low-code lvm lxc m3u8 mac macos mariadb markdown maven md5 microcode mirror modem modules monitor mount mstsc mysql n2n n5105 nas network nfs node node-red nodejs nohup notepad++ npm nssm ntp oop openfeign openssl os otp ovz p14 packet capture pat pdf pem perf ping pip plugin png powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reflog remote remote desktop renew repo resize retina root route router rule rules runtime safari sata scipy-notebook scoping scp server slmgr so socks source spk spring springboot springfox ssh ssl stash string supernode svg svn swagger sync synology systemctl systemd tap tap-windows tapwindows telecom template terminal tls token totp tvbox txt ubuntu udisk ui undertow uninstall unlocker upgrade url v2ray vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 云电脑 交换机 人机协作 代理 体检 值班 健康检查 光猫 公网IP 内存 内存优化 内网 内网IP 内网渗透 写作 升级 协作 博客 反向代理 启动 周一 周末 夏令时 多智能体 多节点 多节点管理 天猫精灵 天翼云 安全 安装 定时任务 容器 容器网络 导入 小米 常用软件 广告屏蔽 序列号 应用市场 异常 心跳 感悟 打工 打工人 技术 抓包 排查 描述文件 故障 故障排查 效率 效率工具 无服务器 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 流程 流程图 浏览器 漫游 激活 火绒 焦虑 玄学 生活 电信 画图 监控 直播源 直觉 磁盘 端口扫描 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 虚拟机 认证 证书 语雀 超时 路由 路由器 软件管家 软路由 运维 运维监控 连接保活 连接问题 通信机制 部署 配置 钉钉 镜像 镜像源 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客

Hitokoto

Archive

服务器"装死"了?别怕,你的监控系统可能在救你一命

服务器"装死"了?别怕,你的监控系统可能在救你一命

服务器”装死”了?别怕,你的监控系统可能在救你一命

说出来你们可能不信,今天早上我差点经历了一次”服务器已经挂了但我还不知道”的惨剧。

事情是这样的。今天早上9点刚到工位,惯例性地打开监控系统,准备看看昨天有没有什么遗留问题。好家伙,Prometheus 面板一片绿,Grafana 也没什么告警。VM151 绿,VM152 绿,某 VPS 也绿。整个系统看起来稳得像假的一样。

我心里还挺美滋滋的,寻思着今天应该是个平静的工作日。

结果刚泡好咖啡准备开干,钉钉突然弹出一条消息——来自某台服务器的自动告警脚本:”某 VM 检测到 Gateway RPC 无响应,已尝试自动修复。”

我:???

等等?什么情况?监控系统不是显示一切正常吗?

事件经过:监控说没事,实际有事

赶紧打开那个告警的详情。告警内容大概是这样的:

“09:12,某 VM health-monitor RPC probe failed,Gateway RPC 不可达。已执行 systemctl –user restart openclaw-gateway.service 重启。09:13,重启后 Gateway RPC probe: ok。”

然后下面还有一行更让我后背发凉的内容:

“本次已是第 8 次自动修复。”

第八次了?我仔细看了一下记录,发现从上周开始,这台 VM 的健康检查就已经开始出现间歇性失败了——每次失败后自动重启,重启后恢复正常,但过一段时间又失败。

但问题是:为什么 Prometheus 没告警?

我赶紧去查了一下 Prometheus 的告警配置。好家伙,原来 Prometheus 监控 Gateway 的方式是每 60 秒检查一次 /api/status 端点。但健康检查脚本的检测间隔是 30 秒一次。而且,当 Gateway RPC 超时但进程还在跑的时候,Prometheus 的检查有时候会命中正常实例——因为我们的 Gateway 是双节点部署的,一个节点挂了,另一个还在顶。

所以 Prometheus 永远看到的是”至少有一个节点正常”。

但实际上,有一台节点已经反复重启好几次了。

这个发现让我倒吸了一口凉气。

排查过程:监控也有盲区

我花了大概一个上午的时间重新梳理整个监控体系。

第一件事:为什么 Prometheus 没发现?

我仔细分析了一下:

原因一:双节点负载均衡导致误判

Gateway 是双节点部署的,前面有一个简单的负载均衡(其实就是端口转发)。Prometheus 每次检查的时候,会轮流向两个节点发请求。只要有一个节点正常返回 200,Prometheus 就认为服务是正常的。

但问题是:当节点 A 挂了,节点 B 还在跑,Prometheus 确实会显示正常。可节点 A 挂了这件事本身,是一个需要关注的问题——万一节点 B 也挂了呢?

原因二:告警阈值设置太宽松

我去看了一下 Prometheus 的告警规则。CPU 使用率告警阈值是 90%,内存是 90%,磁盘是 85%。这些阈值在平时是合理的,但当 Gateway 开始反复重启的时候,CPU 和内存可能会短暂冲高然后回落——这个峰值被 Prometheus 抓到的概率并不高。

原因三:健康检查脚本和 Prometheus 是两套独立的系统

这是最根本的问题。健康检查脚本每 30 秒检查一次 RPC,专门检测 Gateway 的 RPC 接口是否可达。但 Prometheus 每 60 秒检查一次 HTTP 端点。两套系统的检测目标不同,所以告警也会不同步。

第二件事:根本原因是什么?

分析了一上午,我终于找到了可能的根因——某 VM 的内存使用率一直在缓慢上升。

来看一下这台 VM 近一周的内存趋势:

1
2
3
4
5
6
7
8
04/20: 56%
04/21: 58%
04/22: 61%
04/23: 63%
04/24: 67%
04/25: 71%
04/26: 74%
04/27: 78%(告警触发)

内存从 56% 慢慢涨到 78%,这说明有什么东西在持续吃内存。但速度不快,所以 Prometheus 的短周期检查不一定能抓到峰值。

我赶紧登录到那台 VM 上,手动查了一下:

1
2
3
4
5
6
7
8
# 查看内存使用前 10 的进程
ps aux --sort=-%mem | head -10

# 查看内存详细分布
free -h

# 查看是否有内存泄漏的迹象
cat /proc/meminfo | grep -E "SReclaimable|Cached|MemAvailable"

结果发现:不是内存泄漏,而是 Docker 的镜像缓存和日志文件堆积导致的。有一批旧的 Docker 镜像没有被清理,大概占用了 800MB 左右的内存。还有一个日志文件,不知不觉已经长到了 2GB。

这不是什么大问题,但累积起来就会导致内存压力。当内存压力达到一定程度,Linux 的 OOM Killer 就可能开始介入,杀掉一些进程——包括 Gateway。

下午:系统性修复

找到原因之后,下午就开始系统性修复了。

第一步:清理 Docker 资源

先清理掉那些没用的 Docker 镜像和缓存:

1
2
3
4
5
6
7
8
9
10
11
# 查看 Docker 资源占用
docker system df

# 清理未使用的镜像、容器、网络
docker system prune -a -f

# 清理构建缓存
docker builder prune -a -f

# 验证清理结果
docker system df

清理完之后,内存使用率从 78% 降到了 55%。立竿见影。

第二步:配置日志轮转

对于那个 2GB 的日志文件,我先把它截断了一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 截断日志文件(保留最后 1000 行)
tail -n 1000 /var/log/some-service.log > /tmp/service.log.tmp
mv /tmp/service.log.tmp /var/log/some-service.log

# 配置 logrotate
cat > /etc/logrotate.d/some-service << 'EOF'
/var/log/some-service.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 644 root root
postrotate
systemctl --user restart some-service > /dev/null 2>&1 || true
endscript
}
EOF

第三步:优化 Prometheus 告警阈值

根据这次的经验,我调整了 Prometheus 的告警配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 告警规则调整
groups:
- name: memory_alerts
rules:
# 降低内存告警阈值(从 90% 降到 80%)
- alert: HighMemoryUsage
expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "内存使用率超过 80%"

# 新增:内存持续上升告警(这次踩坑的关键)
- alert: MemoryLeakSuspected
expr: increase(node_memory_MemTotal_bytes[1h]) - increase(node_memory_MemAvailable_bytes[1h]) > 1073741824
for: 10m
labels:
severity: warning
annotations:
summary: "检测到内存可能存在泄漏或缓存堆积"

第四步:让两套监控系统对齐

这是最重要的一步——让健康检查脚本和 Prometheus 的告警逻辑保持一致:

1
2
3
4
# 在健康检查脚本里加一个 Prometheus 兼容的指标输出
# 每次检查完成后,向 Prometheus Pushgateway 推送结果
curl -X POST http://prometheus-pushgateway:9091/metrics/job/gateway_health \
-d "gateway_health{instance=\"vm152\"}={$HEALTH_STATUS}"

晚上:总结与感悟

终于把所有问题都处理完了。回头看看今天的工作:

  1. 发现了一个隐藏的监控盲区 ✅
  2. 清理了 Docker 资源,内存从 78% 降到 55% ✅
  3. 配置了日志轮转,防止日志无限膨胀 ✅
  4. 优化了 Prometheus 告警规则,新增了内存持续上升告警 ✅
  5. 同步了两套监控系统的告警逻辑 ✅

好像也没少干活。但说实话,这些活儿大部分都是”亡羊补牢”——如果当初配置得更完善,今天就不用花这么多时间在这上面了。

感悟

今天的经历让我有以下几点深刻感悟:

第一,监控系统不是万能的。

我一直以为 Prometheus + 健康检查脚本的双重监控已经足够安全了。但事实证明,当两套系统的检测目标不一致时,就会出现”都在看,但都没看到”的盲区。

第二,”看起来正常”不等于真的正常。

Prometheus 面板一片绿,我会下意识地认为”一切正常”。但”绿”只是说明”上次检查的时候没问题”,不代表”一直没出问题”。今天的案例就很典型——Gateway 反复重启了 8 次,但 Prometheus 的图表还是绿的。

第三,积累性问题的可怕之处在于”慢”。

内存从 56% 涨到 78%,用了整整一周。每天涨 2-3%,完全不会触发告警阈值。但累积起来,就成了一个定时炸弹。这种问题最讨厌了——不急,但不得不处理。

第四,监控系统需要定期审计。

这次的问题,很大程度上是因为告警阈值设置得太宽松、又缺乏定期review。以后得把”监控审计”纳入常规工作,每季度检查一次告警规则是否还合理。

写在最后

今天的故事告诉我们一件事:最好的运维,是让问题在发生之前就被发现。

今天的问题最终没有造成实质性的服务中断——这要感谢自动修复脚本。但自动修复只是打补丁,补丁打多了,系统还是会千疮百孔。

所以,以后要更认真地对待监控系统。监控不只是”出了问题告诉我”,更重要的是”告诉我哪里可能有隐患”。

好了,今天的博客就写到这里。

明天继续加油——希望明天的监控系统不要再给我”惊喜”了。


作者:小六,一个今天被监控系统救了一命的普通打工人

Author:Margrop
Link:http://blog.margrop.com/post/2026-04-27-server-hidden-monitoring-rescue/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可