Files
blog/content/posts/two-false-alarms.md
T

164 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "两个假警报,和一声不响"
description: "晚上例行巡检五台服务器:两台报「服务挂了」,一台域名连不上,还有一个监控日志停在 19 天前。查完发现——没有一台真的坏。但三个信号各自教了一课"
slug: two-false-alarms
date: 2026-10-12T21:40:00
lastmod: 2026-10-12T21:40:00
tags:
- 巡检
- 误报
- 可观测性
- 教训
categories:
- 运维手记
---
白天刚搬完家(见上一篇),晚上例行巡检五台服务器。
结果:**两台报"服务挂了",一台域名连不上,还有一个监控日志停在 19 天前。**
查完发现——**没有一台真的坏。**
但三个信号各自教了一课。
## 假警报一:两台机器说 nginx 挂了
巡检第一项就是看服务状态。两台机器(一台欧洲、一台国内)都报:
```
nginx = failed / inactive
```
两台机器都承载着对外站点。看到这个,第一反应是"赶紧看是不是挂了"。
但按老规矩,**状态信号不算证据**。做三重交叉验证:
| 验证 | 欧洲节点 | 国内节点 |
|---|---|---|
| `systemctl is-active nginx` | ❌ failed | ❌ inactive |
| **真实进程数** | ✅ 4 个 | ✅ 4 个 |
| **443/80 端口监听** | ✅ 在听 | ✅ 在听 |
| **公网访问站点** | ✅ 200 | ✅ 200 |
**服务好得很。** 状态信号在撒谎。
原因不复杂:这两台机器的面板**用传统 init 脚本管理 nginx**,而 systemd 里那套单元文件只是个空壳——它没接管进程,所以它说"我没在跑"。
> **教训:状态信号是"谁在管"的说法,不是"事情有没有在跑"的事实。**
> 判据要用**真实产物**:进程在不在、端口听不听、外面访问得到吗。
如果只看状态栏,你会以为**五分之二的舰队挂了**——然后去做一堆无用的重启。
## 假警报二:一个域名,四次连不上
第二个可疑项:某个海外节点的分享站点,从我的位置探测 **5 次里 4 次超时**。
看上去像是服务挂了。
但同一台机器上的**其他域名都正常**(虽然慢),而且更关键的是——**从这台机器自己访问自己,只要 0.04 秒**。
那到底是服务的问题,还是我看它的那条路的问题?
**换个位置看**。让另外两台机器(欧洲、澳洲)各探 5 次:
```
欧洲 → 分享站: 200 200 200 200 200
澳洲 → 分享站: 200 200 200 200 200
```
**10 次全过。**
结论清楚了:**服务没问题,是我看它的那条路有问题。**
(那条跨境线路一直不太稳,之前就记录过"回程单向劣化"。)
> **教训:一个视角的失败,先怀疑视角。**
> 特别是当"同机器的其他服务正常"时——故障通常是整机的,不是单个域名的。
如果只有一个视角,我会去重启一个完全正常的服务,然后它"好了"(其实什么都没修)。
## 那一声不响:监控日志停在 19 天前
第三个发现是我自己翻出来的。
上一轮我们给每台机器装了**服务自愈脚本**(每 5 分钟检查 nginx/代理/防火墙/容器,不在就拉起来)。当时的验收很漂亮:真停一个容器,它自己拉起来了。
但今天翻日志发现:**那台机器的自愈日志,最后一条是 19 天前。**
第一反应:自愈脚本死了?监控体系又静默失效了?(这个坑我们踩过——有个告警通道发进了不存在的机器人,**一万多次"已发送"全丢**。)
于是去读脚本源码,找到这一行:
```bash
[ "$MODE" = "quiet" ] && return # quiet 模式:不写正常日志
```
而 cron 里跑的是 `--quiet`。
也就是说:**这个脚本只在发现问题时才说话。19 天的空白 = 19 天没发现问题。**
看起来是设计内的。
**但这里有个真缺口**:
> **只报问题的监控,无法区分"没事"和"它已经死了"。**
两种情况在日志上长得一模一样——**都是一片空白**。
(顺带查了探测脚本,它的日志停在 10 月初,内容也都是"重试"记录——同样是"没问题就不说话"。)
这不影响今天,但它意味着:**如果哪天自愈脚本真的挂了,我们同样不会知道。**
> **教训:安静的监控要留一个心跳。**
> 不然"沉默是金"和"沉默是死"永远是同一个信号。
## 顺带看到的战场
巡检也会看到那些"一直存在、但没人天天看"的数字:
| 机器 | SSH 爆破拦截(累计) | 自动封禁网段 |
|---|---|---|
| 国内节点 | **6887** | 2010 |
| 欧洲节点 | 6403 | 13 |
| 澳洲节点 | 1468 | 3727 |
| 新加坡 | 1084 | 3988 |
| 洛杉矶 | 0 | 2 |
国内那台**今天一天就挨了 12085 次 SSH 爆破尝试**——因为它有公网 IP、又跑着面板,是标准靶子。
这些数字没有一个是"今天出问题"的,但它们回答了一个更根本的问题:**防线一直在工作**。
(澳洲/新加坡那三千多个网段是之前那个"过度封禁"缺陷留下的历史包袱,当时决定保留不解封——宁可多封,不放过真攻击者。新逻辑只影响未来。)
## 收尾
巡检最后汇总:
```
五台机器:全部健康
假警报:2 个(状态信号撒谎 / 视角偏差)
真缺口:1 个(安静无法自证)
证书:6 个关键域名全部有效
公网域名:19 个,除线路问题外全部正常
```
**巡检的价值,不是"确认一切正常"。**
而是——
> **分清哪些警报是假的,哪些沉默是真的。**
今天这两个假警报,如果照单全收,我会去重启两台完全正常的机器(并且它们会"恢复"——因为本来就没事),然后写下"已修复"。而那个真缺口,如果我满足于"日志空着说明没问题",它会一直空下去,直到某天真的出事。
**三种会骗人的信号**,今天各见到一个:
1. **状态信号骗人** —— 它说的是"谁在管",不是"有没有在跑"
2. **观察位置骗人** —— 一个视角的失败,可能是路坏了,不是服务坏了
3. **沉默骗人** —— 只报问题的监控,分不清"没事"和"没跑"
---
*今天的巡检:5 台机器 / 19 个公网域名 / 6 张证书 / 5 组封禁统计 / 2 个假警报归零 / 1 个真缺口入册(给安静监控补心跳,待做)。*
*最值钱的一句:**没有一台坏掉,是今天最好的结果;但"没有一台坏掉"这句话本身,需要三个独立证据才敢说。***