Margrop
Articles256
Tags428
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.js OpenAI OpenClaw OpenResty PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE SOCKS5 SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VPN VPS Web 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 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

Prometheus说"我很好",但我总觉得哪里不对:一次沉默的监控失效

Prometheus说"我很好",但我总觉得哪里不对:一次沉默的监控失效

Prometheus说”我很好”,但我总觉得哪里不对:一次沉默的监控失效

说出来你们可能不信,今天我发现了一个所有监控系统的终极悖论:监控系统告诉你一切正常,但实际上是监控系统本身出了问题。

对,你没听错。

Prometheus 的状态页面显示”一切正常”,targets 接口返回的状态是”healthy”,但实际上有一半的监控目标已经失联超过 48 小时了——而我浑然不知。

这就是今天的故事。

早上10点:我发现了一个奇怪的现象

今天上午,我正在例行检查监控系统。打开 Grafana,看了一眼仪表盘,一切都是绿色的。Prometheus 的 targets 页面显示 34 个目标,全部 up。

作为一个在上海当了几年运维的人,我看到这种”完美状态”的第一反应不是开心,而是——不对劲

为什么?因为我太清楚我的服务器们是什么德性了。它们从来不会集体表现良好,总是有那么几个在闹脾气。

我决定深挖一下。

第一步:Prometheus说一切正常,但我有疑虑

我先查了一下 Prometheus 的告警规则。告警列表:空的。targets 状态:全部 healthy。服务运行时间:17天。

看起来没什么问题。

但我还是打开 Targets 页面看了一眼。结果我发现了一件事:最后一批 targets 的采集时间是 48 小时前。

等等,48 小时前?那不就是前天吗?

我再看了一遍,确信没看错。最后一次成功采集的时间戳显示的是两天前的某个时间点,而现在已经超过 48 小时没有任何新的数据了。

但奇怪的是,Prometheus 的状态页面依然显示所有目标为 up。

第二步:去Grafana验证一下

我打开 Grafana,找到了一个 CPU 使用率的仪表盘。正常情况下,Grafana 应该显示实时的 CPU 数据,图表应该是”流动”的——右端是最新数据,左端是历史数据。

但我看到的图表,右端是平的。

没有任何新的数据点。

我选了最近 1 小时的数据范围,结果——一片空白。不是零,是完全没有数据。

这就很奇怪了。Prometheus 明明说 targets 全 up,为什么 Grafana 拿不到数据?

第三步:手动测试抓取

我在 Prometheus 服务器上跑了一个手动抓取测试,结果很有意思:

  • lastError: 空
  • lastScrape: 48 小时前
  • health: up

好家伙。health: uplastScrape 是 48 小时前。这两个概念在 Prometheus 里居然是分开的——health 表示目标本身是否可达,lastScrape 表示最后什么时候抓的数据。

问题是:Prometheus 认为目标可达,所以 health 显示 up,但因为某种原因,抓取任务本身停了,所以没有新的数据进来。

第四步:检查Prometheus的抓取配置

我开始排查 Prometheus 的抓取配置。配置看起来很正常:15秒一次抓取,targets 列表里也确实有那些服务器。

配置没问题。

第五步:检查Prometheus日志

我去看了 Prometheus 的日志。日志里出现了这个:

1
level=warn component=scrape_manager target=node_exporter msg="append failed" err="persistence queue is full"

“persistence queue is full”。持久化队列满了。

这个错误信息告诉我:Prometheus 在尝试写入数据的时候,发现队列满了,写不进去。于是它跳过了这次抓取。但因为目标是可达的(网络没问题),所以 health 依然显示 up。

这就是那个”沉默的失效”——错误没有触发告警,因为 Prometheus 认为这不是”目标 down”,只是”写入失败”。

根因找到了:磁盘I/O瓶颈

我顺着日志继续查,发现了更多的错误:

1
level=error component=persistence msg="failed to flush chunks" err="no space left on device"

“No space left on device”。磁盘空间不足。

这下真相大白了:

  1. Prometheus 的数据目录所在的磁盘空间不足(或者 I/O 瓶颈)
  2. Prometheus 无法写入新的数据
  3. 为了防止内存溢出,Prometheus 跳过了抓取周期
  4. health check 依然认为目标可达(因为网络没问题)
  5. 但实际上没有任何新数据进来

这个”沉默的失效”持续了 48 小时,直到我今天碰巧去 Grafana 看图表才发现。

修复过程

找到根因就好办了。我先清理了一些旧的监控数据,重启了 Prometheus 服务:

1
2
3
4
5
6
7
8
9
10
11
# 查看数据目录大小
du -sh /var/lib/prometheus/

# 查看磁盘使用情况
df -h /var/lib/prometheus/

# 清理旧数据
find /var/lib/prometheus/ -type f -mtime +7 -delete

# 重启 Prometheus
systemctl restart prometheus

然后我等了几分钟,再去看 Grafana——图表终于开始流动了。

最新数据进来了。

中午:顺便查了一下其他告警配置

既然发现了这个问题,我就顺便检查了一下其他告警规则——Prometheus 会不会有其他”沉默的失效”场景?

还真找到了一个相关规则。这个规则只检查 up == 0,也就是目标完全不可达的情况。但它不会检查”目标可达但数据停止更新”的情况。

换句话说:Prometheus 的默认告警规则没有覆盖”数据停滞”这个场景。

下午:新增了一个告警规则

既然发现了漏洞,我就补上了。

我在告警规则里加了一条新的告警:

1
2
3
4
5
6
7
- alert: PrometheusScrapeStalled
expr: time() - prometheus_target_scrape_interval_seconds * 2 > prometheus_target_last_scrape_timestamp_seconds
for: 5m
labels:
severity: warning
annotations:
summary: "Prometheus scrape stalled for {{ $labels.job }}"

这条规则的逻辑是:如果当前时间减去最后一次抓取时间,大于两倍的抓取间隔,就触发告警。

这样下次再出现类似的”数据停滞”问题,我就能第一时间知道了。

晚上:总结今天的感悟

今天的故事说起来不复杂:Prometheus 的磁盘满了,导致数据抓取停滞,但因为目标是可达的,所以健康检查依然通过,告警没有触发。

这个故事告诉我们几个道理:

第一,监控本身也需要被监控。

我们通常会给服务器、应用、数据库加上监控,但很少会监控”监控系统本身”是否正常工作。Prometheus health 页面显示 up,不代表它的抓取任务在正常工作。你得看实际的数据流,得看图表有没有新数据,得看 lastScrape 时间戳。

第二,”一切正常”往往是最危险的状态。

我看到 Grafana 全绿、Prometheus 全 up 的时候,第一反应不是”太好了”,而是”不对劲”。这种职业敏感度,是被无数次的”明明正常但实际已经挂了”给训练出来的。希望大家也能培养出这种直觉。

第三,”沉默的失效”比”响亮的失效”更可怕。

什么叫”响亮的失效”?服务直接挂了,网站打不开,用户投诉,告警疯狂响。这种失效你一眼就能看到,问题容易排查。”沉默的失效”呢?监控系统告诉你正常,用户没投诉,但实际上数据已经不更新了,你完全不知道。这种失效往往会造成更大的损失——因为你不知道它已经坏了多久了。

第四,定期检查监控数据的时效性。

今天如果不是我去 Grafana 看了一眼图表,可能再过一周都不会发现这个问题。建议大家都养成定期查看监控图表的习惯,看看数据是不是真的在流动,是不是真的在更新。

写在最后

今天的教训就这些。

简单来说:不要相信监控,要验证监控。Prometheus 说一切正常,可能只是它没有发现问题的能力,而不是真的没有问题。

好了,今天的工作总结就到这里。希望明天不要再发现这种”沉默的失效”了。

毕竟,打工已经这么辛苦了,还要时不时被服务器”善意欺骗”一下,真的很累。

明天继续加油吧。


作者:小六,一个在上海努力生存的普通打工人,今天被 Prometheus “善意欺骗”了

Author:Margrop
Link:http://blog.margrop.com/post/2026-04-11-when-prometheus-stopped-scraping-targets-but-everything-looked-fine/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可