跳到主要内容

生产故障案例集

真实生产事故合集,按"现象 → 排查 → 根因 → 修复 → 预防"五段式。

案例 1:偶发 502,每分钟几次

现象

  • Nginx error.log 偶发 upstream prematurely closed connection
  • 时间随机,没有规律
  • 后端服务正常,没崩

排查

# 看错误频率
grep "prematurely closed" /var/log/nginx/error.log | wc -l
# 看错误时间分布
grep "prematurely closed" /var/log/nginx/error.log | awk '{print $1, $2}' | uniq -c

抓包发现:Nginx 通过 keepalive 连接发请求时,连接已被后端关闭,导致 RST。

根因

后端 Node 的 keepAliveTimeout(5s)小于 Nginx 的 keepalive_timeout(60s)。Nginx 拿了快过期的连接发请求,正好碰上后端关闭。

修复

// Node
server.keepAliveTimeout = 65 * 1000; // 比 Nginx 大
server.headersTimeout = 66 * 1000;

或 Nginx 端开重试:

proxy_next_upstream error timeout http_502;
proxy_next_upstream_tries 2;

预防

  • 上下游 keepalive 时间必须 后端 > 前端代理
  • 监控 502 错误率,超阈值告警

案例 2:reload 后部分请求 502

现象

执行 nginx -s reload 后几秒内偶发 502,之后恢复正常。

排查

看 error.log:

upstream timed out
no live upstreams while connecting to upstream

nginx -t 配置无误。

根因

新 worker 启动时还没解析完上游 DNS(server backend.example.com),请求过来时 upstream 列表为空。

老 Nginx 解析 DNS 只在启动时一次,新 worker 启动期间有几百毫秒窗口。

修复

# 显式配 resolver,让 Nginx 用动态 DNS
resolver 1.1.1.1 8.8.8.8 valid=30s ipv6=off;

upstream backend {
server backend.example.com resolve;
}

或者使用 IP 而非域名作 upstream。

预防

  • 上游用 IP 或 K8s service 内部域名(短路径)
  • 监控 reload 后短期 5xx 突增

案例 3:高并发下连接数飙升然后 500

现象

QPS 涨到一定阈值后,error.log 大量:

worker_connections are not enough
500 Internal Server Error

排查

ss -s
# Total: 65000+ TCP
# 每 worker 连接接近 worker_connections 上限

ss -tn state time-wait | wc -l
# 几万个 TIME_WAIT

根因

  1. worker_connections 1024 太小
  2. 上游没用 keepalive,每请求新建 TCP → TIME_WAIT 堆积
  3. 本地端口耗尽

修复

events {
worker_connections 65535;
}

upstream backend {
server 10.0.0.1:8080;
keepalive 100;
}

location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}

系统层:

sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.ip_local_port_range="10000 65535"

预防

  • 压测找到 worker_connections 上限
  • 监控 connections 使用率

案例 4:SPA 部署后偶发白屏

现象

用户报告偶尔打开页面白屏,刷新就好。

排查

浏览器 console:

Refused to execute script from 'app.abc123.js' because its MIME type ('text/html') is not executable

根因

发版后 CDN 部分节点还缓存着旧 index.html,引用的旧 hash JS 文件源站已删。Nginx 找不到旧 JS,try_files fallback 到 index.html,返回 HTML 给浏览器当 JS 执行。

修复

# JS / CSS 找不到就 404,不要 fallback
location ~* \.(js|css|woff2?|png|jpg|svg)$ {
try_files $uri =404;
expires 1y;
}

发版策略改为:

  1. 先发新版(保留旧 JS 文件)
  2. 等 CDN 全部刷完 + 用户老 HTML 缓存过期
  3. 清理 7 天前的旧 JS

预防

  • 静态资源 404 监控
  • 发版保留 N 个版本,不立即删
  • 双写过渡

案例 5:CPU 100% 但 QPS 不高

现象

单台 Nginx CPU 一个核占满,其他核空闲。

排查

top -H -p $(pgrep nginx | head -1)
# 单线程 worker 跑满

只有 1 个 worker:

worker_processes 1;

根因

配置写死 worker_processes 1,老配置没改。

修复

worker_processes auto;
worker_cpu_affinity auto;

预防

  • 配置 review checklist 强制 auto
  • 监控单核 CPU 使用率(不只看平均)

案例 6:CC 攻击打挂源站

现象

某天下午突然源站 CPU 100%、Nginx 大量 503,Node 服务被打死。 access.log 大量来自固定 IP 段的请求,UA 单一。

排查

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head
# Top IP 每秒上千请求

应急修复

临时封 IP 段:

deny 1.2.3.0/24;
deny 5.6.7.0/24;

加全局限流:

limit_req_zone $binary_remote_addr zone=global:10m rate=30r/s;
server {
limit_req zone=global burst=50 nodelay;
limit_req_status 429;
}

长期方案

  • 接入 CDN / Cloudflare WAF
  • fail2ban 自动封禁
  • 关键接口加图形/滑动验证
  • 监控 QPS 异常告警

案例 7:证书过期全站挂

现象

某天凌晨用户全部报 NET::ERR_CERT_DATE_INVALID

根因

Let's Encrypt 证书 90 天有效期。certbot 自动续期任务在某次系统升级中坏了,几次续期失败没人看告警。

紧急修复

sudo certbot renew --force-renewal
sudo systemctl reload nginx

预防

  • 监控证书剩余有效期(Prometheus blackbox_exporter)
  • 30 天告警、7 天紧急告警
  • certbot renew 日志监控
  • 多通道告警(不要只邮件)

案例 8:WebSocket 连不上 / 频繁断

现象

前端报 1006 close code,断线重连不停。

排查

看 Nginx 配置,发现 /ws/ location 没配 Upgrade 头。

根因

WebSocket 升级握手 Nginx 默认不转 Upgrade/Connection 头。

修复

location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}

预防

  • WebSocket 路由必须配 snippet 模板
  • 监控 WS 异常断开率

案例 9:上传大文件 413

现象

前端上传 50MB 文件,Nginx 直接返回 413 Request Entity Too Large

根因

client_max_body_size 默认 1MB。

修复

# 全局或 location 级
location /api/upload {
client_max_body_size 100m;
client_body_buffer_size 1m;
client_body_timeout 300s;
proxy_pass http://backend;
}

后端(Node)也要相应调大:

app.use(express.json({ limit: '100mb' }))

案例 10:SSE 流式输出客户端等很久才收到

现象

后端 LLM 流式输出,期望 token 实时显示。但用户看到全部内容一次性出现。

根因

Nginx proxy_buffering on(默认),把流式响应攒着一次性发给客户端。

修复

location /api/stream {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off; # 关键
proxy_cache off;
proxy_read_timeout 3600s;

# SSE 必备
add_header X-Accel-Buffering no;
}

案例 11:日志写满磁盘

现象

系统报 No space left on device,所有写操作失败。

排查

df -h
du -sh /var/log/* | sort -h | tail
# /var/log/nginx/access.log 50G

紧急修复

# 立即释放(不要 rm,nginx fd 还持有)
sudo truncate -s 0 /var/log/nginx/access.log
sudo nginx -s reopen

根因

logrotate 配错或没生效。

预防

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}

监控 /var/log 使用率,80% 告警。


案例 12:reload 后某些配置没生效

现象

改了 worker_processesevents { worker_connections }nginx -s reload 后通过 ps / top 看 worker 数没变。

根因

worker_processesworker_connections 这类只在启动时读取,reload 不重启 worker 不生效。

修复

sudo systemctl restart nginx
# 而不是 reload

但 restart 会中断连接。可用热升级 kill -USR2 平滑切换。

预防

  • 文档明确哪些配置 reload 生效、哪些需要 restart
  • 变更评审时确认

案例 13:CORS 偶发失败

现象

同一 API 跨域请求有时成功有时失败,浏览器报 CORS preflight 错。

根因

CDN 缓存了第一个用户的响应(含 Access-Control-Allow-Origin: https://a.example.com),其他 Origin 用户拿到这个缓存被拒。

修复

add_header Vary Origin always; # 必加,告诉缓存按 Origin 分别存
add_header Access-Control-Allow-Origin $http_origin always;

always 关键字让 4xx/5xx 响应也加这个头。


案例 14:upstream 健康检查"假阳性"

现象

某后端实例其实已挂,但 Nginx 还在转发请求过去,导致大量 502。

根因

开源 Nginx 没有主动健康检查,依赖被动判断(请求失败 max_fails 次才标记 down)。max_fails 默认 1,但 fail_timeout 默认 10s,也就是 10s 内还会有些请求路由到挂的实例。

修复

  • 用 Tengine / Nginx Plus 的主动健康检查
  • 或用 K8s readinessProbe + Service(Nginx 之外)
  • 调小 fail_timeout
upstream backend {
server 10.0.0.1:8080 max_fails=2 fail_timeout=5s;
server 10.0.0.2:8080 max_fails=2 fail_timeout=5s;
}

通用排查清单

遇到 Nginx 问题先按这个顺序看:

  1. nginx -t 配置语法
  2. tail -f /var/log/nginx/error.log
  3. systemctl status nginx
  4. ss -tnlp | grep nginx 端口监听
  5. ps aux | grep nginx 进程数 + 内存
  6. curl -I http://127.0.0.1 本地直接访问
  7. 上游:curl -I http://upstream-ip:port
  8. 抓包:tcpdump -i any -nn host upstream-ip and port 8080

延伸阅读