必须依赖 $upstream_status 判断后端真实健康状态,它记录后端返回的真实http状态码,配合 $status 可精准定位故障层级;需自定义日志格式并启用,再通过 awk 聚合分析错误分布与节点抖动,最终加权计算0–100健康分。

要真实看清后端集群的整体健康分布,不能只看 Nginx 返回给客户端的 $status(比如全是 200 或一堆 502),必须依赖 $upstream_status——它记录的是每个请求实际触达后端后,后端返回的真实 HTTP 状态码。这个变量是判断“谁在抖、抖得多狠”的核心依据。
确保日志中稳定输出 $upstream_status
默认 access_log 不包含该变量,需主动定义日志格式:
- 在
http块中添加:log_format upstream_log '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $upstream_addr $upstream_status $upstream_response_time'; - 在
server或location中启用:access_log /var/log/nginx/upstream.log upstream_log; - 重启 Nginx 后,日志每行将带上游地址、状态码和响应时间,例如:
10.0.3.5 - - [12/May/2026:06:15:22 +0000] "GET /api/order HTTP/1.1" 200 342 "-" "curl/7.81" 10.0.2.12:8080 504 3.210
按状态码+实例维度聚合统计分布
健康分布的关键是分清“错误类型”和“出错位置”。推荐用 awk 快速提取两类视图:
- 各后端地址的错误码分布(识别某台机器是否 500 高发):
awk '{print $8, $9}' /var/log/nginx/upstream.log | grep "^[45]" | sort | uniq -c | sort -nr
(假设第 8 字段是$upstream_status,第 9 是$upstream_addr) - 高频失败的后端节点(不关心具体错误码,只看谁总失败):
awk '$8 ~ /^[45]/ {print $9}' /var/log/nginx/upstream.log | sort | uniq -c | sort -nr | head -10 - 注意重试干扰:若某行显示
"502,504 10.0.2.11:8080,10.0.2.12:8080",说明发生了重试,两个地址都应纳入排查范围
结合 $status 与 $upstream_status 判定故障根因
单看 $upstream_status 容易误判。必须联动 $status 才能区分问题发生在哪一层:
-
$status = 502且$upstream_status = "-"→ Nginx 根本没连上后端(网络不通、端口未监听、防火墙拦截) -
$status = 504且$upstream_status = "-"→ 后端连接建立但无响应超时(进程卡死、OOM、或 upstream_response_timeout 设置过短) -
$status = 200但$upstream_status含逗号分隔值(如"504,502,200")→ 后端存在明显抖动,靠重试兜底成功,P99 延迟大概率已恶化 -
$status = 503且$upstream_status为数字 → Nginx 主动拒绝(如限流触发),非后端问题
计算整体健康指标并映射为可读分数
仅看原始日志不够直观,建议按分钟窗口聚合关键比率:
-
5xx 错误率:5xx 请求总数 ÷ 有效 upstream 请求总数(排除
"-") - 超时占比:504 数量 ÷ 5xx 总数(反映超时是否主导失败)
- 异常连接失败率:502/503 数量 ÷ 5xx 总数(反映后端不可达或拒绝服务)
- 将上述比率加权组合,可映射为 0–100 健康分,例如:
错误率 ≤ 0.3% → 95 分;每上升 0.2% 扣 3 分;504 占比 > 2% 额外扣 10 分;出现一次 502 直接扣 15 分











