nginx01 综合项目实战笔记
定位:把零散的 nginx 知识(静态托管/反向代理/负载均衡/HTTPS/安全)整合成完整站点,新增日志分析与故障演练
日期:2026-10-16 · 环境:WSL Debian + nginx 1.26.3
前置:nginx-静态网页 / nginx-负载均衡 / nginx-HTTPS / nginx-502故障排查
一、项目架构
用户(curl/浏览器)
│
├─ HTTP :8080 ──→ 301 跳转 ──→ HTTPS :8443
│
└─ HTTPS :8443 ──→ nginx
├─ / → 静态站(/var/www/mysite,autoindex 目录列表)
├─ /api/ → upstream 负载均衡 → 9000/9001/9002(python3 http.server)
├─ /hello → return 200 文本
├─ /private → IP白名单 + Basic Auth(.htpasswd)
├─ 全局限流 → limit_req rate=1r/s burst=5 nodelay
└─ 访问日志 → 自定义格式(含 upstream_addr / request_time)
端口清单
| 端口 | 用途 |
|---|---|
| 8080 | HTTP 入口,301 跳转到 8443 |
| 8443 | HTTPS 入口(自签名证书) |
| 9000/9001/9002 | 后端服务(python3 -m http.server) |
二、完整配置(最终版)
文件:/etc/nginx/sites-available/mysite(软链到 sites-enabled)
# ============================================
# 限速区:按客户端 IP 限流,1r/s + 突发5个不排队
# ============================================
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=1r/s;
# ============================================
# 自定义日志格式:含后端地址 + 后端响应时间 + 总耗时
# 注意:[$time_local] 后面必须有空格!否则字段错位
# ============================================
log_format main_ext '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'upstream=$upstream_addr '
'upstream_time=$upstream_response_time '
'request_time=$request_time';
# ============================================
# 负载均衡:被动健康检查(30秒内失败3次→摘除30秒)
# ============================================
upstream backend_pool {
server 127.0.0.1:9000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:9001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:9002 max_fails=3 fail_timeout=30s;
}
# ============================================
# HTTP → HTTPS 301 跳转
# ============================================
server {
listen 8080;
server_name localhost;
return 301 https://$host:8443$request_uri;
}
# ============================================
# HTTPS 主站点
# ============================================
server {
listen 8443 ssl;
ssl_certificate /etc/nginx/ssl/mysite.crt;
ssl_certificate_key /etc/nginx/ssl/mysite.key;
server_name localhost;
root /var/www/mysite;
index index.html;
# 使用自定义日志格式
access_log /var/log/nginx/mysite_access.log main_ext;
error_log /var/log/nginx/mysite_error.log;
charset utf-8;
# .md 文件按纯文本返回(Chrome 不识别 text/markdown)
location ~* \.md$ {
default_type text/plain;
}
# 静态站 + 目录索引
location / {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
# 反向代理 + 负载均衡 + 限速
location /api/ {
proxy_pass http://backend_pool/;
limit_req zone=api_limit burst=5 nodelay;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 配置生效验证点
location /hello {
default_type text/plain;
return 200 "Hello from nginx config!";
}
# IP白名单 + Basic Auth 双重保护
location /private {
allow 127.0.0.1;
deny all;
auth_basic "Restricted Area - Enter Password";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}
后端启动脚本
mkdir -p /var/www/backend1 /var/www/backend2 /var/www/backend3
echo '<h1>BACKEND-9000</h1><p>served by python3 http.server on port 9000</p>' > /var/www/backend1/index.html
echo '<h1>BACKEND-9001</h1><p>served by python3 http.server on port 9001</p>' > /var/www/backend2/index.html
echo '<h1>BACKEND-9002</h1><p>served by python3 http.server on port 9002</p>' > /var/www/backend3/index.html
nohup python3 -m http.server 9000 --directory /var/www/backend1 > /tmp/backend1.log 2>&1 &
nohup python3 -m http.server 9001 --directory /var/www/backend2 > /tmp/backend2.log 2>&1 &
nohup python3 -m http.server 9002 --directory /var/www/backend3 > /tmp/backend3.log 2>&1 &
三、日志分析(核心新增能力)
3.1 自定义日志格式的 3 个关键字段
| 变量 | 含义 | 排障用途 |
|---|---|---|
$upstream_addr |
实际打到的后端(如 127.0.0.1:9001) |
确认负载均衡是否均匀;502 时显示尝试过的所有后端(逗号分隔) |
$upstream_response_time |
后端处理耗时(秒) | 找慢后端 |
$request_time |
nginx 收请求到发完响应的总耗时(秒) | 找慢请求 |
3.2 日志样例
127.0.0.1 - - [16/Oct/2026:19:20:21 +0800] "GET /api/ HTTP/1.1" 200 70 "-" "curl/8.14.1" upstream=127.0.0.1:9001 upstream_time=0.001 request_time=0.002
字段(空格分隔):$1=IP,$7=路径,$9=状态码,$13=upstream,$15=request_time
3.3 五个 awk 分析命令
LOG=/var/log/nginx/mysite_access.log
# 1. PV / UV
echo "PV=$(wc -l < $LOG)"
echo "UV=$(awk '{print $1}' $LOG | sort -u | wc -l)"
# 2. 状态码分布
awk '{print $9}' $LOG | sort | uniq -c | sort -rn
# 3. 后端分布(负载均衡是否均匀)
awk '{print $13}' $LOG | sort | uniq -c | sort -rn
# 4. 慢请求 Top 10
awk '{
for(i=1;i<=NF;i++) if($i ~ /^request_time=/) {split($i,a,"="); rt=a[2]}
print rt, $7, $9
}' $LOG | sort -rn | head -10
# 5. URL 访问排行 Top 10
awk '{print $7}' $LOG | sort | uniq -c | sort -rn | head -10
3.4 新旧日志混合的过滤方法
改了 log_format 后,当前日志文件会混合旧格式(无 upstream= 字段)和新格式。旧格式字段数少,按固定列号取会错位。
过滤方法:只处理含 request_time= 的行
awk '/request_time=/{print $9}' $LOG | sort | uniq -c | sort -rn
生产规范:改 log_format 后 > $LOG 清空旧日志,或等 logrotate 轮转后新文件就是统一格式。
四、故障演练与排障(核心新增能力)
4.1 演练 1:单个后端挂了 → 无感知
pkill -f 'http.server 9001'
for i in $(seq 6); do curl -sk https://localhost:8443/api/; done
现象:6 次全成功,全是 9000/9002,9001 被静默跳过。
原因:proxy_next_upstream error timeout(默认开启)——连接后端失败时自动转发给下一个后端。
日志验证:upstream 字段里 9001 出现 0 次。
4.2 演练 2:所有后端挂了 → 502
pkill -f 'http.server 900'
curl -sk https://localhost:8443/api/
现象:返回 502 Bad Gateway。
日志关键线索:
upstream=127.0.0.1:9000, 127.0.0.1:9001, 127.0.0.1:9002
upstream_time=0.001, 0.000, 0.000
逗号分隔 = nginx 依次尝试了 3 个后端全部失败。
4.3 502 排障口诀
| upstream_addr 表现 | 含义 |
|---|---|
| 单个地址 | 单后端挂(但重试也失败或没开重试) |
| 多个地址逗号分隔 | nginx 依次尝试全部失败(全挂或网络不通) |
upstream=- |
请求没经过反向代理(静态资源/认证拒绝) |
4.4 被动健康检查(生产必配)
nginx 开源版无主动健康检查,用 max_fails + fail_timeout 做被动摘除:
upstream backend_pool {
server 127.0.0.1:9000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:9001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:9002 max_fails=3 fail_timeout=30s;
}
含义:30 秒内失败 3 次 → 摘除该后端 30 秒(期间不转发)→ 30 秒后试探。
为什么必配:不配的话,一个后端挂了,每次轮到它都要等连接超时(默认 60s),拖慢整体响应。
4.5 503 vs 502 区分
| 状态码 | 原因 | 排查方向 |
|---|---|---|
| 502 Bad Gateway | nginx 连不上后端(后端挂了/端口错了/网络不通) | 看 upstream_addr,ss -tlnp 查后端 |
| 503 Service Unavailable | 被 limit_req 限流拒绝 | 看是否触发 rate+burst,调大 rate 或 burst |
五、生产配置 Checklist
- [ ]
nginx -t通过后必须service nginx reload,且ps -ef | grep nginx确认 worker 时间刷新 - [ ] log_format 中
[$time_local]后必须有空格,否则字段错位 - [ ] 改完 log_format 后清空旧日志或等轮转
- [ ] upstream 配
max_fails+fail_timeout被动健康检查 - [ ] 静态资源
charset utf-8,.md设default_type text/plain - [ ] 反向代理传
Host/X-Real-IP/X-Forwarded-For - [ ] 敏感路径配
auth_basic+ IP 白名单 - [ ] 日志含
$upstream_addr和$request_time(排障必备)
六、踩坑记录
| 坑 | 现象 | 原因 | 修复 |
|---|---|---|---|
| 改了配置不生效 | 负载均衡全打 9000 | 只跑了 nginx -t,没 reload | service nginx reload,看 ps 确认 worker 刷新 |
| log_format 少空格 | awk $9 取到 body_bytes,$7 取到 HTTP/1.1" | [$time_local] 和 "$request" 之间无空格 |
加空格,清空旧日志重测 |
| 新旧日志混合 | 状态码分布出现 3126/70/HTTP/1.1" | 旧格式无 upstream 字段,列号错位 | awk 加 /request_time=/ 过滤,或清空日志 |
| 单后端挂了无 502 | 预期 502 实际全 200 | proxy_next_upstream 默认自动重试下一个后端 | 正常现象;全挂才 502 |
| 快速请求出现 503 | 第 7 次起 503 | limit_req rate=1r/s + burst=5,第 7 次超配额 | 正常现象;调大 rate/burst |
| sites-enabled 垃圾文件 | 目录里有 curl/done 0 字节文件 | 之前误操作重定向创建 | rm 清理 |
评论