Day 9-1 Nginx 架构与基础配置

Day 9-1 Nginx 架构与基础配置 本篇合并《Day_9-1_Nginx架构与基础配置.pdf》与《Day_9-1_Nginx架构与基础配置(1).pdf》,重复内容保留一份,补入两个版本各自的实验、输出和图示。存在配置差异时,在对应小节说明。 Nginx——可作为独立的 Web 服务器,更

Day 9-1 Nginx 架构与基础配置

本篇合并《Day_9-1_Nginx架构与基础配置.pdf》与《Day_9-1_Nginx架构与基础配置(1).pdf》,重复内容保留一份,补入两个版本各自的实验、输出和图示。存在配置差异时,在对应小节说明。

Nginx——可作为独立的 Web 服务器,更是“流量入口”。

主线说明:本课程主线是 Java 项目(若依 RuoYi-Vue),但 Nginx 作为通用接入层,也会承载 PHP/Python 等非 Java 应用。本节会部署一个 WordPress(PHP + MySQL)测试站点和一个 Flask(Python + Gunicorn)测试站点,验证 Nginx 对 PHP 和 Python 两种后端语言的代理能力。

本章概览

Nginx 课程模块概览

本节目标

目标 说明
理解 Nginx 架构 掌握 Master-Worker 进程模型和 epoll 事件驱动原理
部署 Nginx 使用 YUM 和源码两种方式安装 Nginx,包含第三方模块编译
掌握核心配置 理解 nginx.conf 层级结构、核心指令、location 匹配规则
理解 root 与 alias 掌握两者区别及适用场景
部署 PHP 应用 配置 Nginx 代理 PHP-FPM,完成 WordPress 站点部署
部署 Python 应用 配置 Nginx 代理 Gunicorn,完成 Flask 应用部署
平滑升级 掌握 Nginx 不停服升级二进制文件的方法

一、Web 架构演进与 Nginx 定位

1.1 Nginx 在整体架构中的位置

在开始学习之前,先建立完整的架构视图。Nginx 在企业级应用中扮演“接入层”的角色:

Nginx 静态资源服务、反向代理与后端应用和数据库的关系

Nginx 负责“接待”(接收请求、返回静态页面、转发动态请求),各种后端语言负责“干活”(处理业务逻辑、操作数据库)。

二、Nginx 架构深度了解(认知)

2.1 Master-Worker 进程模型

Nginx 启动后,进程分为两种角色:

Master 管理多个独立 Worker,Worker 使用 epoll 处理请求

Master-Worker 详解:

进程 职责 数量 关键特点
Master 进程 读取/校验配置、绑定端口、管理 Worker 进程 1 个 以 root 用户运行,不处理客户端请求
Worker 进程 接收并处理客户端请求 通常 = CPU 核心数 每个 Worker 独立,内存隔离;以 nginx 用户运行

实验:查看 Nginx 进程树。

# 启动 Nginx 后查看进程
[root@magedu ~]# ps aux --forest | grep nginx
root  1234 0.0 0.1 ? Ss ... nginx: master process
nginx 1235 0.0 0.2 ? S  ... nginx: worker process
nginx 1236 0.0 0.2 ? S  ... nginx: worker process

2.2 epoll 事件驱动模型

2.2.1 什么是 IO 模型?

在理解 epoll 之前,首先要明白什么是 IO(Input/Output,输入/输出)。

IO 就是程序从外部设备读取数据或向外部设备写入数据的过程。

对 Web 服务器来说,IO 主要指:

  • 读:从网卡读取客户端发送的 HTTP 请求。
  • 写:向网卡写入响应数据返回给客户端。

当 Nginx 处理大量并发连接时,每个连接都需要进行 IO 操作。IO 模型决定了服务器如何管理和处理这些 IO 操作,直接影响服务器的并发能力。

2.2.2 三种 IO 模型对比

模型一:阻塞 IO(BIO)——一个连接一个线程

BIO 一个连接对应一个阻塞线程

BIO 举例解析:

生活中的场景:餐厅每张桌子配一个服务员。客人不说话,服务员就站在旁边一直等。100 张桌子配 100 个服务员,但真正需要服务的可能只有 2–3 桌。其他服务员都在“发呆等”。

Nginx 实际场景(如果 Nginx 用 BIO):

  • 每个客户端连接需要一个独立的 Worker 线程。
  • 10000 个并发连接 → 10000 个线程。
  • 每个线程栈内存约 1–2 MB → 10000 × 2 MB = 20 GB 内存。
  • 线程切换消耗 CPU → 系统卡死。

Nginx 实际做法:Nginx 不用 BIO。Apache 的 prefork MPM 用的是类似 BIO 的模式,这也是为什么 Apache 并发能力远低于 Nginx。

模型二:select/poll——一个线程轮询所有连接

select/poll 扫描所有连接寻找活跃连接

select/poll 举例解析:

生活中的场景:餐厅只雇了 1 个服务员。服务员每隔 5 秒就跑遍所有桌子,挨个问“需要服务吗?”100 张桌子问完,只有 1 张有需求。99% 的时间浪费在问“不需要服务”的桌子上。

Nginx 实际场景(如果 Nginx 用 select/poll):

  • 一个 Worker 进程监控所有连接。
  • 每次调用 select/poll 需要遍历 10000 个连接的文件描述符。
  • 即使只有 1 个连接活跃,也要扫描 10000 次。
  • 时间复杂度 O(n),n=连接数,连接越多越慢。

Nginx 实际做法:Nginx 不用 select/poll。select 有 1024 连接数限制,poll 虽然无限制但同样低效。

模型三:epoll——事件驱动,只处理活跃连接

epoll 由内核通知就绪的活跃连接

epoll 举例解析:

生活中的场景:餐厅只雇了 1 个服务员,但每张桌子上都有呼叫铃。客人需要服务时按铃,服务员只去按铃的桌子。10000 张桌子只有 1 张按铃,服务员只去那 1 张。

Nginx 实际场景(Nginx 实际使用 epoll):

  • 一个 Worker 进程监控所有连接。
  • 连接有数据时,内核主动通知 Nginx(epoll_wait 返回)。
  • 返回的 events 数组中只有活跃连接,无需遍历。
  • 时间复杂度 O(1),10000 连接只处理活跃的那 1 个。
  • 无连接数限制。

Nginx 实际做法:Nginx 在 events 块中配置 use epoll,就是告诉 Nginx 使用这种高效的事件驱动模型。

2.2.3 三种模型性能对比总结

对比维度 BIO(阻塞 IO) select/poll epoll
并发连接数 低(受线程数限制) 中(select 有 1024 限制) 高(数万)
CPU 占用 高(线程切换) 高(轮询扫描) 低(事件通知)
内存占用 极高(每连接一线程) 低 低
时间复杂度 O(n) O(n) O(1)
适用场景 连接数 <100 连接数几百 高并发生产环境
谁在用 Apache(prefork 模式) 早期 Unix 网络程序 Nginx、Redis

epoll 让 Nginx 的 Worker 进程不会把时间浪费在“问没有数据的连接”上。它只处理真正有数据要读写的连接,所以一个 Worker 能同时支撑数万连接。这就是 Nginx 高性能的核心秘密。

原文的 O(1) 和“无连接数限制”为教学简化:实际处理工作量与就绪事件数相关,连接数还受文件描述符、内存及 worker_connections 等限制。Nginx 的具体事件机制也取决于操作系统,并非所有平台都使用 epoll。

2.3.4 O(n) 和 O(1) 简单理解

这两个是算法复杂度的表示方式,用来描述“随着连接数增加,工作量怎么变化”。本小节保留原文编号 2.3.4。

表示法 读法 含义
O(1) 大 O 一 恒定时间——不管连接有多少,工作量不变
O(n) 大 O n 线性时间——连接越多,工作量越大

对应到 I/O 模型:

I/O 模型 复杂度 说明
select/poll O(n) 每次都要扫描全部 n 个连接,找出哪些有数据。10000 个连接就要扫描 10000 次
epoll O(1) 内核直接告诉你有数据的连接是哪几个,只处理这些即可。10000 个连接只有 1 个活跃,就只处理 1 个
O(n) = 人越多,活越重
O(1) = 人再多,活不变

2.2.5 Nginx 中如何配置 epoll

events {
    use epoll;                  # 指定使用 epoll 事件模型
    worker_connections 10240;   # 单 Worker 最大连接数
    multi_accept on;            # 一次接受所有新连接
}

注意:在 Linux 系统上,Nginx 默认就使用 epoll,无需显式指定。但了解这个配置可以让你知道 Nginx 在用什么机制工作。

三、Nginx 快速部署

3.1 环境准备

主环境:Rocky Linux 10。Ubuntu 26.04 差异见旁注。

# 安装基础工具
[root@magedu ~]# dnf install -y curl wget vim net-tools git

# 关闭防火墙(测试环境)
[root@magedu ~]# systemctl stop firewalld
[root@magedu ~]# systemctl disable firewalld

Ubuntu 26.04 旁注:

[root@magedu ~]# apt update
[root@magedu ~]# apt install -y curl wget vim git
[root@magedu ~]# ufw disable

3.2 生产环境安装方式选择

对比维度 YUM/DNF/APT 安装 源码编译安装
安装速度 快(1–2 分钟) 慢(5–10 分钟)
版本可控性 取决于 OS 仓库版本 完全可控
模块选择 固定 完全自定义
升级维护 简单 需重新编译
systemd 服务 自动创建 需手动创建

生产环境推荐:源码编译安装——版本可控、路径标准化、可按需添加第三方模块。

3.3 YUM 安装 Nginx(快速上手)

# Rocky Linux 10:安装 Nginx
[root@magedu ~]# dnf install -y nginx

# 查看版本
[root@magedu ~]# nginx -v
nginx version: nginx/1.26.0

# 启动服务(YUM 安装已自带 systemd 服务文件)
[root@magedu ~]# systemctl enable --now nginx
[root@magedu ~]# systemctl status nginx

3.4 源码编译安装(含第三方模块)

源码编译安装后,需要手动创建 systemd 服务文件。

什么是第三方模块?

Nginx 官方版本只包含核心功能,很多企业级功能需要通过第三方模块扩展:

模块 用途 常用场景
echo-nginx-module 输出调试信息 测试 location 匹配、调试配置
headers-more-nginx-module 修改请求/响应头 添加、删除、修改 HTTP 头
nginx-http-flv-module 视频流推送 直播推流、视频点播
ngx_brotli Brotli 压缩 比 Gzip 压缩率更高
nginx-module-vts 流量统计 监控每个虚拟机的请求数、流量

静态模块 vs 动态模块

Nginx 支持两种方式添加第三方模块:

静态模块嵌入二进制,动态模块生成独立 .so 文件

两种方式对比总结:

对比维度 静态模块(--add-module) 动态模块(--add-dynamic-module)
编译方式 嵌入 nginx 二进制 生成独立的 .so 文件
加载方式 自动加载 需在 nginx.conf 中 load_module
版本匹配 无此问题 必须与 nginx 版本精确匹配
启动风险 低 高(.so 不匹配则启动失败)
新增模块 需重新编译整个 nginx 可单独编译新模块
适用场景 生产环境(推荐) 开发测试环境

企业生产环境实践建议:

生产环境:一次性把所有需要的模块编译进去(静态模块),省心、稳定、不出错。

  • 无版本匹配问题,升级 Nginx 时只需重新编译整个二进制。
  • 启动时加载完成,运行时无额外开销。
  • 避免因 .so 文件与 Nginx 版本不匹配导致服务启动失败。

开发测试环境:可以用动态模块方便试错,但上线前转成静态编译。

第一步:安装编译依赖

[root@magedu ~]# dnf install -y gcc make pcre2-devel zlib-devel openssl-devel git
[root@magedu ~]# dnf groupinstall -y "Development Tools"

第二步:下载 Nginx 源码和第三方模块

[root@magedu ~]# cd /usr/local/src

# 下载 nginx
[root@magedu ~]# wget https://nginx.org/download/nginx-1.30.4.tar.gz

# 下载 echo 模块
[root@magedu ~]# git clone https://github.com/openresty/echo-nginx-module.git

提示:echo 模块如果不能克隆,可以使用提前准备好的文件。

[root@magedu src]# pwd
/usr/local/src
[root@magedu src]# ls
echo-nginx-module-0.65.tar.gz  nginx-1.30.4.tar.gz

第三步:配置编译选项(添加第三方模块)

生产推荐:静态编译。

[root@magedu src]# useradd nginx
[root@magedu src]# tar xf nginx-1.30.4.tar.gz
[root@magedu src]# tar xf echo-nginx-module-0.65.tar.gz
[root@magedu src]# ls
echo-nginx-module-0.65  echo-nginx-module-0.65.tar.gz  nginx-1.30.4  nginx-1.30.4.tar.gz

[root@magedu ~]# cd nginx-1.30.4

[root@magedu ~]# ./configure \
    --prefix=/usr/local/nginx \
    --sbin-path=/usr/sbin/nginx \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --user=nginx \
    --group=nginx \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_stub_status_module \
    --with-http_gzip_static_module \
    --add-module=/usr/local/src/echo-nginx-module-0.65

动态编译(了解即可):

# 如需使用动态模块,将 --add-module 替换为 --add-dynamic-module
./configure ...原有参数... \
    --add-dynamic-module=/usr/local/src/echo-nginx-module

# 编译后会生成 .so 文件
# 需要在 nginx.conf 中加载:
# load_module modules/ngx_http_echo_module.so;

第四步:编译安装

[root@magedu ~]# make -j$(nproc)
[root@magedu nginx-1.30.4]# make install
make -f objs/Makefile install
make[1]: Entering directory '/usr/local/src/nginx-1.30.4'
test -d '/usr/local/nginx' || mkdir -p '/usr/local/nginx'
test -d '/usr/sbin' \
        || mkdir -p '/usr/sbin'
test ! -f '/usr/sbin/nginx' \
        || mv '/usr/sbin/nginx' \
                '/usr/sbin/nginx.old'
cp objs/nginx '/usr/sbin/nginx'
test -d '/etc/nginx' \
        || mkdir -p '/etc/nginx'
cp conf/koi-win '/etc/nginx'
cp conf/koi-utf '/etc/nginx'
cp conf/win-utf '/etc/nginx'
test -f '/etc/nginx/mime.types' \
        || cp conf/mime.types '/etc/nginx'
cp conf/mime.types '/etc/nginx/mime.types.default'
test -f '/etc/nginx/fastcgi_params' \
        || cp conf/fastcgi_params '/etc/nginx'
cp conf/fastcgi_params \
        '/etc/nginx/fastcgi_params.default'
test -f '/etc/nginx/fastcgi.conf' \
        || cp conf/fastcgi.conf '/etc/nginx'
cp conf/fastcgi.conf '/etc/nginx/fastcgi.conf.default'
test -f '/etc/nginx/uwsgi_params' \
        || cp conf/uwsgi_params '/etc/nginx'
cp conf/uwsgi_params \
        '/etc/nginx/uwsgi_params.default'
test -f '/etc/nginx/scgi_params' \
        || cp conf/scgi_params '/etc/nginx'
cp conf/scgi_params \
        '/etc/nginx/scgi_params.default'
test -f '/etc/nginx/nginx.conf' \
        || cp conf/nginx.conf '/etc/nginx/nginx.conf'
cp conf/nginx.conf '/etc/nginx/nginx.conf.default'
test -d '/var/run' \
        || mkdir -p '/var/run'
test -d '/var/log/nginx' \
        || mkdir -p '/var/log/nginx'
test -d '/usr/local/nginx/html' \
        || cp -R html '/usr/local/nginx'
test -d '/var/log/nginx' \
        || mkdir -p '/var/log/nginx'
make[1]: Leaving directory '/usr/local/src/nginx-1.30.4'

第五步:验证模块是否编译成功

[root@magedu nginx-1.30.4]# /usr/sbin/nginx -V
nginx version: nginx/1.30.4
built by gcc 14.3.1 20251022 (Red Hat 14.3.1-4) (GCC)
built with OpenSSL 3.5.5 27 Jan 2026
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --add-module=/usr/local/src/echo-nginx-module-0.65

第六步:源码编译安装后的原生管理方式

在创建 systemd 服务文件之前,先了解 Nginx 原生的启动/停止/重载方式。这些信号控制方式在生产环境排障时非常有用,也是理解 systemd 服务文件的基础。

# 启动 Nginx(直接执行)
[root@magedu ~]# /usr/sbin/nginx
[root@magedu ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State  PID/Program name
tcp        0      0 0.0.0.0:22    0.0.0.0:*      LISTEN 1003/sshd: /usr/sbi
tcp        0      0 0.0.0.0:80    0.0.0.0:*      LISTEN 7140/nginx: master
tcp6       0      0 :::22         :::*           LISTEN 1003/sshd: /usr/sbi

# 停止 Nginx(快速停止,强制终止)
[root@magedu ~]# /usr/sbin/nginx -s stop

# 优雅停止(完成当前请求后退出)
[root@magedu ~]# /usr/sbin/nginx -s quit

# 重载配置(不停服重新加载)
[root@magedu ~]# /usr/sbin/nginx -s reload

# 重新打开日志文件(日志切割时使用)
[root@magedu ~]# /usr/sbin/nginx -s reopen

# 查看 Nginx 进程
[root@magedu ~]# ps aux | grep nginx

信号控制方式详解:

命令 信号 作用
nginx -s stop SIGTERM 快速停止 Nginx 进程
nginx -s quit SIGQUIT 优雅停止,完成当前请求后退出
nginx -s reload SIGHUP 重新加载配置文件,不停服
nginx -s reopen SIGUSR1 重新打开日志文件

这些信号控制方式在 systemd 服务文件中也有体现,例如 ExecReload=/usr/sbin/nginx -s reload。

第七步:创建 systemd 服务文件

[root@magedu ~]# tee /etc/systemd/system/nginx.service <<'EOF'
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=network.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

[root@magedu ~]# systemctl daemon-reload
[root@magedu ~]# systemctl enable --now nginx
[root@magedu ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State  PID/Program name
tcp        0      0 0.0.0.0:22    0.0.0.0:*      LISTEN 1003/sshd: /usr/sbi
tcp        0      0 0.0.0.0:80    0.0.0.0:*      LISTEN 7231/nginx: master
tcp6       0      0 :::22         :::*           LISTEN 1003/sshd: /usr/sbi

浏览器访问 Nginx 默认欢迎页面

systemd 服务文件各字段说明:

字段 说明
Type=forking Nginx 是 fork 方式启动的守护进程
PIDFile 指定 PID 文件路径,systemd 靠它跟踪主进程
ExecStartPre 启动前执行配置语法检查
ExecStart 启动命令
ExecReload 重载配置命令(nginx -s reload)
ExecStop 停止命令(kill -s QUIT,即优雅停止)

两种管理方式对比:

管理方式 启动 停止 重载
原生信号 /usr/sbin/nginx nginx -s stop / nginx -s quit nginx -s reload
systemd systemctl start nginx systemctl stop nginx systemctl reload nginx

3.5 Nginx 平滑升级(生产环境常用)

3.5.1 什么是平滑升级?

平滑升级 = 在不停止 Nginx 服务的情况下,替换二进制文件并让新版本 Worker 接管请求。

原理:旧 Master 进程启动新 Master 进程 → 新 Master 启动新 Worker → 新 Worker 接管新连接 → 旧 Worker 处理完现有请求后退出。

场景 是否需要平滑升级
小版本安全补丁(如 1.30.4 → 1.30.5) 推荐
添加/删除模块重新编译 推荐
大版本升级(如 1.30 → 1.32) 推荐
测试环境 可直接重启

3.5.2 平滑升级前:必须先查看旧版本的编译参数

平滑升级时,新版本 Nginx 必须用与旧版本完全相同的编译参数,否则新 Master 进程启动时可能因为路径、模块不匹配而失败。

# 1. 查看旧版本的编译参数(必须先做这一步!)
[root@magedu ~]# /usr/sbin/nginx -V
nginx version: nginx/1.30.4
built by gcc 14.2.1 20250128 (Red Hat 14.2.1-7) (GCC)
configure arguments: --prefix=/usr/local/nginx --sbin-path=/usr/sbin/nginx ... --add-module=/usr/local/src/echo-nginx-module

# 建议保存到文件,方便下次升级直接复用
[root@magedu ~]# /usr/sbin/nginx -V | tee /opt/nginx-build-args.txt

原文这条保存命令未重定向标准错误;nginx -V 通常输出到标准错误,应使用本章速查表中的 nginx -V 2>&1 | tee /opt/nginx-build-args.txt。

为什么要先看 nginx -V?

原因 说明
路径一致性 --prefix、--conf-path 等路径参数必须一致,否则新 Master 找不到配置文件
模块一致性 静态编译的模块(--add-module)必须保留,否则新 Master 启动失败
功能一致性 如果旧版本有 --with-http_ssl_module,新版本没有,HTTPS 站点将无法工作

3.5.3 平滑升级实验步骤

# 1. 查看当前 Nginx 版本
[root@magedu ~]# /usr/sbin/nginx -v
nginx version: nginx/1.30.4

# 2. 备份当前二进制
[root@magedu ~]# mv /usr/sbin/nginx /usr/sbin/nginx.old

# 3. 编译新版本(使用与旧版本完全相同的编译参数)
[root@magedu src]# cd /usr/local/src
[root@magedu src]# wget https://nginx.org/download/nginx-1.31.3.tar.gz
[root@magedu src]# ls
echo-nginx-module-0.65         nginx-1.30.4         nginx-1.31.3.tar.gz
echo-nginx-module-0.65.tar.gz  nginx-1.30.4.tar.gz

[root@magedu src]# tar xzf nginx-1.31.3.tar.gz
[root@magedu src]# cd nginx-1.31.3

[root@magedu nginx-1.31.3]# ./configure [原样粘贴旧版本的编译参数]
[root@magedu nginx-1.31.3]# make        # 只编译,不安装

# 4. 复制新二进制文件(保留旧版本)
[root@magedu nginx-1.31.3]# cp objs/nginx /usr/sbin/nginx
[root@magedu nginx-1.31.3]# ls /usr/sbin/nginx*
/usr/sbin/nginx  /usr/sbin/nginx.old

# 5. 向旧 Master 进程发送 USR2 信号(启动新版本)
[root@magedu ~]# kill -USR2 $(cat /var/run/nginx.pid)
[root@magedu nginx-1.31.3]# ps aux | grep nginx
root  9092 0.0 0.1 12828 3108 ? Ss 18:04 0:00 nginx: master process /usr/sbin/nginx
nginx 9093 0.0 0.2 13028 4720 ? S  18:04 0:00 nginx: worker process
root  9129 0.0 0.4 12848 7328 ? S  18:08 0:00 nginx: master process /usr/sbin/nginx
nginx 9130 0.0 0.2 13048 4276 ? S  18:08 0:00 nginx: worker process
root  9132 0.0 0.1 227696 2180 pts/0 S+ 18:08 0:00 grep --color=auto nginx

# 6. 向旧 Master 进程发送 WINCH 信号(停止旧 Worker,如果有连接会等连接关闭)
[root@magedu ~]# kill -WINCH $(cat /var/run/nginx.pid.oldbin)
[root@magedu nginx-1.31.3]# ps aux | grep nginx
root  9092 0.0 0.1 12828 3112 ? Ss 18:04 0:00 nginx: master process /usr/sbin/nginx
root  9129 0.0 0.4 12848 7328 ? S  18:08 0:00 nginx: master process /usr/sbin/nginx
nginx 9130 0.0 0.2 13048 4276 ? S  18:08 0:00 nginx: worker process

# 7. 向旧 Master 进程发送 QUIT 信号(完全退出)
[root@magedu ~]# kill -QUIT $(cat /var/run/nginx.pid.oldbin)
[root@magedu nginx-1.31.3]# ps aux | grep nginx
root  9129 0.0 0.4 12848 7328 ? S 18:08 0:00 nginx: master process /usr/sbin/nginx
nginx 9130 0.0 0.2 13048 4276 ? S 18:08 0:00 nginx: worker process
root  9146 0.0 0.1 227696 2208 pts/0 S+ 18:11 0:00 grep --color=auto nginx

# 8. 确认旧 master 已退出
[root@magedu ~]# ps aux | grep nginx | grep -v grep
# 应该只看到新的 master 和 worker 进程

# 9. 验证新版本生效
[root@magedu nginx-1.31.3]# /usr/sbin/nginx -v
nginx version: nginx/1.31.3

平滑升级信号说明:

信号 作用
USR2 启动新版本 Master 进程(新旧共存)
WINCH 停止旧 Worker 进程(新 Worker 接管请求)
QUIT 旧 Master 进程退出

进程变化过程:

初始状态:
    master (pid A) + worker

USR2 后:
    master (pid A) + worker (旧)
    master (pid B) + worker (新)

WINCH 后:
    master (pid A) ← 只保留了 master 进程
    master (pid B) + worker (新) ← 提供服务

QUIT 后:
    master (pid B) + worker (新) ← 最终状态

四、Nginx 基础配置

4.1 配置文件的层级结构

配置块 作用范围 主要职责
main(全局块) 全局 Nginx 整体运行参数(用户、进程数、PID)
events 块 全局 网络连接参数(事件模型、最大连接数)
http 块 全局 HTTP 通用配置(MIME、日志、Gzip)
server 块 虚拟主机 定义网站(域名、端口)
location 块 URL 路径 匹配 URL 并执行处理
# ==================== main 块:全局配置 ====================
# 指定 Nginx worker 进程的运行用户(用于文件访问权限)
user nginx;

# worker 进程数,建议设置为 CPU 核心数,auto 表示自动检测
worker_processes auto;

# 主进程 PID 文件存放路径
pid /var/run/nginx.pid;

# ==================== events 块:事件驱动配置 ====================
events {
    # 每个 worker 进程的最大并发连接数,需根据系统 ulimit -n 调整
    worker_connections 10240;

    # 事件驱动模型,Linux 下使用 epoll 高效处理大量并发连接
    use epoll;
}

# ==================== http 块:HTTP 协议相关配置 ====================
http {
    # 启用 sendfile 系统调用(零拷贝),提升静态文件传输效率
    sendfile on;

    # 长连接超时时间(秒),65 秒是常见值,平衡性能与资源占用
    keepalive_timeout 65;

    # ==================== server 块:虚拟主机配置 ====================
    server {
        # 监听端口 80(HTTP 默认端口)
        listen 80;

        # 虚拟主机域名,用于匹配请求的 Host 头
        server_name localhost;

        # ==================== location 块:URL 路由规则 ====================
        location / {
            # 站点根目录,对应请求 URL 的物理路径
            root /usr/share/nginx/html;

            # 默认索引文件,访问目录时按顺序查找
            index index.html;
        }
    }
}

4.2 Nginx 虚拟主机配置

虚拟主机是一种资源共享技术,它让一台物理服务器上同时运行多个独立的网站。每个网站在逻辑上相互隔离,拥有自己独立的域名、根目录和配置文件,互不干扰。

类型 区分依据 配置指令 典型应用场景
基于域名 请求头中的 Host 字段 server_name 同一 IP 和端口下运行多个网站(最常用)
基于 IP 请求的目标 IP 地址 listen IP:port 服务器绑定多个 IP,每个 IP 对应不同站点
基于端口 请求的目标端口号 listen port 同一 IP 不同端口区分不同服务,如管理后台

基于域名的虚拟主机

所有域名解析到同一个 IP,Nginx 根据请求头中的 Host 字段分发到不同站点。

多个域名解析到同一 IP,根据 Host 分发到不同站点

基于 IP 的虚拟主机

服务器绑定多个 IP,每个 IP 对应一个虚拟主机,Nginx 根据请求的目标 IP 分发。

不同目标 IP 对应不同虚拟主机

基于端口的虚拟主机

同一 IP,不同端口对应不同虚拟主机,通过端口号区分服务。

同一 IP 使用 80、8080 和 8888 端口区分服务

虚拟主机配置示例

# ① 基于域名的虚拟主机(最常用)
server {
    listen 80;
    server_name www.magedu.com;   # 访问 www.magedu.com 走这个
    root /var/www/magedu;
}

server {
    listen 80;
    server_name blog.magedu.com;  # 访问 blog.magedu.com 走这个
    root /var/www/blog;
}

# ② 基于 IP 的虚拟主机
server {
    listen 192.168.1.10:80;       # 访问 192.168.1.10 走这个
    server_name _;
    root /var/www/site1;
}

server {
    listen 192.168.1.11:80;       # 访问 192.168.1.11 走这个
    server_name _;
    root /var/www/site2;
}

# ③ 基于端口的虚拟主机
server {
    listen 80;                  # 对外网站
    server_name www.magedu.com;
    root /var/www/public;
}

server {
    listen 8080;                # 管理后台
    server_name www.magedu.com;
    root /var/www/admin;
}

虚拟主机技术:以京东为例

为什么需要虚拟主机?

想象一下,如果京东所有业务都挤在一个服务器、一个域名下:

  • www.jd.com/家电
  • www.jd.com/图书
  • www.jd.com/金融
  • www.jd.com/物流

这种结构会导致以下问题:

问题 说明
代码耦合 所有业务代码混在一起,牵一发而动全身
故障扩散 图书系统崩溃可能导致整个 www.jd.com 不可用
独立运维 无法单独重启或升级某一个业务模块
权限混乱 所有业务共享同一套配置和权限体系
扩展困难 无法针对高流量业务单独扩容

京东的子域名架构(虚拟主机实践)

原文以基于域名的虚拟主机为例,将不同业务隔离到独立的子域名下:

京东各业务子域名及业务隔离的教学示例

Nginx 虚拟主机配置(京东风格):

# 京东主站
server {
    listen 80;
    server_name www.jd.com;
    root /var/www/jd/main;
}

# 京东图书
server {
    listen 80;
    server_name book.jd.com;
    root /var/www/jd/book;
}

# 京东金融:独立域名,独立 SSL 证书
server {
    listen 443 ssl;
    server_name jr.jd.com;
    root /var/www/jd/finance;
    ssl_certificate finance.crt;
    ssl_certificate_key finance.key;
}

# 京东秒杀
server {
    listen 80;
    server_name miaosha.jd.com;
    root /var/www/jd/miaosha;
}

# 京东物流
server {
    listen 80;
    server_name logistics.jd.com;
    root /var/www/jd/logistics;
}

虚拟主机本质上是一种“多租户”技术——让一台服务器(或一组服务器)通过域名区分不同业务,每个业务拥有独立的代码、配置、团队和生命周期,既能共享基础设施,又能保持业务隔离。京东等大型互联网公司的子域名架构,正是原文用于解释虚拟主机技术的案例。

4.3 location 匹配规则

location 是 Nginx 配置中最核心和强大的指令之一,它决定了如何响应不同的客户端请求。

六种匹配方式:

优先级 匹配类型 语法 示例 匹配后行为
1 精确匹配 = /path location = /health 立即停止
2 前缀优先匹配 ^~ /path location ^~ /static/ 停止正则匹配
3 正则匹配(区分大小写) ~ /pattern location ~ .php$ 按顺序匹配
4 正则匹配(不区分大小写) ~* /pattern location ~* .jpg$ 按顺序匹配
5 通用前缀匹配 /path location /api/ 最长匹配
6 通用匹配 / location / 兜底

原文将 ~ 和 ~ 分列为优先级 3、4;实际上两者同属正则 location,按配置出现顺序测试,首个匹配者胜出,并非 ~ 固定高于 ~。前缀匹配先记录最长前缀;最长前缀带 ^~ 时才跳过相应正则匹配。

生产环境常见 location 配置:

server {
    # 1. 健康检查:精确匹配
    location = /health {
        return 200 "OK\n";
    }

    # 2. 静态资源:前缀优先匹配
    location ^~ /static/ {
        root /var/www/ruoyi;
        expires 30d;
    }

    # 3. 图片文件:不区分大小写正则匹配
    location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
        root /var/www/ruoyi;
        expires 30d;
    }

    # 4. PHP 请求:区分大小写正则匹配
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # 5. API 版本控制:最长匹配
    location /api/v1/ {
        proxy_pass http://backend_v1/;
    }
    location /api/ {
        proxy_pass http://backend/;
    }

    # 6. 前端 SPA:兜底
    location / {
        try_files $uri $uri/ /index.html;
    }
}

第二份课件还展示了默认虚拟主机中的错误页处理。下面为去除重复监听及首页配置后的相关指令,放在 server 中:

error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root html;
}

4.4 实验:验证 location 匹配规则

实验目标

通过本实验,直观验证六种 location 匹配方式的优先级和实际效果。

实验步骤

Step 1:确认 echo 模块已安装。

[root@magedu ~]# /usr/sbin/nginx -V 2>&1 | grep echo
--add-module=/usr/local/src/echo-nginx-module

如果没有输出,说明编译时未添加 echo 模块,需要重新编译。

Step 2:创建测试配置文件。

[root@magedu ~]# cat > /etc/nginx/conf.d/location-test.conf <<'EOF'
server {
    listen 80;
    server_name location.magedu.com;

    # 日志
    access_log /var/log/nginx/location_test_access.log main;
    error_log /var/log/nginx/location_test_error.log warn;

    # ============================================
    # 1. 精确匹配(最高优先级)
    # ============================================
    location = /health {
        echo "精确匹配(=):/health";
    }

    # ============================================
    # 2. 前缀优先匹配(匹配后停止正则)
    # ============================================
    location ^~ /static/ {
        echo "前缀优先匹配(^~):/static/ 下的所有请求";
    }

    # ============================================
    # 3. 正则匹配(区分大小写)
    # ============================================
    location ~ \.php$ {
        echo "正则匹配(~):.php 结尾的请求";
    }

    # ============================================
    # 4. 正则匹配(不区分大小写)
    # ============================================
    location ~* \.jpg$ {
        echo "正则匹配(~*):.jpg 或 .JPG 结尾的请求";
    }

    # ============================================
    # 5. 通用前缀匹配(最长匹配原则)
    # ============================================
    location /api/v1/ {
        echo "通用前缀匹配:/api/v1/(更长的匹配)";
    }

    location /api/ {
        echo "通用前缀匹配:/api/(较短的匹配)";
    }

    # ============================================
    # 6. 通用匹配(兜底)
    # ============================================
    location / {
        echo "通用匹配(/):所有未被匹配的请求";
    }
}
EOF

Step 3:测试配置并重载。

[root@magedu ~]# nginx -t
[root@magedu ~]# systemctl reload nginx

Step 4:验证各匹配规则。

# 1. 精确匹配测试
[root@magedu ~]# curl http://location.magedu.com/health
精确匹配(=):/health

[root@magedu ~]# curl http://location.magedu.com/health/
通用匹配(/):所有未被匹配的请求
# 注意:/health/ 不能匹配 = /health,会落到 / 兜底

# 2. 前缀优先匹配测试
[root@magedu ~]# curl http://location.magedu.com/static/css/style.css
前缀优先匹配(^~):/static/ 下的所有请求

# 3. 正则匹配(区分大小写).php
[root@magedu ~]# curl http://location.magedu.com/index.php
正则匹配(~):.php 结尾的请求

[root@magedu ~]# curl http://location.magedu.com/index.PHP
通用匹配(/):所有未被匹配的请求
# 注意:区分大小写,.PHP 不匹配

# 4. 正则匹配(不区分大小写).jpg
[root@magedu ~]# curl http://location.magedu.com/photo.jpg
正则匹配(~*):.jpg 或 .JPG 结尾的请求

[root@magedu ~]# curl http://location.magedu.com/photo.JPG
正则匹配(~*):.jpg 或 .JPG 结尾的请求

# 5. 通用前缀匹配(最长匹配原则)
[root@magedu ~]# curl http://location.magedu.com/api/v1/user
通用前缀匹配:/api/v1/(更长的匹配)

[root@magedu ~]# curl http://location.magedu.com/api/user
通用前缀匹配:/api/(较短的匹配)

# 6. 兜底匹配
[root@magedu ~]# curl http://location.magedu.com/anything
通用匹配(/):所有未被匹配的请求

匹配结果总结:

请求 URL 匹配规则 说明
/health = /health 精确匹配 最高优先级,完全相等才匹配
/health/ / 通用匹配 精确匹配不包含 /health/
/static/css/style.css ^~ /static/ 前缀优先匹配 匹配后停止正则匹配
/index.php ~ .php$ 正则匹配(区分大小写) 小写 .php 匹配
/index.PHP / 通用匹配 大写 .PHP 不匹配区分大小写的正则
/photo.jpg ~* .jpg$ 正则匹配(不区分大小写) 小写 .jpg 匹配
/photo.JPG ~* .jpg$ 正则匹配(不区分大小写) 大写 .JPG 也匹配
/api/v1/user /api/v1/ 通用前缀匹配 更长的匹配优先
/api/user /api/ 通用前缀匹配 匹配到较短的通用前缀
/anything / 通用匹配 兜底匹配

核心收获:通过这个实验,你直观地看到了 Nginx 如何根据不同的 location 规则将请求路由到不同的处理逻辑。这正是 Nginx 作为“流量入口”的核心能力——根据 URL 特征做精细化路由。

4.5 root vs alias 的区别

root 和 alias 都用于指定文件路径,但行为完全不同:

指令 路径拼接方式 示例
root root 路径 + location 路径 root /var/www; + /images/1.jpg → /var/www/images/1.jpg
alias alias 路径替换 location 路径 alias /var/www/img/; + /images/1.jpg → /var/www/img/1.jpg

配置对比

# root 方式:拼接路径
location /images/ {
    root /var/www/static;
    # 请求 /images/logo.png → 查找 /var/www/static/images/logo.png
}

# alias 方式:替换路径
location /images/ {
    alias /var/www/static/img/;
    # 请求 /images/logo.png → 查找 /var/www/static/img/logo.png
}

关键注意事项

注意事项 root alias
末尾斜杠 可加可不加 必须加(否则路径拼接异常)
location 匹配 匹配路径会拼接到 root 后 匹配路径被 alias 完全替换
适用场景 常规静态文件服务 路径映射/重命名目录
# 错误:alias 忘记末尾斜杠
location /images/ {
    alias /var/www/static/img;
    # 请求 /images/logo.png → /var/www/static/imglogo.png(缺少 /)
}

# 正确
location /images/ {
    alias /var/www/static/img/;
}

# root 无此问题
location /images/ {
    root /var/www/static;
    # 请求 /images/logo.png → /var/www/static/images/logo.png
}

实验验证

# 创建测试文件
[root@magedu ~]# mkdir -p /var/www/static/root-test
[root@magedu ~]# echo "root test" > /var/www/static/root-test/test.txt
[root@magedu ~]# mkdir -p /var/www/static/img
[root@magedu ~]# echo "alias test" > /var/www/static/img/test.txt

# 配置测试
[root@magedu ~]# cat > /etc/nginx/conf.d/root-alias-test.conf <<'EOF'
server {
    listen 80;
    server_name test.magedu.com;

    location /root-test/ {
        root /var/www/static;
    }

    location /alias-test/ {
        alias /var/www/static/img/;
    }
}
EOF

[root@magedu ~]# nginx -t && systemctl reload nginx

[root@magedu ~]# curl http://test.magedu.com/root-test/test.txt
root test      # 实际读取 /var/www/static/root-test/test.txt

[root@magedu ~]# curl http://test.magedu.com/alias-test/test.txt
alias test     # 实际读取 /var/www/static/img/test.txt

本实验采用第二份课件已修正的 root-test 路径,避免第一份课件中创建 images 目录而请求 root-test 导致的路径不一致。

五、实验:创建第一个静态网站

# 创建网站目录和首页
[root@magedu ~]# mkdir -p /var/www/ruoyi
[root@magedu ~]# cat > /var/www/ruoyi/index.html <<'EOF'
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>若依管理系统</title></head>
<body><h1>若依管理系统</h1><p>Nginx 静态页面测试成功!</p></body>
</html>
EOF
[root@magedu ~]# chown -R nginx:nginx /var/www/ruoyi

# 创建配置文件
[root@magedu ~]# cat > /etc/nginx/conf.d/ruoyi.conf <<'EOF'
server {
    listen 80;
    server_name ruoyi.magedu.com;
    root /var/www/ruoyi;
    index index.html;
}
EOF

# 测试并重载
[root@magedu ~]# nginx -t
[root@magedu ~]# systemctl reload nginx

# 验证
[root@magedu ~]# curl -I http://localhost/
HTTP/1.1 200 OK

第二份课件的完整请求验证:

[root@magedu ~]# curl http://ruoyi.magedu.com
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>若依管理系统</title></head>
<body><h1>若依管理系统</h1><p>Nginx 静态页面测试成功!</p></body>
</html>

[root@magedu ~]# curl -v http://ruoyi.magedu.com
* Host ruoyi.magedu.com:80 was resolved.
* IPv6: (none)
* IPv4: 10.0.0.144
*   Trying 10.0.0.144:80...
* Connected to ruoyi.magedu.com (10.0.0.144) port 80
* using HTTP/1.x
> GET / HTTP/1.1
> Host: ruoyi.magedu.com
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Server: nginx/1.31.3
< Date: Thu, 30 Jul 2026 12:31:36 GMT
< Content-Type: text/html
< Content-Length: 180
< Last-Modified: Thu, 30 Jul 2026 12:28:30 GMT
< Connection: keep-alive
< ETag: "6a6b436e-b4"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>若依管理系统</title></head>
<body><h1>若依管理系统</h1><p>Nginx 静态页面测试成功!</p></body>
</html>
* Connection #0 to host ruoyi.magedu.com left intact

六、案例实战一:部署 WordPress 站点(PHP + MySQL)

6.1 整体架构

浏览器经 Nginx、PHP-FPM 访问 MySQL 中的 WordPress 数据

生产环境部署方式:

组件 实验环境(单机) 生产环境(推荐)
Nginx magedu 独立接入层服务器
PHP-FPM magedu 独立应用服务器(可水平扩展)
MySQL magedu 独立数据库服务器(主从/集群)
层级 组件 主机名 IP 地址 端口 作用
接入层 Nginx magedu 192.168.100.10 80 接收用户请求,转发 .php 请求给 PHP-FPM
应用层 PHP-FPM magedu 192.168.100.10 9000 执行 WordPress 的 PHP 代码
数据层 MySQL magedu 192.168.100.10 3306 存储 WordPress 数据

6.2 安装 MySQL

Rocky Linux 10 的 MySQL 软件包名是 mysql8.4-server。

# Rocky Linux 10 安装 MySQL
[root@magedu ~]# dnf install -y mysql8.4-server

# 启动 MySQL
[root@magedu ~]# systemctl enable --now mysqld
[root@magedu ~]# systemctl status mysqld

# Rocky 10 默认空密码,直接登录
[root@magedu ~]# mysql

# 创建 WordPress 数据库和用户
mysql> CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mysql> CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'YANGge123@';
mysql> GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

Ubuntu 26.04 旁注:

[root@magedu ~]# apt install -y mysql-server
[root@magedu ~]# systemctl enable --now mysql

6.3 部署 PHP-FPM

6.3.1 安装 PHP-FPM 与监听方式

# Rocky Linux 10
[root@magedu ~]# dnf install -y php php-fpm php-mysqlnd php-gd php-xml php-mbstring
[root@magedu ~]# systemctl enable --now php-fpm

第一份课件使用 TCP 端口连接:

[root@magedu ~]# ss -tlnp | grep php-fpm
tcp LISTEN 0 128 127.0.0.1:9000 0.0.0.0:* users:(("php-fpm",pid=1234,fd=5))

第二份课件使用 Unix Socket,后文 WordPress 的完整配置采用此方式:

[root@magedu ~]# ls /run/php-fpm/www.sock
/run/php-fpm/www.sock
[root@magedu ~]# ps aux | grep php-fpm
root   10248 0.0 1.7 419860 29200 ? Ss 18:27 0:00 php-fpm: master process (/etc/php-fpm.conf)
apache 10250 0.0 0.6 419860 11640 ? S  18:27 0:00 php-fpm: pool www
apache 10251 0.0 0.6 419860 11640 ? S  18:27 0:00 php-fpm: pool www
apache 10252 0.0 0.6 419860 11636 ? S  18:27 0:00 php-fpm: pool www
apache 10253 0.0 0.6 419860 11640 ? S  18:27 0:00 php-fpm: pool www
apache 10254 0.0 0.6 419860 11652 ? S  18:27 0:00 php-fpm: pool www

Ubuntu 26.04 旁注:

[root@magedu ~]# apt install -y php php-fpm php-mysql php-gd php-xml php-mbstring
[root@magedu ~]# systemctl enable --now php8.4-fpm
# Ubuntu 默认使用 socket 通信:/run/php/php8.4-fpm.sock

6.3.2 PHP-FPM 进程管理模式

PHP-FPM 有三种进程管理模式,通过 pm 指令配置:

模式 说明 适用场景
static 固定进程数,启动时创建 pm.max_children 个进程,永不变化 高并发、内存充足的生产环境
dynamic 动态调整,根据负载在 min 和 max 之间调整进程数 大多数场景(默认)
ondemand 按需创建,空闲进程在 pm.process_idle_timeout 后退出 低并发、内存敏感的场景

配置示例:

[root@magedu ~]# vim /etc/php-fpm.d/www.conf
; 管理模式选择
; static: 固定进程数
; dynamic: 动态调整(默认)
; ondemand: 按需创建
pm = dynamic

; static 模式配置
; pm.max_children = 50           ; 固定 50 个进程

; dynamic 模式配置(推荐)
pm.max_children = 50            ; 最大进程数(不能超过)
pm.start_servers = 10           ; 启动时创建 10 个进程
pm.min_spare_servers = 5         ; 最少空闲进程数
pm.max_spare_servers = 15        ; 最多空闲进程数

; ondemand 模式配置
; pm.max_children = 50
; pm.process_idle_timeout = 10s  ; 空闲 10 秒后退出

进程数计算公式

pm.max_children 的计算公式(dynamic 模式):
max_children = (总内存 - 系统内存 - MySQL内存) / 单个PHP进程内存

示例:服务器 8GB 内存
- 系统 + MySQL ≈ 2GB
- 可用内存 ≈ 6GB
- 单个PHP进程 ≈ 30–50MB
- max_children = 6GB / 40MB ≈ 153
- 保守建议:80–100

查看当前进程数

# 查看 PHP-FPM 进程数量
[root@magedu ~]# ps aux | grep php-fpm | grep -v grep | wc -l
12

两份课件的进程计数示例分别为 12 和 7,实际数量取决于运行环境和负载。状态页的开启配置及完整输出统一见 6.7。

6.4 下载并配置 WordPress

# 下载 WordPress
[root@magedu ~]# cd /tmp
[root@magedu ~]# wget https://wordpress.org/latest.tar.gz
[root@magedu ~]# tar xzf latest.tar.gz

# 复制到网站目录
[root@magedu ~]# mkdir -p /var/www/wordpress
[root@magedu ~]# cp -r /tmp/wordpress/* /var/www/wordpress/

# 设置权限(Nginx 用户需读写权限)
[root@magedu ~]# chown -R nginx:nginx /var/www/wordpress
[root@magedu ~]# chmod -R 755 /var/www/wordpress

# 创建配置文件
[root@magedu ~]# cd /var/www/wordpress
[root@magedu ~]# cp wp-config-sample.php wp-config.php
[root@magedu ~]# vim wp-config.php
# 修改以下内容:
# DB_NAME = 'wordpress'
# DB_USER = 'wpuser'
# DB_PASSWORD = 'YANGge123@'
# DB_HOST = 'localhost'

6.5 配置 Nginx 代理 WordPress

先在主配置中定义 main 日志格式,并包含 conf.d 子配置。第二份课件给出的有效配置如下:

[root@magedu ~]# grep -Ev "^ *#|^$" /etc/nginx/nginx.conf
worker_processes 1;
events {
    worker_connections 1024;
}
http {
    include mime.types;
    default_type application/octet-stream;

    # 打开日志格式定义
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    sendfile on;
    keepalive_timeout 65;
    server {
        listen 80;
        server_name localhost;
        location / {
            root html;
            index index.html index.htm;
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
    # 包含该目录下 *.conf 的子配置文件
    include /etc/nginx/conf.d/*.conf;
}

创建 WordPress 虚拟主机:

[root@magedu ~]# cat > /etc/nginx/conf.d/wordpress.conf <<'EOF'
server {
    listen 80;
    server_name wordpress.magedu.com;

    root /var/www/wordpress;
    index index.php index.html;

    access_log /var/log/nginx/wordpress_access.log main;
    error_log /var/log/nginx/wordpress_error.log warn;

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /robots.txt {
        log_not_found off;
        access_log off;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
}
EOF

[root@magedu ~]# nginx -t
[root@magedu ~]# systemctl reload nginx

6.6 验证 WordPress

# 测试 PHP 是否正常
[root@magedu ~]# curl -I http://wordpress.magedu.com/
HTTP/1.1 200 OK
Server: nginx/1.30.4

# 浏览器访问 http://wordpress.magedu.com/
# 应显示 WordPress 安装页面

提前做好 Windows hosts 文件解析。浏览器按下面的画面完成站点初始化、登录和后台验证:

WordPress 站点安装信息表

填写站点名称、管理员和邮箱

WordPress 登录页面

WordPress 管理后台

6.7 开启并查看 PHP-FPM 状态页

# PHP-FPM 配置
[root@magedu ~]# vim /etc/php-fpm.d/www.conf
; Default Value: not set
pm.status_path = /status
[root@magedu ~]# systemctl reload php-fpm.service

# nginx 配置:添加到 WordPress server 中
[root@magedu ~]# vim /etc/nginx/conf.d/wordpress.conf
location = /status {
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php-fpm/www.sock;
}
[root@magedu ~]# systemctl reload nginx.service

# 查看 PHP-FPM 状态
[root@magedu ~]# curl http://127.0.0.1/status -H "Host: wordpress.magedu.com"
pool:                 www
process manager:      dynamic
start time:           30/Jul/2026:19:25:19 +0800
start since:          14
accepted conn:        1
listen queue:         0
max listen queue:     0
listen queue len:     0
idle processes:       4
active processes:     1
total processes:      5
max active processes: 1
max children reached: 0
slow requests:        0

6.8 TCP 与 Unix Socket 两种连接方式对比

Nginx 通过 TCP 端口或 Unix Socket 连接 PHP-FPM

对比项 TCP 端口方式 Unix Socket 方式
地址 127.0.0.1:9000 /run/php-fpm/www.sock
网络开销 有(经过网络协议栈) 无(本地进程间通信)
性能 稍低 更高(推荐)
跨机器 支持 仅限本机
配置复杂度 简单 需注意权限(listen.acl_users)

对应 Nginx 配置

完整 WordPress 配置已采用 Socket 方式。选择 TCP 时,将该 PHP location 的 fastcgi_pass 改为:

fastcgi_pass 127.0.0.1:9000;

Socket 方式对应:

fastcgi_pass unix:/run/php-fpm/www.sock;

其余 FastCGI 参数保留完整配置中的设置。

PHP-FPM 配置

; /etc/php-fpm.d/www.conf

; 端口方式
; listen = 127.0.0.1:9000

; Socket 方式(推荐)
listen = /run/php-fpm/www.sock
listen.acl_users = apache,nginx
listen.mode = 0660

架构选择建议

场景 推荐方式
单机部署(Nginx + PHP-FPM 同机) Unix Socket,性能更好
分布式部署(Nginx 和 PHP-FPM 分离) TCP 端口,支持跨机通信
容器化环境(K8s/Docker) TCP 端口或 Socket,取决于网络模型

七、案例实战二:部署 Flask 应用(Python + Gunicorn)

7.1 整体架构

Nginx 接入层代理 Gunicorn 中运行的 Flask 应用

生产环境部署方式:

组件 实验环境(单机) 生产环境(推荐)
Nginx magedu 独立接入层服务器
Gunicorn + Flask magedu 独立应用服务器(可水平扩展)
层级 组件 主机名 IP 地址 端口 作用
接入层 Nginx magedu 192.168.100.10 80 接收用户请求,转发给 Gunicorn
应用层 Gunicorn + Flask magedu 192.168.100.10 8000 WSGI 服务器,运行 Flask 应用

7.2 什么是 Gunicorn?

Gunicorn(Green Unicorn)是 Python 的 WSGI HTTP 服务器,用于运行 Python Web 应用。

类似 Java 中的 Tomcat、PHP 中的 PHP-FPM,Gunicorn 负责接收 Nginx 转发的请求,调用 Flask/Django 应用处理,并返回响应。

Gunicorn 多个 Worker 调用 Flask 应用处理业务

WSGI(Web Server Gateway Interface,Web 服务器网关接口)是 Python Web 应用与 Web 服务器之间的一种标准接口规范。原文将其表述为 Web 服务器(如 Nginx)和应用框架(如 Flask、Django)之间的通信协议。

严格来说,本例由 Gunicorn 实现 WSGI 接口,Nginx 与 Gunicorn 之间使用 HTTP 代理。

7.3 安装 Python 环境

# Rocky Linux 10 安装 Python 和 pip
[root@magedu ~]# dnf install -y python3 python3-pip

# 安装 Gunicorn 和 Flask
[root@magedu ~]# pip3 install gunicorn flask

安装 Flask、Gunicorn 及其依赖的过程

安装完成及 pip 输出

7.4 创建 Flask 应用

# 创建应用目录
[root@magedu ~]# mkdir -p /var/www/flask-app
[root@magedu ~]# cd /var/www/flask-app

# 创建 Flask 应用
[root@magedu ~]# cat > app.py <<'EOF'
#!/usr/bin/env python3
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/')
def hello():
    return jsonify({
        'message': 'Hello from Flask!',
        'status': 'success'
    })

@app.route('/api/user/<int:uid>')
def get_user(uid):
    return jsonify({
        'uid': uid,
        'name': f'User_{uid}',
        'email': f'user{uid}@magedu.com'
    })

@app.route('/api/health')
def health():
    return jsonify({'status': 'ok'})

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000)
EOF

7.5 启动 Gunicorn

# 使用 Gunicorn 启动 Flask 应用(绑定 127.0.0.1:8000)
[root@magedu ~]# cd /var/www/flask-app
[root@magedu ~]# gunicorn -w 4 -b 127.0.0.1:8000 app:app --daemon

# 参数说明:
# -w 4:4 个 Worker 进程
# -b 127.0.0.1:8000:绑定地址和端口
# app:app:模块名:应用实例名
# --daemon:后台运行

# 验证 Gunicorn 是否启动
[root@magedu flask-app]# ss -tnlp | grep 8000
LISTEN 0 2048 127.0.0.1:8000 0.0.0.0:* users:(("gunicorn",pid=10981,fd=3),("gunicorn",pid=10980,fd=3),("gunicorn",pid=10979,fd=3),("gunicorn",pid=10978,fd=3),("gunicorn",pid=10977,fd=3))

7.6 配置 Nginx 代理 Python 应用

[root@magedu ~]# cat > /etc/nginx/conf.d/flask.conf <<'EOF'
server {
    listen 80;
    server_name flask.magedu.com;

    access_log /var/log/nginx/flask_access.log main;
    error_log /var/log/nginx/flask_error.log warn;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

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

[root@magedu ~]# nginx -t
[root@magedu ~]# systemctl reload nginx

7.7 验证 Flask 应用

# 直接测试 Gunicorn
[root@magedu ~]# curl http://127.0.0.1:8000/
{"message":"Hello from Flask!","status":"success"}

# 通过 Nginx 访问
[root@magedu ~]# curl http://flask.magedu.com/api/user/100
{"email":"user100@magedu.com","name":"User_100","uid":100}

# 不依赖域名解析的本机测试方式
[root@magedu flask-app]# curl http://127.0.0.1/api/user/100 -H "Host: flask.magedu.com"
{"email":"user100@magedu.com","name":"User_100","uid":100}

# 浏览器访问 http://flask.magedu.com/
# 应显示 JSON 响应

浏览器经 Nginx 访问 Flask 的 JSON 响应

八、本章小结

8.1 核心知识点速记

Nginx 架构:Master 管 Worker,Worker 处理请求,epoll 高并发,单机数万连。

IO 模型:BIO 一个连接一个线程,select 轮询所有连接,epoll 只通知活跃连接。

location 优先级:= 最高,^~ 次之,~ 正则排第三,最后才是通用匹配。

root vs alias:root 拼接路径,alias 替换路径。

静态 vs 动态模块:静态编译进二进制,生产推荐;动态生成 .so,版本匹配是隐患。

Nginx 与 PHP-FPM:Nginx 接收 HTTP 请求,.php 请求通过 FastCGI 转发给 PHP-FPM。

Nginx 与 Gunicorn:Nginx 接收 HTTP 请求,通过 proxy_pass 转发给 Gunicorn(Python 应用)。

平滑升级:USR2 启动新版本 → WINCH 停止旧 Worker → QUIT 退出旧 Master。

8.2 常用命令速查

操作 命令
语法检查 nginx -t
systemd 重载 systemctl reload nginx
原生重载 nginx -s reload
原生停止 nginx -s stop
原生优雅停止 nginx -s quit
平滑升级 kill -USR2 $(cat /var/run/nginx.pid)
查看版本 nginx -v
查看编译参数(升级前必做) nginx -V
保存编译参数 nginx -V 2>&1 | tee /opt/nginx-build-args.txt

8.3 学完本节应能独立完成

  • 理解 Nginx 的 Master-Worker 进程模型。
  • 理解三种 IO 模型的区别。
  • 源码编译安装 Nginx 并添加第三方模块。
  • 执行 Nginx 平滑升级。
  • 部署 WordPress 站点(Nginx + PHP-FPM + MySQL 8.4)。
  • 部署 Flask 应用(Nginx + Gunicorn + Python)。
  • 配置 Nginx 代理 PHP 和 Python 两种后端。
  • 区分 root 和 alias 的使用场景。

下一节预告

Day 9-2:反向代理与负载均衡。

我们将学习反向代理(转发请求给 Java/PHP/Python 后端)和负载均衡(分发请求到多台后端),让 Nginx 从“静态 Web 服务器”升级为“流量入口”。

评论