This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/DEV/Nginx/Nginx.md
T
2026-05-18 21:09:55 +08:00

1006 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [tech, infrastructure, web-server, proxy]
create time: 2026-05-18 20:30
---
# Nginx
## 概述
Nginx(读作 "engine-x")是一个高性能的 **HTTP 服务器**和**反向代理服务器**,同时也支持 IMAP/POP3/SMTP 协议。由 Igor Sysoev 开发,以其高并发、低内存消耗闻名。
> [!QUESTION] 为什么选择 Nginx?
> - **异步事件驱动架构**:能轻松支撑数万并发连接
> - **轻量级**:处理静态文件只需 Apache 1/5 的内存
> - **热部署**:配置文件修改后可无缝 reload,不中断现有请求
> - **生态成熟**:大量第三方模块和社区方案
核心用途:**反向代理**、**负载均衡**、**静态资源服务**、**SSL/TLS 终止**、**API 网关**。
## 安装
### 主流方式
| 方式 | 适用场景 | 命令示例 |
|------|---------|---------|
| 系统包管理器 | 快速部署、稳定优先 | `sudo apt install nginx` / `sudo yum install nginx` |
| 官方仓库 | 需要最新版本 | 添加 `nginx.org` 源后安装 |
| Docker | 容器化部署 | `docker run -d --name nginx -p 80:80 nginx:alpine` |
| 源码编译 | 需要自定义模块 | `./configure --with-http_ssl_module && make && sudo make install` |
> [!TIP] 生产环境推荐
> 使用官方仓库或 Docker Alpine 镜像,体积小且更新及时。开发环境直接用系统包管理器即可。
### 验证安装
```bash
nginx -v # 查看版本
nginx -t # 测试配置语法
systemctl status nginx # 查看服务状态
```
## 核心概念
### 架构模型
```mermaid
graph LR
Master["Master Process 管理进程"] --> WorkerA["Worker Process 1 处理请求"]
Master --> WorkerB["Worker Process 2 处理请求"]
Master --> WorkerC["Worker Process 3 处理请求"]
WorkerA --> Conn1["Connection A"]
WorkerA --> Conn2["Connection B"]
WorkerB --> Conn3["Connection C"]
WorkerB --> Conn4["Connection D"]
WorkerC --> Conn5["Connection E"]
style Master fill:#f9d,stroke:#333
style WorkerA fill:#ccf,stroke:#333
style WorkerB fill:#ccf,stroke:#333
style WorkerC fill:#ccf,stroke:#333
```
Nginx 采用 **Master-Worker 多进程模型**:
- **Master 进程**:负责读取配置、管理 Worker、平滑重载
- **Worker 进程**:每个 Worker 独立运行,用**事件驱动**方式处理请求。一个请求不会在其他 Worker 中排队
> [!NOTE] 关键问题
> 为什么不用多线程?因为线程切换开销大、锁竞争严重。Nginx 的事件驱动模型(epoll/kqueue)在 Linux/BSD 上能做到单个 Worker 处理上万并发连接,这就是著名的 **"C10K 问题"**解决方案之一。
### 配置文件结构
主配置文件通常是 `/etc/nginx/nginx.conf`,采用**嵌套块结构**:
```nginx
# 全局块
worker_processes auto; # Worker 数量(auto = CPU 核数)
error_log /var/log/nginx/error.log warn;
events { # events 块
worker_connections 1024; # 每个 Worker 的最大连接数
}
http { # http 块
include mime.types;
default_type application/octet-stream;
sendfile on;
# upstream 块 — 定义后端服务器池
upstream backend {
server 127.0.0.1:8080;
}
server { # server 块 — 虚拟主机
listen 80;
server_name example.com;
location / { # location 块 — URL 匹配路由
proxy_pass http://backend;
}
}
}
```
配置文件加载顺序:**全局块 → events 块 → http 块 → server 块 → location 块**。外层指令对内层生效(继承关系)。
## 常用配置实战
### 1. 反向代理
最经典的用法——将客户端请求转发到后端服务。
```nginx
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
# 必须传递的信息头
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# ── Proxy Buffering(缓冲控制)──
proxy_buffering on; # 默认开启,先接收后端完整响应再发给客户端
proxy_buffer_size 4k; # 第一块缓冲区大小(通常存响应头)
proxy_buffers 8 4k; # 后续缓冲区数量和大小
proxy_busy_buffers_size 8k; # 忙缓冲区(一边发客户端一边收后端)
# proxy_buffering off; # 关闭缓冲 = 流式转发,适合 SSE/WebSocket
}
}
```
> [!NOTE] 反向代理 vs 正向代理
> - **正向代理**:代理**客户端**,帮内网用户访问外网(如 VPN)
> - **反向代理**:代理**服务端**,对外隐藏后端真实地址(Nginx 的主要角色)
> [!NOTE] Proxy Buffering 工作原理
> ```mermaid
> sequenceDiagram
> participant Client as 客户端
> participant Nginx as Nginx (Buffer)
> participant Backend as 后端服务
> Client->>Nginx: 请求
> Nginx->>Backend: 转发请求
> Backend-->>Nginx: 响应数据写入 Buffer
> Note over Nginx: 整个响应写完后...
> Nginx-->>Client: 从 Buffer 发送给客户端
> alt buffering off
> Nginx-->>Client: 实时转发 (Stream)
> end
> ```
> - **开启 buffering**(默认):后端可以先完成处理再发送,Nginx 承担内存压力,提升吞吐
> - **关闭 buffering**:实时转发,内存占用低但要求后端尽快返回;适合 SSE、大文件下载
#### Proxy Cache — 反向缓存
Nginx 还可以作为**反向缓存代理**,将后端的响应缓存在本地磁盘。
```nginx
http {
# 定义缓存区域
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m
max_size=1g inactive=60m use_temp_path=off;
server {
location /api/ {
proxy_pass http://backend;
# 启用缓存
proxy_cache app_cache; # 使用哪个缓存区
proxy_cache_key "$scheme$request_method$host$request_uri"; # 缓存 key
proxy_cache_valid 200 302 10m; # 200/302 响应缓存 10 分钟
proxy_cache_valid 404 1m; # 404 缓存 1 分钟
proxy_cache_valid any 5m; # 其他状态码缓存 5 分钟
# 缓存命中时添加头部(方便调试)
add_header X-Cache-Status $upstream_cache_status;
# 绕过缓存的条件(未登录用户不走缓存)
proxy_cache_bypass $http_authorization;
proxy_no_cache $http_authorization;
# 过期后仍向客户端提供旧缓存(stale),后台异步刷新
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_revalidate on;
}
}
}
```
`X-Cache-Status` 返回值说明:
| 值 | 含义 |
|------|------|
| `HIT` | 缓存命中,直接返回 |
| `MISS` | 缓存未命中,回源获取并缓存 |
| `EXPIRED` | 缓存过期,向后端重新验证 |
| `UPDATING` | 后台正在更新缓存,返回旧版本 |
| `BYPASS` | 被配置主动跳过 |
### 2. 负载均衡
当后端有多台服务器时,Nginx 可以将流量分发到不同节点。
```nginx
upstream frontend {
# 三种常用策略:
# 轮询(默认)— 按顺序依次分配
# server 10.0.0.1:8080;
# server 10.0.0.2:8080;
# 权重 — 数值越大分到的请求越多
server 10.0.0.1:8080 weight=3;
server 10.0.0.2:8080 weight=1;
# IP Hash — 同一客户端 IP 固定分配到同一台后端
# ip_hash;
# 最少连接 — 分配到当前活跃连接最少的节点
# least_conn;
}
server {
listen 80;
location / {
proxy_pass http://frontend;
}
}
```
常见策略对比:
| 策略 | 适用场景 | 注意 |
|------|---------|------|
| **轮询 (round-robin)** | 通用场景 | 各节点性能相近时效果最好 |
| **加权轮询 (weight)** | 节点性能差异大 | 可根据硬件能力分配权重 |
| **IP Hash** | 有 Session 粘性需求 | Cookie 登录可能失效 |
| **最少连接 (least_conn)** | 请求处理时间不均 | 长连接场景需留意偏差 |
### 3. 健康检查与后端管理
Nginx 社区版提供**被动式**健康检查(通过 fail_timeout 和 max_fails 参数),商业版(Plus)支持主动探测。
```nginx
upstream backend {
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;
# 标记为 down — 不参与任何流量(常用于灰度发布逐步放量)
# server 10.0.0.3:8080 down;
# max_conns — 限制分配到该节点的并发数,超限时返回错误
# server 10.0.0.4:8080 max_conns=100;
# keepalive — 保持与后端的长连接,避免频繁建连开销
keepalive 32; # 每个 Worker 最多保持的空闲连接数
keepalive_timeout 60s;
keepalive_requests 100;
}
```
> [!NOTE] passive health check 机制
> `max_fails=3 fail_timeout=30s` 含义:**在 30 秒内,某个后端失败超过 3 次,则该后端在 30 秒内被剔除**。失败次数计数是从后端恢复后重新累加的。
使用 `keepalive` 时需要在 proxy 层做配套设置:
```nginx
http {
upstream backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
location / {
proxy_pass http://backend;
# 关键:使用 HTTP/1.1 + Connection 头告诉 Nginx 复用连接
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
```
> [!QUESTION] 为什么需要 `proxy_set_header Connection ""`?
>
> HTTP/1.0 默认关闭 keepalive,如果后端期望长连接,客户端(Nginx)发送的 `Connection: close` 会导致连接立即关闭,使 `keepalive` 配置形同虚设。清空该头部即可让 TCP 连接持续复用。
### 4. 静态文件服务
利用 Nginx 处理静态资源的优势,大幅减轻后端压力。
```nginx
server {
listen 80;
server_name cdn.example.com;
# 根目录指向
root /var/www/html;
index index.html index.htm;
# 精确匹配
location /assets/ {
expires 30d; # 浏览器缓存 30 天
add_header Cache-Control "public, immutable";
gzip_static on; # 提供 .gz 预压缩文件
}
# 错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
}
```
> [!TIP] 静态文件优化要点
> - **gzip_static on**:如果预先生成了 `.gz` 文件,直接发送,节省 CPU
> - **sendfile on**:启用内核零拷贝,静态文件传输效率极高
> - **expires**:合理设置缓存头部,减少重复请求
### 5. HTTPS / SSL
```nginx
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# 推荐的现代 TLS 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
# HTTP → HTTPS 自动跳转
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
```
> [!NOTE] Let's Encrypt 免费证书
> 用 Certbot 可以一键申请和续期免费证书:
> ```bash
> sudo certbot --nginx -d example.com
> ```
> Certbot 会自动帮你写入 Nginx 配置并完成 SSL 设置。
### 6. 读写分离 & 限流
```nginx
# 请求速率限制
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 连接数限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay; # 突发 20 个请求
limit_conn conn_limit 10; # 每个 IP 最多 10 连接
proxy_pass http://backend;
}
}
```
### 7. IP 访问控制
通过 `allow` / `deny` 指令实现基于客户端 IP 的白名单和黑名单。
```nginx
# ── 场景 1:管理后台仅允许内网访问 ──
location /admin/ {
allow 10.0.0.0/8; # 允许内网段
allow 192.168.1.0/24; # 允许办公网
deny all; # 拒绝其余所有 IP
proxy_pass http://admin-backend;
}
# ── 场景 2:特定接口封禁恶意 IP ──
map $remote_addr $blocked_ip {
default 0;
192.168.1.100 1; # 已知攻击者
10.99.0.0/16 1; # 整个 C 段封禁
}
server {
location / {
if ($blocked_ip) {
return 403; # 直接返回,不转发到后端
}
proxy_pass http://backend;
}
}
```
> [!TIP] 推荐做法
> `allow/deny` 是在 Nginx 层面直接拦截(return 403),比让请求到达后端更安全高效。对于大名单封禁场景,使用 `geo` / `map` 模块配合数组匹配更灵活。
### 8. HTTP Basic Auth 认证
为特定接口添加用户名密码登录。
```bash
# 生成密码文件(首次)
sudo htpasswd -c /etc/nginx/.htpasswd admin
# 后续添加用户去掉 -c 参数即可
sudo htpasswd /etc/nginx/.htpasswd developer
```
```nginx
location /api/internal/ {
auth_basic "Restricted Area"; # 弹窗提示标题
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
```
## 安全加固
生产环境中的 Nginx 是第一道防线,需要做好基础安全设置。
```nginx
http {
# ── 隐藏版本信息 ──
server_tokens off; # response header 中不显示 Nginx 版本
more_clear_headers Server; # 需 headers-more 模块,完全抹除 Server 头
# ── 安全响应头 ──
add_header X-Frame-Options "SAMEORIGIN" always; # 防止点击劫持
add_header X-Content-Type-Options "nosniff" always; # 禁止 MIME 嗅探
add_header X-XSS-Protection "1; mode=block" always; # XSS 过滤器
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# ── HSTS(强制 HTTPS)──
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# ── CORS 跨域支持 ──
set $cors_origin "";
if ($http_origin ~* "^https://(app\.example\.com|cdn\.example\.com)$") {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
location /api/ {
# 处理浏览器预检请求 (OPTIONS)
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://backend;
}
}
```
### 防盗链
```nginx
location ~* \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
root /var/www/static;
# valid_referers — 只允许指定域名引用图片
valid_referers none blocked server_names
*.example.com example.com;
if ($invalid_referer) {
# 非法引用时返回 403 或一张占位图
return 403;
# rewrite ^/ https://example.com/no-hotlink.png break;
}
}
```
## 高级特性
### Location 匹配优先级
```mermaid
graph TD
A["URL请求到达"] --> B{"精确匹配\\n^~ ?"}
B -->|"是"| E["使用精确匹配"]
B -->|"否"| C{"正则匹配\\n按顺序?"}
C -->|"第一个匹配"| F["使用正则匹配 \\n立即停止"]
C -->|"无匹配"| D{"前缀最长匹配\\n是否有^~ ?"}
D -->|"是"| G["使用前缀匹配 \\n跳过正则"]
D -->|"否"| H["使用最长前缀匹配"]
style E fill:#9f9,stroke:#333
style F fill:#f99,stroke:#333
style G fill:#9cf,stroke:#333
style H fill:#ff9,stroke:#333
```
Location 匹配遵循严格优先级:
| 优先级 | 类型 | 写法 | 说明 |
|--------|------|------|------|
| ① | 精确 | `=` | 完全相等才命中 |
| ② | 最佳前缀 | `^~` | 最长前缀匹配后立即停止搜索正则 |
| ③ | 正则 | `~` / `~*` | 按配置文件中的**顺序**匹配,第一个命中就停止 |
| ④ | 普通前缀 | (无前缀符) | 记录最长匹配,但最终可能被正则覆盖 |
```nginx
location = /exact-match { # ① 精确匹配
return 200 "exact";
}
location ^~ /static/ { # ② 最佳前缀,跳过正则
root /var/www;
}
location ~* \.(jpg|png|gif)$ { # ③ 正则,忽略大小写
expires 7d;
}
location /images { # ④ 普通前缀
alias /var/www/images;
}
```
> [!QUESTION] 思考题
> 请求 `/images/logo.png` 会被哪个 location 处理?
> - ① `/images` 和普通前缀
> - ② `~* \.(jpg|png|gif)$` 正则
>
> **答案**:②。虽然 `/images` 的前缀更长,但正则匹配的优先级高于普通前缀。只有 `^~` 才能阻止正则匹配。
### Gzip 压缩
```nginx
http {
gzip on;
gzip_vary on; # 添加 Vary: Accept-Encoding
gzip_proxied any; # 对所有代理请求都压缩
gzip_comp_level 6; # 压缩级别 1-9,6 是性价比最优
gzip_min_length 1k; # 小于 1k 的不压缩
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript
image/svg+xml; # 需要压缩的内容类型
}
```
### 日志配置
```nginx
http {
# 自定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 按小时分割日志
map $time_iso8601 $logdate {
~^(\d{4}-\d{2}-\d{2}) %Y-%m-%d;
}
}
```
> [!NOTE] 常用变量速查
> | 变量 | 含义 |
> |------|------|
> | `$remote_addr` | 客户端真实 IP |
> | `$request_uri` | 原始请求 URI(含参数) |
> | `$status` | 响应状态码 |
> | `$body_bytes_sent` | 发送给客户端的字节数 |
> | `$request_time` | 请求处理总耗时(秒) |
> | `$upstream_response_time` | 后端响应耗时 |
> | `$http_host` | Host 请求头 |
> | `$https` | 是否 HTTPS(on/off) |
### Stream 模块 — L4 四层代理
Nginx 不仅可以代理 HTTP(L7),通过 `stream` 块还可以做 **TCP/UDP 四层负载均衡**。
```nginx
# stream 块在 http 块同级
stream {
# SSH 连接到内网跳板机
upstream ssh_backend {
server 10.0.0.5:22;
server 10.0.0.6:22;
}
# Redis / MySQL 等数据库代理
upstream redis_backend {
least_conn;
server 10.0.1.10:6379;
server 10.0.1.11:6379;
}
server {
listen 8022; # 对外暴露 SSH 端口
proxy_pass ssh_backend;
proxy_timeout 30s;
}
server {
listen 6379; # 对外暴露 Redis 端口
proxy_pass redis_backend;
}
}
http {
# ... HTTP 配置照常
}
```
> [!NOTE] Stream vs HTTP
> | 维度 | `http {}` 块 | `stream {}` 块 |
> |------|-------------|----------------|
> | 工作层级 | L7 应用层 | L4 传输层 |
> | 协议支持 | HTTP、HTTPS | TCP、UDP |
> | 可解析内容 | URL、Header、Cookie | 无法解析应用数据 |
> | 典型场景 | Web 代理、静态服务 | SSH/DB 代理、游戏服务器 |
### SSI — 服务端包含
Nginx 内置 SSI 支持,可以在 HTML 页面中嵌入其他页面的片段。
```nginx
location / {
ssi on; # 启用 SSI
ssi_silent_errors on; # 忽略子请求错误,不影响主响应
root /var/www;
}
```
HTML 中使用 `<!--#include -->` 指令:
```html
<!--# include virtual="/includes/header.html" -->
<div class="content">主内容区域</div>
<!--# include virtual="/includes/footer.html" -->
```
> [!CAUTION] 性能注意
> SSI 会为每个 include 发起内部子请求,频繁使用会增加响应延迟。现代架构中更推荐前端组件化方案(React/Vue)。仅在遗留系统维护时考虑 SSI。
---
## 性能调优
### 核心参数参考值
```nginx
worker_processes auto; # 等于 CPU 核心数
events {
worker_connections 4096; # 根据预期并发调整
use epoll; # Linux 默认就是 epoll,可显式指定
}
http {
sendfile on; # 零拷贝传输静态文件
tcp_nopush on; # 与 sendfile 配合,减少数据包数量
tcp_nodelay on; # 禁用 Nagle 算法,降低延迟
keepalive_timeout 65; # 保持连接超时
client_max_body_size 20m; # 上传文件大小限制
open_file_cache max=10000 inactive=20s; # 文件描述符缓存
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
}
```
### 调优原则
| 方向 | 方法 | 效果 |
|------|------|------|
| **并发提升** | `worker_connections` + `worker_processes` | 最大并发 ≈ workers × connections |
| **I/O 优化** | `sendfile` + `tcp_nopush` | 静态文件传输减少 30%~50% |
| **连接复用** | `keepalive_timeout` + `proxy_keepalive` | 减少 TCP 握手开销 |
| **磁盘 I/O** | `open_file_cache` | 避免频繁打开文件描述符 |
## 故障排查
### 常用命令
```bash
# 查看 Nginx 进程信息
ps aux | grep nginx
# 重新加载配置(零停机)
sudo nginx -s reload
# 测试配置文件语法
sudo nginx -t
# 查看监听端口
sudo ss -tlnp | grep nginx
# 实时查看错误日志
sudo tail -f /var/log/nginx/error.log
# 检查配置中包含的所有 server_name
grep -r server_name /etc/nginx/
```
### 常见问题速查
> [!ERROR] 502 Bad Gateway
> **原因**:后端服务未启动或无法连接。
> **排查**:确认后端服务正常运行;检查 `proxy_pass` 的地址和端口;查看后端日志。
> [!ERROR] 504 Gateway Timeout
> **原因**:后端响应超时(默认 60 秒)。
> **解决**:增加 `proxy_read_timeout`,或优化后端查询性能。
> ```nginx
> proxy_connect_timeout 5s;
> proxy_send_timeout 10s;
> proxy_read_timeout 60s;
> ```
> [!ERROR] 413 Request Entity Too Large
> **原因**:上传文件超过 `client_max_body_size`。
> **解决**:在对应 location 中增大限制。
> ```nginx
> client_max_body_size 100m;
> ```
> [!WARNING] 403 Forbidden(静态文件)
> **常见原因**:权限不足或 `index` 文件不存在。
> **解决**:确认 Nginx 用户(`www-data`/`nginx`)对目录有读权限;确认文件存在。
## 配置管理与部署
### 配置文件拆分
生产环境中,不建议把所有配置写在一个大文件中。利用 `include` 实现模块化拆分。
```
/etc/nginx/
├── nginx.conf # 主配置(全局 + events + http)
├── conf.d/
│ ├── api.conf # API 代理配置
│ ├── web.conf # Web 前端配置
│ └── static.conf # 静态资源服务配置
├── sites-available/ # 可用站点配置(软链到 enabled)
│ ├── example.com
│ └── staging.example.com
├── sites-enabled/ # 已启用站点
├── upstream/
│ ├── backend-pool.conf # 后端服务器池定义
│ └── cache.conf
├── snippets/ # 可复用片段
│ ├── ssl-params.conf # SSL 安全参数
│ ├── rate-limit.conf # 限流规则
│ └── security-headers.conf
└── mime.types
```
```nginx
# nginx.conf — 主入口保持精简
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 引入所有 upstream 定义
include /etc/nginx/upstream/*.conf;
# 引入所有 server 配置
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
```
> [!TIP] 为什么这样设计?
> - `conf.d/` 下的 `.conf` 文件按功能划分,每个文件一个主题
> - `sites-available` / `sites-enabled` 模式源自 Apache:修改后通过 `ln -s` 启用,方便回滚
> - `snippets/` 存放通用模板,避免多处复制粘贴同一份 SSL 或安全配置
### Docker Compose 完整示例
用 Docker Compose 一键拉起 Nginx + 后端服务的开发环境。
```yaml
# docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/certs:/etc/nginx/certs:ro
- ./frontend/dist:/usr/share/nginx/html:ro
- nginx-cache:/var/cache/nginx
- nginx-run:/var/run
depends_on:
- api
- webapp
restart: unless-stopped
api:
build: ./api
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
expose:
- "3000"
restart: unless-stopped
webapp:
build: ./webapp
expose:
- "8080"
restart: unless-staged
volumes:
nginx-cache:
nginx-run:
```
```nginx
# nginx/conf.d/default.conf
upstream api_backend {
server api:3000;
}
server {
listen 80;
server_name _;
location /api/ {
proxy_pass http://api_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html; # SPA 路由兜底
}
}
```
> [!NOTE] Docker Nginx 注意事项
> - 挂载配置时使用 `:ro` 只读标志,防止容器内被篡改
> - `/var/run` 和 `/var/cache/nginx` 需要用 volume,否则重载时会因缺少 pid 文件而报错
> - SPA 应用需要 `try_files` 兜底到 `index.html`,否则直接刷新子路由会 404
### 灰度发布(Canary Release)
Nginx 可以实现基于权重或客户端特征的渐进式流量切分。
```nginx
http {
# 基于请求头实现灰度(前端在特定版本时携带 x-canary: true)
map $http_x_canary $canary_upstream {
default "";
"true" canary_group;
}
# 基于用户 IP 的哈希分流(同一 IP 始终访问同一版本)
geo $hash_upstream {
default production_pool;
10.0.0.0/8 canary_group; # 仅内部 IP 走新版
}
upstream production_pool {
server 10.1.0.1:8080;
server 10.1.0.2:8080;
}
upstream canary_group {
server 10.2.0.1:8080; # 新版本仅一台机器测试
}
server {
location / {
# 优先使用 header 分流,其次 IP hash,最后默认生产
set $target $canary_upstream;
if ($target = "") {
set $target $hash_upstream;
}
if ($target = "") {
set $target production_pool;
}
proxy_pass http://$target;
}
}
}
```
> [!TIP] 灰度发布常见策略
> | 阶段 | 流量分配 | 适用场景 |
> |------|---------|---------|
> | **内部测试** | 100% 内部 IP → 新版 | 开发团队自测 |
> | **小流量灰度** | 1% ~ 5% 随机 → 新版 | 验证核心指标 |
> | **逐步放量** | 逐步提升 10% → 50% → 100% | A/B 结果良好 |
> | **全量上线** | 100% → 新版 | 灰度结束 |
### NJS — Nginx JavaScript 脚本
Nginx 支持嵌入 JavaScript 代码,实现动态配置逻辑(无需 reload)。
```bash
# 安装 njs 模块
apt install nginx-module-njs # Debian/Ubuntu
```
```nginx
# 加载 JS 脚本
load_module /usr/lib/nginx/modules/ngx_http_js_module.so;
http {
js_import filter.js;
server {
location / {
js_content filter.add_custom_header;
proxy_pass http://backend;
}
}
}
```
```javascript
// filter.js
export function addCustomHeader(r) {
r.headersOut["X-Custom-Version"] = "v2.1";
r.headersOut["X-Served-By"] = r.serverName;
r.sendHeader();
r.finish();
}
export default { addCustomHeader };
```
> [!NOTE] NJS vs OpenResty
> - **NJS**:轻量级,适合简单逻辑(改 Header、条件路由),语法受限但性能极高
> - **OpenResty**(Nginx + Lua):完整的编程平台,可以操作数据库、调用外部 API,适合复杂网关场景
---
## 与 CDN 的配合
典型的生产架构中,Nginx 常作为 **边缘节点** 或 **回源层** 与 CDN 配合工作:
```mermaid
sequenceDiagram
participant Client as 客户端
participant CDN as CDN 边缘节点
participant Nginx as Nginx 边缘/回源
participant App as 应用后端
Client->>CDN: 请求静态资源
CDN->>CDN: 缓存命中?
alt 命中
CDN-->>Client: 返回缓存内容
else 未命中
CDN->>Nginx: 回源请求
Nginx->>App: 转发请求
App-->>Nginx: 响应数据
Nginx-->>CDN: 返回内容并缓存
CDN-->>Client: 返回内容
end
```
> [!TIP] Nginx + CDN 的最佳实践
> - 静态资源走 CDN,动态请求回源到 Nginx + 后端
> - Nginx 可以作为 CDN 的边缘服务器,做一层统一入口
> - 通过 `X-Forwarded-For` 追踪真实的用户 IP(即使经过了 CDN)
## 关联笔记
- [[hhs/DEV/Docker/Docker.md]]
- [[hhs/DEV/Linux/Linux.md]]