跳至正文

一条 proxy_pass 炸掉整台 Nginx:DNS 解析的 36 小时惨案

凌晨两点,Nginx 挂了

昨天凌晨,我收到告警——网站返回 502。登上去一看,Nginx 进程根本没在跑。日志里赫然一行:host not found in upstream

什么上游?我没改过配置啊。翻了半天,定位到一个几乎遗忘的文件:image-tmdb-proxy.conf。里面有一行 proxy_pass https://image.tmdb.org,是给 NAS 影视刮削器做的反向代理。Nginx 启动时会解析所有 proxy_pass 里的域名,DNS 解析失败,整个 Nginx 就起不来。不是”这个 location 挂了”,是所有站点一起陪葬。一个几乎没人用的 TMDB 图片代理,把整个服务器拖垮了 36 个小时。

36 个小时。从 4 月 16 号上午 10:53 到 4 月 17 号晚上 22:33。中间整个网站完全不可用,而我居然没第一时间察觉——告警通知被淹没在 NAS Docker 日志里了。

DNS 解析:Nginx 的隐藏地雷

这个坑挺经典的,没踩过很难意识到。Nginx 的 proxy_pass 在启动阶段就做 DNS 解析,域名不可达就直接拒绝启动。这是设计选择不是 bug,但很多场景下域名是动态的,你不希望它拖垮整个服务。

修复方案是用变量间接引用。把直接写域名:

proxy_pass https://image.tmdb.org;

改成:

set $tmdb_upstream https://image.tmdb.org;
proxy_pass $tmdb_upstream;

同时加 resolver 8.8.8.8 valid=30s;。这样 Nginx 启动时不会立即解析,而是请求到达时再解析。即使 DNS 暂时挂了,也只是这个 location 返回 502,其他站点照常运行。一个 set 指令的差别,就是”全站挂 36 小时”和”一个代理暂时 502″的差别。

我顺便检查了其他配置,发现还有一个 proxy_pass 写了外部域名,赶紧一并改了。不排查不知道,一排查到处都是定时炸弹。

我对”可用性”有了新的理解

36 个小时这个数字让我有点难受。不是故障本身多严重——Nginx 起不来,systemctl restart 就好了。难受的是我居然让它挂了这么久才发现。告警是有的,但被噪音淹没了。告警的有效性,不取决于你设了多少条规则,而取决于你能不能在噪音里听到信号

我现在的告警太粗了,磁盘满了告一次,CPU 高了告一次,进程挂了又告一次。消息太多,每条都不紧急,最后全部被忽略。就像设了十个闹钟,每个都按掉继续睡,跟没设有什么区别?

我打算做两件事:第一,告警分级,Nginx 挂掉这种”全站不可用”必须是 P0,直接推手机通知而不是塞进日志流;第二,把 proxy_pass 域名检查加到巡检脚本,每周跑一次,有硬编码域名就自动提醒。改动不大,但能避免下一次同样的 36 小时。

运维不是”出了问题修好就行”,而是”出了问题后,确保它不会以同样的方式出第二次”。修复配置用了十分钟,修复告警体系要花更多心力。但这一次,我愿意花。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注