NGINX 生产运维手册

面向生产环境的 Nginx 安装、配置、排障、升级与日常运维指南
适用场景:接入层反向代理、负载均衡、SSL 终结、静态资源、四层代理、平滑升级


目录

  1. 架构与核心原理
  2. 信号与进程管理
  3. 命令行参数
  4. 配置文件结构
  5. location 匹配与路径规则
  6. 反向代理与 proxy_pass 路径规则
  7. 正向代理与反向代理
  8. 负载均衡
  9. SSL/TLS 与证书运维
  10. HTTP 跳转 HTTPS 两种场景
  11. 常用模块
  12. 生产环境性能优化
  13. 日志、切割与排障
  14. 平滑升级与动态加模块
  15. 生产值班 SOP
  16. 安全加固清单
  17. 真实 IP 与多层代理
  18. systemd 服务管理
  19. 磁盘、inode 与日志容量巡检
  20. 配置回滚与版本管理
  21. 监控告警与指标解读
  22. upstream 节点摘除与灰度
  23. WebSocket / SSE 长连接
  24. 静态资源、缓存与 map 模块
  25. 日常巡检清单与常见故障
  26. 高可用:Keepalived + VIP
  27. Kubernetes 与 Ingress 运维
  28. 与 WAF / CDN 协作

1. 架构与核心原理

1.1 优点

特性 说明
高并发 单机可支撑数万并发连接(取决于 worker 数、连接数、业务类型)
IO 多路复用 Linux 下默认 epoll,高效处理大量 idle 连接
异步非阻塞 工作进程不阻塞等待单个连接,事件驱动处理请求
多进程模型 主进程负责监听外部控制信号,通过频道机制将相关信号操作传递给工作进程,多个工作进程间通过共享内存来共享数据和信息,默认配置下,工作进程的数量与主机CPU核数相同,充分利用cpu和进程的亲缘性将工作进程与CPU绑定,从而最大限度发挥CPU的处理能力

Master 进程职责:读取配置、绑定端口、创建/管理 Worker、处理信号(reload/upgrade/reopen 等)。
Worker 进程职责:接受连接、处理 HTTP/TCP 请求。Worker 之间不共享连接状态,因此 session 粘性需靠 ip_hash、sticky cookie 或外部存储(Redis 等)。

1.2 Master-Worker 请求处理时序

后端/upstream Worker 进程 Master 进程 客户端 后端/upstream Worker 进程 Master 进程 客户端 启动时读取配置、绑定 80/443 alt [反向代理] [静态文件] keepalive 连接可复用 fork 多个 Worker TCP 连接 + HTTP 请求 解析 location / rewrite 转发请求 响应 sendfile 读取磁盘 HTTP 响应

1.3 reload 平滑重载时序

新 Worker 旧 Worker Master 进程 运维人员 新 Worker 旧 Worker Master 进程 运维人员 全程无端口中断,用户无感知 alt [配置有误] [配置正确] kill -HUP / nginx -s reload 校验新配置语法 拒绝 reload,旧 Worker 继续服务 启动新 Worker(新配置) 发送 QUIT,停止接受新连接 处理完存量请求后退出

1.4 主配置文件模块分类

模块 作用
CoreModule 进程管理、内存管理、配置文件解析、日志
EventsModule 连接事件处理;按平台选择 epoll/kqueue/eventport 等
HttpCoreModule HTTP 核心:server/location、proxy、upstream、gzip 等
StreamModule 四层 TCP/UDP 代理(如 MySQL、Redis、MQTT over TLS)

2. 信号与进程管理

信号又称软中断信号,可以通过调用系统命令 kill 来发送信号实现进程通信,或使用 nginx -s

信号 命令行参数 功能 生产建议
TERM 或 INT stop 快速关闭 Nginx 服务 仅紧急停机;正常下线用 quit
QUIT quit 安全关闭(处理完当前请求后退出) 计划停机、滚动下线前使用
HUP reload 平滑重启,热加载配置文件 改配置标准操作;先 nginx -t
WINCH 从容关闭工作进程 平滑升级中关闭旧 Worker
USR1 reopen 重新创建日志文件,主要用于日志切割 logrotate 切割后必须执行
USR2 平滑更新 Nginx 执行文件 升级 nginx 版本时使用
# 推荐:先检查再 reload
nginx -t && nginx -s reload

# 等价 kill 方式
kill -HUP "$(cat /export/server/nginx/logs/nginx.pid)"

# 日志切割后
kill -USR1 "$(cat /export/server/nginx/logs/nginx.pid)"
# 或
nginx -s reopen

生产注意reload 语法失败时旧配置继续生效;但若新配置语法正确、逻辑有误(如 upstream 指向错误 IP),仍会切到新配置——变更前务必在预发/灰度节点验证


3. 命令行参数

参数 说明
-v nginx 版本信息
-V 版本 + 编译参数 + 已安装模块(加模块/升级前必查)
-t 进行配置文件语法检查,测试配置文件的有效性
-T 语法检查并输出所有有效配置内容(配置文件误删且 nginx 处于运行状态、没有 reload 加载过时,可使用该命令恢复配置)
-q 在测试配置文件的有效性时,不输出非错误信息
-s stop|quit|reopen|reload 发送信号给 nginx 主进程(reopen=重新打开日志文件;reload=加载新配置并优雅关闭旧 worker)
-p 指定 nginx 的执行目录
-c 指定 nginx.conf 文件的位置
-g 外部指定配置文件中的全局指令
nginx -t                          # 1. 语法检查
nginx -s reload                   # 2. 平滑加载
nginx -V 2>&1 | tr ' ' '\n' | grep -i module   # 查看已编译模块

4. 配置文件结构

典型层级:

nginx.conf
├── events { ... }
├── http {
│   ├── upstream backend { ... }
│   ├── log_format / access_log
│   ├── gzip / limit_req / proxy_* 全局默认值
│   └── include conf.d/*.conf;     # 按站点/业务拆分
│       └── server { location { ... } }
└── stream { ... }                 # 四层代理(可选)

生产建议

  • 主配置放全局默认值;业务 server 块放 conf.d/,便于 Git 管理与 Code Review
  • 每个 server 命名规范:域名.conf业务线-环境.conf
  • 变更走 MR + nginx -t + 灰度 reload,避免直接在单机手改

5. location 匹配与路径规则

5.1 前缀与优先级

修饰符 含义
= 精确匹配,优先级最高
^~ URL 以某个常规字符串开头;匹配成功后不再走正则
~ 区分大小写的正则匹配
~* 不区分大小写的正则匹配
!~ / !~* 正则否定(很少用)
/ 通用匹配,任何请求都会匹配到

多个 location 配置时的匹配顺序

  1. 精确匹配 =
  2. 最长前缀匹配;若该前缀带 ^~ 则直接选用,不再正则
  3. 按配置文件出现顺序依次尝试正则 ~ / ~*
  4. 最后是通用匹配 /

5.2 root 与 alias

# root:URI = root路径 + 完整请求路径
location /abc/ {
    root /home/www/nginx;
    index 2.html index.html;
    # 请求 /abc/2.html → /home/www/nginx/abc/2.html
}

# alias:URI = alias路径 + (请求路径 - location前缀)
location /abc/ {
    alias /home/www/nginx/;   # alias 目录结尾建议以 / 结尾
    index 2.html index.html;
    # 请求 /abc/2.html → /home/www/nginx/2.html
}

常见坑location /abcalias /path/ 时,访问 /abc 可能 301 到 /abc/,路径拼接易出错。生产环境推荐统一写成 location /abc/ + alias /path/abc/ 或使用 root

5.3 rewrite 地址重写

URL 重写,把传入 Web 的请求重定向到其他 URL。可用在 serverlocation 中。

标志 行为 适用场景
redirect 302 临时重定向,浏览器地址栏变为新 URL 临时跳转
permanent 301 永久重定向,浏览器会缓存 域名迁移、HTTP→HTTPS
last 重写后重新走一轮 location 匹配(server 级 rewrite 循环) 适合 server 块;location 内慎用
break 重写后停止当前 location 内后续 rewrite,不再重新匹配 适合 location 内单次重写

permanent 添加与不添加的区别

  • 加 permanent:浏览器收到 301,地址栏显示新 URL,F12 可见两次请求
  • 不加 permanent:服务器内部转换,浏览器地址栏不变(内部 rewrite)

生产建议:避免在 if 中嵌套复杂 rewrite;rewrite 链超过 10 次会报 500。优先用 return 301 替代简单跳转。


6. 反向代理与 proxy_pass 路径规则

6.1 核心规则

proxy_pass 的 URL 带 URI 路径(通常以 / 结尾)时,location 匹配到的部分会被替换掉;不带 URI 时,完整请求 URI 原样转发。

location proxy_pass 请求 转发到后端
/api/ http://127.0.0.1:8080/ /api/upload http://127.0.0.1:8080/upload
/api http://127.0.0.1:8080/ /api/upload http://127.0.0.1:8080//upload(双斜杠,需避免)
/api/ http://127.0.0.1:8080 /api/upload http://127.0.0.1:8080/api/upload
/api http://127.0.0.1:8080 /api/upload http://127.0.0.1:8080/api/upload
# 带 /:去掉 location 匹配前缀
location /pss/ {
    proxy_pass http://127.0.0.1:18081/;
    # /pss/bill.html → http://127.0.0.1:18081/bill.html
}

# 不带 /:保留完整路径
location /pss/ {
    proxy_pass http://127.0.0.1:18081;
    # /pss/bill.html → http://127.0.0.1:18081/pss/bill.html
}

6.2 生产推荐 proxy 头与超时

location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Connection        "";

    proxy_connect_timeout  5s;
    proxy_send_timeout     60s;
    proxy_read_timeout     60s;

    proxy_buffering        on;
    proxy_buffer_size      16k;
    proxy_buffers          8 32k;
}

修正说明:原「正代示例」中 proxy_cache_* 等配置实为反向代理场景,已移至第 7 节区分说明。


7. 正向代理与反向代理

7.1 概念对比

正向代理 反向代理
隐藏对象 隐藏客户端 隐藏后端服务器
部署位置 客户端侧 / 内网出口 服务端接入层
典型用途 内网出网、翻墙、统一出口审计 负载均衡、SSL 终结、WAF 前置
Nginx 默认支持 仅 HTTP 正向(HTTPS 需第三方模块) 原生支持

7.2 反向代理示例

http {
    upstream backend {
        server 127.0.0.1:8000;
        keepalive 32;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host       $host;
            proxy_set_header X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

7.3 正向代理配置

背景:192.168.1.191 可以上外网(公网 IP 223.234.56.45),192.168.1.192 不能上外网,使用 Nginx 正向代理让内网主机访问外网。

前提:Nginx 需编译 ngx_http_proxy_connect_module(默认 Nginx 不支持 HTTPS 正向代理 CONNECT 隧道)。

# HTTP 正向代理
server {
    resolver 8.8.8.8 valid=300s;   # 必须指定 DNS,用于解析 $host
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://$host$request_uri;
        proxy_set_header Host $host;
        proxy_connect_timeout 30s;
        proxy_send_timeout    60s;
        proxy_read_timeout    60s;
    }
}

# HTTPS 正向代理(需 proxy_connect 模块)
server {
    resolver 8.8.8.8 valid=300s;
    listen 443;

    proxy_connect;
    proxy_connect_allow           443 563;
    proxy_connect_connect_timeout 10s;
    proxy_connect_read_timeout    10s;
    proxy_connect_send_timeout    10s;

    location / {
        proxy_pass http://$host;
        proxy_set_header Host $host;
    }
}

验证

curl -I http://www.baidu.com/ -v -x 127.0.0.1:80
curl -I https://www.baidu.com/ -v -x 127.0.0.1:443

客户端配置

# 仅 yum 走代理
# /etc/yum.conf
proxy=http://192.168.0.20:80

# 全局代理
export http_proxy=http://192.168.0.20:80
export https_proxy=http://192.168.0.20:443
export no_proxy=localhost,127.0.0.1,10.0.0.0/8

8. 负载均衡

通过 upstream 模块实现。

策略 说明 生产注意
轮询(默认) 依次分配 后端性能不均时考虑 weight
weight 加权轮询 权重高的机器承担更多流量
ip_hash 按客户端 IP 哈希 同一出口 IP 固定到同一后端;NAT 后大量用户同 IP 会失衡
least_conn 最少连接(1.9.1+) 长连接场景比轮询更均衡
hash 通用哈希(如 $request_uri 缓存类业务可让同一 URL 固定后端
fair 按响应时间分配 第三方模块,开源版 Nginx 不包含
url_hash 按 URL 哈希 第三方模块(Tengine 等)
upstream backend {
    least_conn;
    server 10.0.0.1:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 weight=1 max_fails=3 fail_timeout=30s;
    keepalive 64;
}

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

session 问题:ip_hash 只能缓解,生产推荐 Session 外置(Redis)+ 无状态应用,而非依赖 Nginx 粘性。

健康检查

  • 被动健康检查max_fails + fail_timeout(默认机制)
  • 主动健康检查:需 nginx_upstream_check_module(Tengine)或 Nginx Plus / OpenResty

9. SSL/TLS 与证书运维

9.1 证书替换

下载已申请完成的 SSL 证书文件

在这里插入图片描述

注意事项

  • CSR 文件是申请证书时上传或系统在线生成的,安装时可忽略
  • 替换前备份旧证书,确认新证书链完整(fullchain / bundle)

替换步骤

  1. 打开 SSL 证书配置路径
  2. 备份旧证书,上传新证书(如 xxx_bundle.crtxxx.key
  3. 修改 conf 中 ssl_certificatessl_certificate_key 路径
  4. 检查并重载:nginx -t && nginx -s reload

9.2 生产推荐 SSL 配置

server {
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;

    add_header Strict-Transport-Security "max-age=63072000" always;
}

9.3 四层 SSL(stream 模块)

背景:用于 TCP 层反向代理并终结 TLS(如 MQTT over TLS)。

stream {
  upstream stream_backend {
      zone tcp_servers 512k;
      hash $remote_addr consistent;
      server emqx:1883 max_fails=2 fail_timeout=30s;
  }

  server {
      listen 1883 ssl;
      proxy_pass stream_backend;
      proxy_buffer_size 4k;
      ssl_handshake_timeout 15s;
      ssl_certificate     /export/certs/ca.crt;
      ssl_certificate_key /export/certs/ca.key;
  }
}

9.4 证书到期监控

# 检查证书过期时间(提前 30 天告警)
echo | openssl s_client -connect www.example.com:443 -servername www.example.com 2>/dev/null \
  | openssl x509 -noout -dates

10. HTTP 跳转 HTTPS 两种场景

10.1 用户直接访问 Nginx(无上层代理)

在这里插入图片描述

# http server
server {
    listen 80;
    server_name www.example.com;
    return 301 https://$host$request_uri;
}

# https server
server {
    listen 443 ssl http2;
    server_name www.example.com;
    # ... SSL 与业务配置
}

10.2 上层有代理(SLB/CDN/WAF)

在这里插入图片描述

场景说明

  1. 用户不直接访问 Nginx,而是通过上层 Proxy
  2. 实际 HTTPS 终结在上层 Proxy,Nginx 侧通常只收到 HTTP
  3. 此时 $scheme 始终为 http,不能直接用来判断
# 错误:条件恒真,导致无限重定向
server {
    listen 80;
    if ($scheme = "http") {
        return 301 https://$host$request_uri;
    }
}

# 正确:读取上层 Proxy 传递的 Header(名称因厂商而异)
server {
    listen 80;
    # 阿里云 SLB: X-Forwarded-Proto
    # 某些 CDN: X-Forwarded-Scheme / X-Client-Scheme
    if ($http_x_forwarded_proto = "http") {
        return 301 https://$host$request_uri;
    }
}

生产建议:优先在上层 Proxy 做 HTTP→HTTPS 跳转;Nginx 层用 $http_x_forwarded_proto 做二次兜底,并确认 Header 未被客户端伪造(上层 Proxy 应剥离外部传入的同名 Header)。


11. 常用模块

11.1 访问控制

基于用户认证(auth_basic)

auth_basic "Please input password";
auth_basic_user_file /etc/nginx/conf.d/auth_pwd;
yum -y install httpd-tools
htpasswd -c /etc/nginx/conf.d/auth_pwd username

基于 IP 访问控制

location /admin/ {
    allow 10.18.45.65;
    allow 10.18.45.181;
    deny all;
}

11.2 rewrite 模块

server {
    listen 80;
    server_name www.aa.com;
    location /test {
        rewrite ^/test(.*)$ http://www.bb.com/test1$1 permanent;
    }
}

11.3 压缩模块

gzip on;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
gzip_static on;   # 优先使用预压缩的 .gz 文件(需编译 gzip_static 模块)

11.4 防盗链

valid_referers none blocked *.a.com;
if ($invalid_referer) {
    return 403;
}

注意:if 在 Nginx 中有坑,防盗链场景可用,复杂逻辑建议改用 map

11.5 状态监控(stub_status)

location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    allow 10.0.0.0/8;    # 监控网段
    deny all;
}

指标含义

指标 含义
Active connections 当前活跃连接数(含 waiting)
accepts / handled 已接受 / 已处理连接数;两者差值大说明有丢弃
requests 总请求数
Reading / Writing / Waiting 读请求头 / 响应客户端 / 空闲 keepalive

11.6 限流与连接限制

# http 块
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;

server {
    location /api/ {
        limit_req zone=api burst=20 nodelay;
        limit_conn conn 10;
    }
}

12. 生产环境性能优化

12.1 核心参数

worker_processes auto;              # 自动匹配 CPU 核数(推荐)
worker_cpu_affinity auto;           # 1.9.1+ 自动绑核
worker_rlimit_nofile 102400;

events {
    use epoll;                      # Linux 默认即是,可省略
    worker_connections 10240;       # 单 worker 最大连接数
    multi_accept on;                # 一次 accept 多个连接(原注释写反了)
    # accept_mutex off;             # 高并发现代内核通常关闭(默认 off)
}

http {
    sendfile on;
    tcp_nopush on;                  # 配合 sendfile,累计到一定大小再发
    tcp_nodelay on;                 #  keepalive 连接降低延迟
    keepalive_timeout 65;
    keepalive_requests 1000;

    server_tokens off;              # 隐藏版本号
    autoindex off;

    client_max_body_size 100m;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 16k;   # 过大易内存浪费;414 时适当调大

    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 4;              # 官方推荐 1-6,4 为性能与压缩率折中
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
    gzip_vary on;                   # 配合 CDN/缓存,原配置 off 不推荐
    gzip_disable "MSIE [1-6]\.";

    include conf.d/*.conf;
}

12.2 连接数估算

最大并发连接数 ≈ worker_processes × worker_connections

实际上游连接、静态资源、长连接都会占用 worker_connections;需预留 20%~30% 余量。同时检查系统 ulimit -n/proc/sys/fs/file-max

12.3 反向代理超时(按业务调整)

proxy_connect_timeout 5s;     # 建连超时,不宜过长
proxy_send_timeout    60s;
proxy_read_timeout    60s;      # 慢接口可单独在 location 调大

12.4 隐藏版本号

# http 块
server_tokens off;

如需彻底隐藏(Error 页也不显示),需 recompile 或 patch,一般 server_tokens off 即可。


13. 日志、切割与排障

13.1 推荐 access_log 格式

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" '
                'rt=$request_time uct=$upstream_connect_time '
                'uht=$upstream_header_time urt=$upstream_response_time '
                'ups=$upstream_addr';

access_log /export/server/nginx/logs/access.log main;
error_log  /export/server/nginx/logs/error.log warn;

13.2 日志轮转

按大小切割

/export/server/nginx/logs/*.log {
    create 0644 root root
    compress
    delaycompress
    missingok
    dateext
    dateformat -%Y%m%d
    rotate 1000
    notifempty
    size 100M
    postrotate
        if [ -f /export/server/nginx/logs/nginx.pid ]; then
            /export/server/nginx/sbin/nginx -s reopen
        fi
    endscript
}

按时间切割(daily)

/export/server/nginx/logs/*.log {
    create 0644 root root
    daily
    compress
    delaycompress
    missingok
    dateext
    dateformat -%Y%m%d
    rotate 30
    notifempty
    postrotate
        if [ -f /export/server/nginx/logs/nginx.pid ]; then
            /export/server/nginx/sbin/nginx -s reopen
        fi
    endscript
}
/usr/sbin/logrotate -f /etc/logrotate.d/nginx

修正:原 dateformat %Y%m%d%s%s 并非 logrotate 标准占位符,应使用 dateext + dateformat -%Y%m%d

13.3 常见 HTTP 状态码排障

状态码 含义 常见原因 排查方向
499 客户端主动断开 用户取消、前端超时、LB 超时短于后端 $request_time、上游耗时、客户端/LB timeout
502 Bad Gateway 后端未启动、端口不通、upstream 返回非法响应 curl 后端IP:端口、查 error.log connect() failed
503 Service Unavailable 限流触发、upstream 全部 marked down limit_req、后端健康、max_fails
504 Gateway Timeout proxy_read_timeout 到期后端仍未响应 $upstream_response_time、后端慢 SQL/GC
413 Request Entity Too Large 上传超过 client_max_body_size 调大 limit 或走对象存储直传
414 URI Too Long 超过 large_client_header_buffers 检查是否异常长 URL / Cookie

13.4 502/504 排查时序

后端应用 Nginx 用户/监控 后端应用 Nginx 用户/监控 error.log: connect() failed proxy_read_timeout 到期 alt [502 - 连接失败] [504 - 读取超时] 请求 connect() refused / timeout 502 转发请求 处理过慢 504

13.5 常用排障命令

# 配置检查
nginx -t

# 查看连接与 upstream
ss -s
ss -tnp | grep nginx

# 实时 access 日志(按状态码过滤)
tail -f access.log | awk '$9 ~ /^5/'

# 统计 5xx TOP URI
awk '$9 ~ /^5/ {print $7}' access.log | sort | uniq -c | sort -rn | head

# 压测 upstream 连通性
curl -v -H "Host: www.example.com" http://127.0.0.1/backend-health

14. 平滑升级与动态加模块

14.1 二进制平滑升级(USR2)

新 Worker 旧 Worker 新 Master 旧 Master 运维 新 Worker 旧 Worker 新 Master 旧 Master 运维 升级完成,零停机 替换 sbin/nginx 二进制 kill -USR2 fork 新 Master(新二进制) 启动新 Worker kill -WINCH(via pid.oldbin) 优雅关闭旧 Worker 全部退出 自动退出

步骤

  1. 下载新版本,解压,执行旧版 nginx -V 记录全部 configure 参数
  2. 在新源码目录 ./configure <原参数> && make不要 make install
  3. 备份旧二进制:cp sbin/nginx sbin/nginx.bak.$(date +%Y%m%d)
  4. 复制新二进制:cp objs/nginx /export/server/nginx/sbin/nginx
  5. 发送 USR2:kill -USR2 $(cat /export/server/nginx/logs/nginx.pid)
  6. 确认新 Master 已启动:ps aux | grep nginx
  7. 关闭旧 Worker:kill -WINCH $(cat /export/server/nginx/logs/nginx.pid.oldbin)
  8. 验证:/export/server/nginx/sbin/nginx -v

修正:原步骤 8~9(对 oldbin 再发 HUP/QUIT)通常不需要;WINCH 后旧 Master 会在旧 Worker 全部退出后自动结束。

14.2 动态添加模块

背景:已安装且正在使用的 Nginx,经 nginx -V 检查缺失某个模块,需要重新编译增加。

在这里插入图片描述

步骤

  1. nginx -V 记录现有全部 configure 参数
  2. 下载与当前运行版本一致的源码包
  3. 重新 configure:必须带上所有原有参数 + 新增模块参数
  4. make不要 make install
  5. 备份旧二进制:cp sbin/nginx sbin/nginx.bak_20240918
  6. 覆盖二进制:cp objs/nginx /export/server/nginx/sbin/nginx
  7. 平滑升级(USR2 流程)或 nginx -t && nginx -s reload(仅加模块不涉及 ABI 变化时 reload 即可,大版本变更建议 USR2)
  8. nginx -V 确认新模块已存在
# 示例:增加 http_v2 模块(注意拼写 --with-http_v2_module,原 --wiht 为笔误)
./configure --prefix=/export/package/nginx-1.20.1/ \
  --with-http_stub_status_module \
  --with-http_ssl_module \
  --with-http_v2_module \
  # ... 其他原有参数
make

生产注意:原步骤「stop nginx 再覆盖」会造成业务中断;应改用 USR2 平滑升级。


15. 生产值班 SOP

15.1 配置变更 Checklist

步骤 操作
1 确认变更窗口与回滚方案
2 备份:cp -a conf.d/ conf.d.bak.$(date +%Y%m%d%H%M)
3 修改配置
4 nginx -t 语法检查
5 灰度节点先 reload,观察 5~10 分钟
6 全量 reload:nginx -s reload
7 验证:核心业务 curl、监控 5xx 率、error.log

15.2 接到 502/504 告警

  1. 先止损:若单后端故障,upstream 中 down 该节点或 SLB 摘除
  2. 看监控:5xx 比例、是否单节点 / 全集群
  3. 看 Nginxtail -100 error.log、stub_status
  4. 探活后端curl / 端口探测 / 应用日志
  5. 关联变更:最近是否 reload、发版、证书更换
  6. 留证后调整:timeout、keepalive、upstream 参数

15.3 变更「只读 / 慎用」分级

级别 操作 说明
只读安全 nginx -t-V、tail 日志、stub_status 随时可执行
低风险 nginx -s reloadreopen 标准运维操作
慎用 stopquit、USR2 升级 需变更窗口,双人复核
禁止慌乱操作 无备份直接改主配置、生产 kill -9 nginx 可能导致全站不可恢复

16. 安全加固清单

配置
隐藏版本 server_tokens off;
禁止目录遍历 autoindex off;
限制请求体 client_max_body_size 按业务最小化
限流防刷 limit_req / limit_conn
管理端隔离 /nginx_status/admin 仅内网 IP
TLS 禁用 TLSv1.0/1.1,优先 TLS 1.3
Header 安全 X-Frame-OptionsX-Content-Type-Options
敏感路径 禁止访问 .git.env*.sql
location ~ /\.(git|env|ht) {
    deny all;
    return 404;
}

17. 真实 IP 与多层代理

多层代理(CDN → SLB → Nginx → 应用)下,Nginx 默认看到的 $remote_addr上一跳代理 IP,会导致限流、黑白名单、access 日志、审计全部失真。

17.1 转发头设置(Nginx 作为中间层)

proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

17.2 还原客户端真实 IP(Nginx 作为最内层接入)

http {
    # 只信任已知上游网段,切勿写 0.0.0.0/0
    set_real_ip_from 10.0.0.0/8;
    set_real_ip_from 172.16.0.0/12;
    set_real_ip_from 223.234.56.45/32;   # SLB 公网 IP 示例

    real_ip_header    X-Forwarded-For;   # 或 X-Real-IP
    real_ip_recursive on;                # 多级代理时从右向左解析

    # 之后 $remote_addr 即为真实客户端 IP
}

17.3 各层 Header 对照

层级 常见传递方式 Nginx 侧处理
CDN X-Forwarded-For / True-Client-IP set_real_ip_from CDN 回源 IP 段
云 SLB X-Forwarded-For + X-Forwarded-Proto 信任 SLB 内网/专线 IP
PROXY Protocol 四层透传(Stream/部分 LB) listen 443 proxy_protocol; + real_ip_header proxy_protocol;

安全注意set_real_ip_from 必须精确到可信上游;否则攻击者伪造 X-Forwarded-For 可绕过 IP 限制。


18. systemd 服务管理

生产环境推荐用 systemd 托管 Nginx,实现开机自启、异常重启、统一 reload。

18.1 unit 文件示例

# /usr/lib/systemd/system/nginx.service
[Unit]
Description=Nginx HTTP Server
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/export/server/nginx/logs/nginx.pid
ExecStartPre=/export/server/nginx/sbin/nginx -t -q
ExecStart=/export/server/nginx/sbin/nginx
ExecReload=/export/server/nginx/sbin/nginx -s reload
ExecStop=/export/server/nginx/sbin/nginx -s quit
PrivateTmp=true
Restart=on-failure
RestartSec=5s
LimitNOFILE=102400

[Install]
WantedBy=multi-user.target

18.2 常用命令

systemctl daemon-reload          # 修改 unit 后重载
systemctl enable --now nginx     # 开机自启并启动
systemctl status nginx
systemctl reload nginx           # 等价 nginx -s reload(会先走 ExecStartPre -t)
systemctl stop nginx             # 等价 nginx -s quit(优雅停止)
journalctl -u nginx -f           # 查看 systemd 侧日志

18.3 启动失败常见原因

现象 排查
nginx: [emerg] bind() to 0.0.0.0:80 failed 端口被占用:ss -tlnp | grep ':80 '
open() ... failed (13: Permission denied) 非 root 绑 80/443;或目录权限不对
nginx: configuration file ... test failed nginx -t 看具体行号
SELinux 拦截 ausearch -m avc -ts recent;或 setsebool -P httpd_can_network_connect 1
PID 文件 stale 进程已死但 pid 文件在:rm -f nginx.pid 后重启

维护建议:日常变更用 systemctl reload nginx,不要 restart(restart 会短暂中断连接)。


19. 磁盘、inode 与日志容量巡检

Nginx 本身很少占磁盘,日志和临时文件才是维护中的隐形炸弹。

19.1 风险点

路径/对象 风险
access.log / error.log 高 QPS 下单日可达数十 GB
proxy_temp_path 大文件上传/代理缓冲写满磁盘
client_body_temp_path 大 POST 上传占满
logrotate 失败 + nginx -s reopen 未执行 旧 fd 仍写已 rename 的文件,inode 泄漏

19.2 巡检命令

# 磁盘空间(告警阈值建议 80% warn / 90% critical)
df -hT /export/server/nginx/logs

# inode(小文件海量时可能比磁盘先满)
df -i /export/server/nginx/logs

# 日志目录占用 TOP
du -sh /export/server/nginx/logs/* | sort -rh | head

# 当前 nginx 打开哪些日志文件
ls -l /proc/$(cat /export/server/nginx/logs/nginx.pid)/fd | grep log

# 验证 logrotate 最近一次是否成功
grep nginx /var/lib/logrotate/status

19.3 应急处理 SOP

# 1. 确认是磁盘满还是 inode 满
df -h; df -i

# 2. 禁止直接 rm 正在写入的 access.log
# 正确做法:truncate 或 logrotate + reopen
truncate -s 0 /export/server/nginx/logs/access.log   # 仅紧急止血
# 或
/usr/sbin/logrotate -f /etc/logrotate.d/nginx
nginx -s reopen

# 3. 清理已压缩的历史日志(确认 postrotate 已执行后再删)
find /export/server/nginx/logs -name "*.gz" -mtime +30 -delete

19.4 临时目录配置(建议显式指定到大分区)

http {
    client_body_temp_path  /data/nginx/client_body 1 2;
    proxy_temp_path        /data/nginx/proxy       1 2;
    fastcgi_temp_path      /data/nginx/fastcgi     1 2;
}

20. 配置回滚与版本管理

20.1 变更前备份(标准动作)

BACKUP_DIR="/export/backup/nginx/conf.d.bak.$(date +%Y%m%d%H%M)"
mkdir -p "$BACKUP_DIR"
cp -a /export/server/nginx/conf.d/ "$BACKUP_DIR/"
cp -a /export/server/nginx/nginx.conf "$BACKUP_DIR/"
nginx -t   # 改之前也先确认当前配置 OK

20.2 一键回滚

# 回滚到指定备份
BACKUP_DIR="/export/backup/nginx/conf.d.bak.202406241030"
cp -a "$BACKUP_DIR/conf.d/"* /export/server/nginx/conf.d/
cp -a "$BACKUP_DIR/nginx.conf" /export/server/nginx/nginx.conf
nginx -t && nginx -s reload

20.3 Git 管理建议

nginx-config/                 # 独立 Git 仓库
├── nginx.conf
├── conf.d/
│   ├── api.example.com.conf
│   └── static.example.com.conf
└── scripts/
    ├── deploy.sh             # rsync + nginx -t + reload
    └── rollback.sh
实践 说明
一域名一文件 MR Review 时可读、可回滚
CI 跑 nginx -t 合并前语法检查
变更单关联 commit 故障时可快速定位「谁、何时、改了什么」
灰度节点先 deploy 1 台验证 5~10 分钟再全量

21. 监控告警与指标解读

stub_status 适合人工查看;生产告警建议接入 nginx-prometheus-exporternginx-module-vts

21.1 stub_status + exporter

location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}
# nginx-prometheus-exporter 示例
nginx-prometheus-exporter -nginx.scrape-uri http://127.0.0.1/nginx_status

21.2 建议告警项

指标 阈值参考 含义
5xx_rate > 1% 持续 5min 后端或配置异常
nginx_connections_active > worker_connections × workers × 0.8 连接接近上限
accepts - handled 差值持续增大 连接被丢弃(worker 满、内核队列满)
upstream_response_time p99 突增 3 倍 后端变慢
证书 expiry_days < 30 天 避免 HTTPS 中断

21.3 证书到期监控

# 脚本/check_exporter 探活
echo | openssl s_client -connect www.example.com:443 -servername www.example.com 2>/dev/null \
  | openssl x509 -noout -enddate

可与项目内 Prometheus笔记.md 的 blackbox_exporter、Alertmanager 路由配合使用。


22. upstream 节点摘除与灰度

22.1 临时摘除故障节点

upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080 down;      # 立即摘除,reload 生效
    server 10.0.0.3:8080 backup;    # 仅主节点全 down 时启用
}
nginx -t && nginx -s reload    # down 标记需 reload,非热更新字段

22.2 灰度放量

upstream backend {
    server 10.0.0.1:8080 weight=9;   # 旧版本 90%
    server 10.0.0.2:8080 weight=1;   # 新版本 10%
}

22.3 维护窗口 SOP

新节点 旧节点 Nginx 运维 新节点 旧节点 Nginx 运维 存量连接处理完,无新流量 逐步放大 weight upstream 中标记 Old 为 down nginx -t && reload 部署/重启/维护 取消 down,调整 weight 灰度 reload

注意down 只阻止新连接分配;长连接/keepalive 需等超时或配合后端 graceful shutdown。


23. WebSocket / SSE 长连接

默认 proxy_read_timeout 60s 会导致 WebSocket、SSE 被 Nginx 主动断开

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

location /sse/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection '';
    proxy_buffering off;           # SSE 必须关缓冲
    proxy_cache off;
    proxy_read_timeout 86400s;
    chunked_transfer_encoding off;
}

排障要点

  • 504 + $upstream_response_time 接近 timeout → 调大 proxy_read_timeout
  • 连接数暴涨 → 检查 worker_connections 是否够用(每个 WS 占 1 连接)
  • 502 + upstream prematurely closed → 后端重启或超时关连接

24. 静态资源、缓存与 map 模块

24.1 静态资源与 SPA

location / {
    root /var/www/html;
    try_files $uri $uri/ /index.html;   # Vue/React SPA 路由
    expires 7d;
    add_header Cache-Control "public, immutable";
}

location ~* \.(js|css|png|jpg|gif|ico|woff2)$ {
    expires 30d;
    access_log off;
}

24.2 open_file_cache(高 QPS 静态站)

open_file_cache          max=10000 inactive=60s;
open_file_cache_valid    80s;
open_file_cache_min_uses 2;
open_file_cache_errors   on;

24.3 map 替代复杂 if

# 按 User-Agent 分流
map $http_user_agent $backend {
    default        desktop;
    ~*Mobile       mobile;
    ~*Android      mobile;
}

upstream desktop { server 10.0.0.1:8080; }
upstream mobile  { server 10.0.0.2:8080; }

server {
    location / {
        proxy_pass http://$backend;
    }
}

# 维护页开关(运维常用)
map $remote_addr $maintenance {
    default     1;
    10.0.0.0/8  0;    # 内网跳过维护页
    127.0.0.1   0;
}

24.4 resolver(域名 upstream / 动态后端)

resolver 10.0.0.2 valid=30s ipv6=off;

location / {
    set $upstream_api api.internal.example.com;
    proxy_pass http://$upstream_api:8080;
}

valid 过大会导致 DNS 变更后切换慢;K8s Service 域名建议 valid=10s~30s


25. 日常巡检清单与常见故障

25.1 日检 / 周检清单

频率 检查项 命令/方法
每日 进程存活 systemctl is-active nginx
每日 5xx 率 监控面板 / awk '$9~/^5/' access.log | wc -l
每日 磁盘/inode df -h; df -i
每日 证书有效期 openssl / 监控告警
每周 配置漂移 nginx -T 与 Git 对比
每周 error.log 新 emerg/alert grep emerg error.log
每周 worker 数是否正常 ps aux | grep nginx | wc -l
每月 内核/连接数余量 ss -s、stub_status
每月 logrotate 是否生效 /var/lib/logrotate/status

25.2 常见故障速查

现象 可能原因 处理
全站 502 后端全挂 / upstream 配错 curl upstream、查 error.log
间歇 502 单后端故障 / keepalive 到 dead socket 摘节点、proxy_next_upstream
全站 504 后端集体慢 / timeout 过短 $upstream_response_time、调 timeout
reload 后部分 404 location/proxy_pass 路径变更 对比 Git diff、nginx -T
磁盘满 日志未切割 logrotate + reopen + 清理 .gz
连接数打满 worker_connections 不足 / CC 调参、限流、上游 WAF
改配置不生效 改错文件 / 未 reload nginx -T | grep 确认生效配置
502 仅 POST 大文件 client_max_body_size 太小 调大 limit 或走 OSS 直传

25.3 维护权限建议

# 推荐:专用低权用户运行 worker
user nginx nginx;

# 配置目录:root 拥有,nginx 只读
chown -R root:root /export/server/nginx/conf.d
chmod 644 /export/server/nginx/conf.d/*.conf

# 日志目录:nginx 可写
chown -R nginx:nginx /export/server/nginx/logs

26. 高可用:Keepalived + VIP

裸机 / 虚拟机部署 Nginx 时,常用 Keepalived + VIP(虚拟 IP) 实现主备切换,避免单点故障。

26.1 架构示意

后端应用 Backup Nginx Master Nginx VIP 10.0.0.100 用户 后端应用 Backup Nginx Master Nginx VIP 10.0.0.100 用户 Keepalived MASTER 持有 VIP Master 故障 / nginx 进程挂 请求 流量转发 反向代理 VIP 漂移 Backup 升 MASTER 继续服务 用户无感知(TCP 层可能断连重试)

典型拓扑:

                    VIP 10.0.0.100
                         │
            ┌────────────┴────────────┐
            │                         │
     Nginx-A (MASTER)          Nginx-B (BACKUP)
     10.0.0.11                 10.0.0.12
     Keepalived priority 100   Keepalived priority 90
            │                         │
            └────────────┬────────────┘
                         │
                    后端 upstream

26.2 Keepalived 配置示例

Master(10.0.0.11)

# /etc/keepalived/keepalived.conf
global_defs {
    router_id NGINX_HA_01
    script_user root
    enable_script_security
}

# Nginx 健康检查:进程不存在则降优先级,触发 VIP 漂移
vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight -30
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass NginxHa2024!
    }

    virtual_ipaddress {
        10.0.0.100/24 dev eth0
    }

    track_script {
        check_nginx
    }

    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}

Backup(10.0.0.12):同上,仅改 state BACKUPpriority 90router_id NGINX_HA_02

健康检查脚本

#!/bin/bash
# /etc/keepalived/check_nginx.sh
# 同时检查进程 + 本地 HTTP 探活(比只检查 pid 更可靠)
if ! pgrep -x nginx > /dev/null; then
    exit 1
fi
curl -sf -o /dev/null -H "Host: health.internal" http://127.0.0.1/nginx_status || exit 1
exit 0
chmod +x /etc/keepalived/check_nginx.sh
systemctl enable --now keepalived

26.3 双机 Nginx 配置要点

要求
配置文件 两台完全一致(Git 同步 / Ansible 下发)
证书 同步到相同路径,同时更新
日志 各自写本地;或挂载共享存储 / 日志采集 Sidecar
upstream 指向相同后端池
pid / logs 本地路径,不共享
# 变更时:先改 Git → 两台同步 → 两台分别 nginx -t → 两台 reload
ansible nginx_ha -m synchronize -a "src=conf.d/ dest=/export/server/nginx/conf.d/"
ansible nginx_ha -a "nginx -t && nginx -s reload"

26.4 切换与脑裂

正常切换流程

  1. Master 上 Nginx 挂掉或 check 脚本失败
  2. Keepalived 降低 Master 优先级
  3. Backup 抢占 VIP(发送 gratuitous ARP)
  4. 流量切到 Backup

脑裂(Split-Brain):两台同时持有 VIP → 部分用户访问 A、部分访问 B,配置不一致时出故障。

原因 预防
网络分区 多播/单播 VRRP 心跳网络独立;unicast_peer 替代 multicast
防火墙拦截 VRRP 放行协议 112 或单播 198.18/198.19
check 脚本误判 fall/rise 防抖;探活 URL 独立于业务
两台都 MASTER 确保 virtual_router_idauth_pass 一致且唯一
# 单播模式(云环境推荐,部分云不支持组播)
unicast_src_ip 10.0.0.11
unicast_peer {
    10.0.0.12
}

26.5 维护 SOP

场景 操作
升级 Master Nginx systemctl stop keepalived 让 VIP 漂到 Backup → 升级 Master → 启动 keepalived
升级 Keepalived 配置 先改 Backup 并 reload → 再改 Master
演练切换 Backup 上 systemctl restart keepalived 观察 VIP;确认告警触发
紧急回切 Master 恢复后自动抢回(priority 高);或手动 systemctl restart keepalived
# 查看当前 VIP 在哪台
ip addr show eth0 | grep 10.0.0.100

# 查看 Keepalived 状态
systemctl status keepalived
journalctl -u keepalived -f

与云 SLB 对比:云环境更推荐直接用 SLB/ALB 做四层/七层负载 + 健康检查,Keepalived 适合私有云、IDC、无云 LB 的场景。


27. Kubernetes 与 Ingress 运维

K8s 中 Nginx 常以 Ingress Controller 形式运行(如 ingress-nginx),与裸机 Nginx 运维思路相通,但配置载体、发布方式、排障路径不同。

27.1 架构对比

业务 Pod K8s Service Ingress Controller Pod 云 LB / NodePort 用户 业务 Pod K8s Service Ingress Controller Pod 云 LB / NodePort 用户 HTTPS 请求 转发到 Ingress Controller 匹配 Ingress 规则 转发到 Service ClusterIP kube-proxy / IPVS 负载 响应
对比项 裸机 Nginx K8s Ingress-Nginx
配置载体 nginx.conf / conf.d/*.conf Ingress / ConfigMap 资源
生效方式 nginx -t && reload Controller watch API 自动 reload
后端发现 静态 IP / 域名 Service Endpoints 动态更新
证书 文件挂载 Secret (tls) 或 cert-manager
日志位置 /var/log/nginx/ Pod stdout / 挂载 volume
扩缩容 手动 / Keepalived Deployment replicas + HPA

27.2 Ingress 资源示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "120"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-example-com-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080

27.3 常用 Ingress-Nginx Annotations

Annotation 作用 运维场景
proxy-body-size 上传大小限制 413 报错
proxy-read-timeout 读超时 504 / 慢接口
proxy-send-timeout 写超时 大文件上传
ssl-redirect HTTP→HTTPS 与 CDN 协作时注意
use-forwarded-headers 信任 X-Forwarded-* 前面有 CDN/LB 时必开
compute-full-forwarded-for 追加 X-Forwarded-For 真实 IP 链路
limit-rps / limit-connections 限流 防刷
configuration-snippet 注入 location 块 慎用,易冲突
canary / canary-weight 金丝雀发布 灰度流量

全局 ConfigMap(ingress-nginx-controller)

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  use-forwarded-headers: "true"
  compute-full-forwarded-for: "true"
  use-proxy-protocol: "false"
  log-format-upstream: '$remote_addr - $host [$time_local] "$request" $status $body_bytes_sent "$http_referer" rt=$request_time urt=$upstream_response_time ups=$upstream_addr'
  proxy-body-size: "50m"

27.4 运维操作

# 查看 Ingress 及后端
kubectl get ingress -A
kubectl describe ingress api-ingress -n production

# 查看 Controller Pod
kubectl get pod -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

# 进入 Controller 看实际 nginx 配置(排障神器)
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller -- cat /etc/nginx/nginx.conf
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller -- nginx -T 2>/dev/null | less

# 查看 Controller 日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx -f --tail=200

# 验证后端 Endpoints 是否正常
kubectl get endpoints api-service -n production

27.5 常见故障

现象 排查
404 / default backend Ingress host/path 不匹配;pathType 错误
502 Service 无 Endpoints(Pod 未 Ready);Pod 端口与 targetPort 不一致
504 proxy-read-timeout 过短;Pod 资源不足
证书错误 Secret 过期;cert-manager 续签失败 kubectl describe certificate
改 Ingress 不生效 Controller 未 watch 到;看 Controller 日志有无 reload 错误
仅部分节点 502 Node 网络 / Calico 策略;Endpoints 分布不均

27.6 发布与回滚

# 变更前导出当前配置
kubectl get ingress api-ingress -n production -o yaml > ingress.bak.yaml

# 应用变更
kubectl apply -f ingress.yaml

# 回滚
kubectl apply -f ingress.bak.yaml
# 或
kubectl rollout undo deployment/ingress-nginx-controller -n ingress-nginx

27.7 监控(与 Prometheus 联动)

ingress-nginx 默认暴露 :10254/metrics,Prometheus 抓取 job 示例:

# ServiceMonitor 或 static_configs
- job_name: ingress-nginx
  static_configs:
    - targets: ['ingress-nginx-controller-metrics:10254']

关键指标:nginx_ingress_controller_requests(按 status/class/host)、nginx_ingress_controller_nginx_process_connections


28. 与 WAF / CDN 协作

生产链路常见:用户 → CDN → WAF → SLB → Nginx → 应用。Nginx 作为源站接入层,需与上层正确协作。

28.1 全链路架构

应用 源站 Nginx 云 SLB WAF CDN 边缘节点 用户 应用 源站 Nginx 云 SLB WAF CDN 边缘节点 用户 alt [拦截] [放行] alt [缓存命中] [缓存未命中 / 动态] 静态资源 / 动态回源 直接返回 回源请求 SQL注入/XSS/CC 检测 403/挑战页 转发 HTTP + X-Forwarded-* 反向代理 响应(经原路或 CDN 缓存)

28.2 职责划分

层级 典型职责 Nginx 是否应重复做
CDN 静态加速、边缘缓存、DDoS 基础防护 静态 expires 可配合,不必重复缓存
WAF Web 攻击防护、Bot 管理、CC 高级防护 基础 limit_req 作兜底,不替代 WAF
SLB 四层/七层负载、健康检查、SSL 终结(可选) upstream 健康检查与之互补
Nginx 路由、限流兜底、Header 治理、应用反代 源站最后一道接入

28.3 源站只接受 CDN/WAF 回源 IP(安全基线)

禁止公网直连源站,仅允许 CDN/WAF 回源 IP 段访问 80/443:

# 在 http 或 server 块最前面
# 阿里云 CDN 回源 IP 段从控制台导出;以下为示例
geo $is_trusted_source {
    default          0;
    10.0.0.0/8       1;    # 内网 SLB
    223.5.5.0/24     1;    # CDN 回源 IP 段(示例,以厂商文档为准)
    47.93.0.0/16     1;    # WAF 回源 IP 段(示例)
}

server {
    listen 443 ssl;
    if ($is_trusted_source = 0) {
        return 403;
    }
    # ... 业务配置
}

或使用防火墙(更推荐,在 Nginx 之前拦截):

# iptables:仅允许 CDN 回源 IP + 内网 SLB
iptables -A INPUT -p tcp --dport 443 -s 223.5.5.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j DROP

CDN/WAF 回源 IP 会变更,需订阅厂商公告或 API 同步,定期更新白名单

28.4 真实 IP 传递(与第 17 章联动)

厂商 回源 Header Nginx 配置
阿里云 CDN Ali-CDN-Real-IP / X-Forwarded-For real_ip_header X-Forwarded-For
腾讯云 CDN X-Forwarded-For 同上
Cloudflare CF-Connecting-IP real_ip_header CF-Connecting-IP
通用 WAF X-Forwarded-For set_real_ip_from WAF 回源 IP 段
# Cloudflare 示例
set_real_ip_from 173.245.48.0/20;
# ... 其他 CF IP 段见官方文档
real_ip_header CF-Connecting-IP;

28.5 HTTPS 终结层级

模式 用户侧 CDN→源站 Nginx 侧
全链路 HTTPS HTTPS HTTPS listen 443 ssl
CDN 终结(常见) HTTPS HTTP listen 80 + 信任 X-Forwarded-Proto
双重 HTTPS HTTPS HTTPS 源站也配 SSL(更安全,略增源站负载)
# CDN 终结 SSL 时,源站 Nginx 判断跳转
map $http_x_forwarded_proto $redirect_to_https {
    default "";
    "http"  "https://$host$request_uri";
}
# 通常 CDN 已做跳转,源站无需再 301

28.6 缓存协作

CDN 缓存静态,源站 Nginx 职责

# 静态:长缓存 + 版本号/hash 文件名
location /static/ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000";
    add_header X-Cache-Status "ORIGIN";   # 便于排查
}

# 动态 API:禁止 CDN 缓存
location /api/ {
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    add_header Pragma "no-cache";
    proxy_pass http://backend;
}

CDN 刷新 / 预热

操作 场景 说明
URL 刷新 单个页面更新 CDN 控制台/API
目录刷新 整站静态发版 刷新 /static/
预热 大促前 提前灌入热点 URL
Cache-Control 变更 发版后仍命中旧缓存 检查 CDN 是否「忽略源站 Cache-Control」

28.7 WAF 协作与误拦排查

WAF 拦截后用户看到 403/405/验证码,源站 Nginx 无 access 日志 → 问题在 WAF/CDN 层,不在 Nginx。

排查步骤:

  1. CDN/WAF 控制台查拦截日志(规则 ID、特征)
  2. 对比正常请求与拦截请求的 Header/Body/URI
  3. 临时加白(指定 URL/IP/规则 ID)验证
  4. 调整规则灵敏度或排除业务接口(如大 POST、特殊 Content-Type)

Nginx 侧避免误触发 WAF

# 避免异常大的 Header(WAF 可能判为攻击)
large_client_header_buffers 4 16k;

# 避免 URI 中异常字符(按业务需要)
# 若 WAF 已拦,源站不必重复

28.8 协作运维 Checklist

变更类型 CDN WAF Nginx 源站
新域名上线 添加加速域名、回源 Host 接入防护、绑定域名 server_name + 证书
SSL 证书更新 CDN 侧证书(用户可见) 源站证书(回源 HTTPS 时)
源站 IP 变更 改回源地址 改回源地址 更新防火墙白名单
发版静态资源 刷新/预热 CDN 确认 Cache-Control
502 排障 查 CDN 回源失败日志 查是否误拦 查 upstream/error.log
大促保障 预热 + 放宽带宽 调高 CC 阈值 扩容 + limit_req 兜底

28.9 典型故障

现象 可能层级 处理
全国用户 403,源站无日志 WAF/CDN 拦截 WAF 白名单/规则调整
部分地区慢 CDN 节点 CDN 预热、切换线路
源站 IP 暴露被 CC 安全 防火墙仅允许回源 IP;隐藏 DNS 直连
HTTPS 混合内容 CDN/源站配置 检查 X-Forwarded-Proto、资源 URL 协议
刷新 CDN 仍旧内容 缓存 Key 检查是否按 Query/Header 缓存;Purge 是否成功
仅 CDN 用户 502 回源链路 CDN 回源 Host/端口/协议是否与 Nginx 一致

附录:原文优化对照摘要

原内容 修正/补充
-s reopen 写为「重新打开配置文件」 应为「重新打开日志文件」
正向代理示例用了 upstream + proxy_cache 实为反向代理,已分节重写
multi_accept off 注释为「批量建立连接」 应为 multi_accept on 才批量 accept
fair / url_hash 作为内置策略 标注为第三方模块,开源版不含
平滑升级步骤 8~9 对 oldbin 发 HUP/QUIT 通常只需 USR2 + WINCH
加模块前 nginx stop 改为 USR2 平滑升级,避免中断
logrotate dateformat %Y%m%d%s 修正为 -%Y%m%d
HTTP→HTTPS 仅用 $scheme 上层代理场景需读 X-Forwarded-Proto 等 Header
缺少 502/504/499 排障、限流、日志格式、SOP 已补充完整生产章节
缺少 real_ip、systemd、磁盘巡检、回滚、监控 已补充第 17~25 章
缺少高可用、K8s Ingress、WAF/CDN 协作 已补充第 26~28 章

| 预热 | 大促前 | 提前灌入热点 URL |
| Cache-Control 变更 | 发版后仍命中旧缓存 | 检查 CDN 是否「忽略源站 Cache-Control」 |

28.7 WAF 协作与误拦排查

WAF 拦截后用户看到 403/405/验证码,源站 Nginx 无 access 日志 → 问题在 WAF/CDN 层,不在 Nginx。

排查步骤:

  1. CDN/WAF 控制台查拦截日志(规则 ID、特征)
  2. 对比正常请求与拦截请求的 Header/Body/URI
  3. 临时加白(指定 URL/IP/规则 ID)验证
  4. 调整规则灵敏度或排除业务接口(如大 POST、特殊 Content-Type)

Nginx 侧避免误触发 WAF

# 避免异常大的 Header(WAF 可能判为攻击)
large_client_header_buffers 4 16k;

# 避免 URI 中异常字符(按业务需要)
# 若 WAF 已拦,源站不必重复

28.8 协作运维 Checklist

变更类型 CDN WAF Nginx 源站
新域名上线 添加加速域名、回源 Host 接入防护、绑定域名 server_name + 证书
SSL 证书更新 CDN 侧证书(用户可见) 源站证书(回源 HTTPS 时)
源站 IP 变更 改回源地址 改回源地址 更新防火墙白名单
发版静态资源 刷新/预热 CDN 确认 Cache-Control
502 排障 查 CDN 回源失败日志 查是否误拦 查 upstream/error.log
大促保障 预热 + 放宽带宽 调高 CC 阈值 扩容 + limit_req 兜底

28.9 典型故障

现象 可能层级 处理
全国用户 403,源站无日志 WAF/CDN 拦截 WAF 白名单/规则调整
部分地区慢 CDN 节点 CDN 预热、切换线路
源站 IP 暴露被 CC 安全 防火墙仅允许回源 IP;隐藏 DNS 直连
HTTPS 混合内容 CDN/源站配置 检查 X-Forwarded-Proto、资源 URL 协议
刷新 CDN 仍旧内容 缓存 Key 检查是否按 Query/Header 缓存;Purge 是否成功
仅 CDN 用户 502 回源链路 CDN 回源 Host/端口/协议是否与 Nginx 一致

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐