目录
[2.1 安装Nginx](#2.1 安装Nginx)
[2.2 启动、停止、重载](#2.2 启动、停止、重载)
[2.3 配置文件结构](#2.3 配置文件结构)
[3.1 什么是虚拟主机?](#3.1 什么是虚拟主机?)
[3.2 实战:配置两个虚拟主机](#3.2 实战:配置两个虚拟主机)
[3.3 server_name的通配符匹配](#3.3 server_name的通配符匹配)
[4.1 什么是反向代理?](#4.1 什么是反向代理?)
[4.2 实战:用Nginx反向代理Node.js应用](#4.2 实战:用Nginx反向代理Node.js应用)
[五、nginx -s reload:平滑重启的秘密](#五、nginx -s reload:平滑重启的秘密)
[5.1 为什么需要平滑重启?](#5.1 为什么需要平滑重启?)
[5.2 reload的完整流程](#5.2 reload的完整流程)
[5.3 reload vs restart vs stop](#5.3 reload vs restart vs stop)
[5.4 可能遇到的问题](#5.4 可能遇到的问题)
一、引言:Nginx为什么这么流行?
如果你去问后端开发或运维工程师"用什么部署Web应用",答案十有八九是Nginx。
它凭什么成为事实标准?几个关键数字可以说明问题:
-
C10k问题的终结者:Nginx采用事件驱动架构,一台服务器就能轻松处理上万个并发连接,而传统Apache在几百个连接时已经吃力
-
内存占用极低:空闲状态下仅占用几MB内存,即使在高并发场景下也远低于同类产品
-
功能三合一:既是Web服务器(托管静态文件)、又是反向代理(转发请求到后端)、还是负载均衡器(分发流量到多台服务器)
简单说:一个软件,解决三个问题,而且全部免费开源。
二、安装与基础管理
2.1 安装Nginx
Ubuntu/Debian系统默认仓库已经包含Nginx,一条命令即可安装:
bash
sudo apt update && sudo apt install nginx -y
CentOS/RHEL需要先启用EPEL仓库:
bash
sudo dnf install epel-release -y # CentOS 8+ / Fedora
sudo dnf install nginx -y
安装完成后验证版本:
bash
nginx -v
2.2 启动、停止、重载
Nginx作为systemd服务运行,用我们第15篇学过的systemctl管理:
bash
sudo systemctl start nginx # 启动
sudo systemctl stop nginx # 停止
sudo systemctl restart nginx # 重启(会中断服务)
sudo systemctl reload nginx # 平滑重载(不中断服务)
sudo systemctl status nginx # 查看状态
sudo systemctl enable nginx # 开机自启
2.3 配置文件结构
Nginx配置文件的默认位置是/etc/nginx/,核心文件是nginx.conf。现代Nginx采用模块化设计,主配置文件通过include引入子目录中的配置片段:
bash
/etc/nginx/
├── nginx.conf # 主配置文件
├── sites-available/ # 所有可用的站点配置
│ ├── default
│ └── example.com
└── sites-enabled/ # 已启用的站点(软链接到sites-available)
└── example.com -> ../sites-available/example.com
约定 :把每个站点的配置单独放在sites-available/下,需要启用时创建一个软链接到sites-enabled/。这样需要下线某个站点时,只删除软链接即可,原配置文件保留备用。
每次修改配置后,先测试语法再重载:
bash
sudo nginx -t # 测试配置语法
sudo systemctl reload nginx # 确认无误后平滑重载
三、虚拟主机:一台服务器跑多个网站
3.1 什么是虚拟主机?
在Nginx出现之前,一个域名对应一个IP、一台服务器只能跑一个网站。Nginx的虚拟主机(Server Block) 打破了这种绑定:一台服务器可以托管多个域名,每个域名对应不同的网站目录。
Nginx通过server_name指令来判断当前请求的域名,然后匹配到对应的server块处理。
3.2 实战:配置两个虚拟主机
假设你有一台服务器,需要托管两个网站:blog.example.com对应博客,shop.example.com对应商城。
第一步:创建网站目录和首页
bash
sudo mkdir -p /var/www/blog.example.com
sudo mkdir -p /var/www/shop.example.com
# 创建示例首页
echo '<h1>Welcome to Blog</h1>' | sudo tee /var/www/blog.example.com/index.html
echo '<h1>Welcome to Shop</h1>' | sudo tee /var/www/shop.example.com/index.html
# 设置权限
sudo chown -R www-data:www-data /var/www/blog.example.com
sudo chown -R www-data:www-data /var/www/shop.example.com
第二步:创建虚拟主机配置文件
/etc/nginx/sites-available/blog.example.com:
nginx
server {
listen 80;
server_name blog.example.com;
root /var/www/blog.example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
/etc/nginx/sites-available/shop.example.com:
nginx
server {
listen 80;
server_name shop.example.com;
root /var/www/shop.example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
关键指令解释:
-
listen 80:监听HTTP的默认80端口 -
server_name blog.example.com:这个server块只处理域名匹配的请求 -
root /var/www/blog.example.com:网站文件的根目录 -
index index.html:默认首页文件名 -
try_files $uri $uri/ =404:先尝试匹配请求的文件路径,找不到返回404
第三步:启用站点并重载
bash
# 创建软链接启用站点
sudo ln -s /etc/nginx/sites-available/blog.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/shop.example.com /etc/nginx/sites-enabled/
# 测试语法
sudo nginx -t
# 平滑重载
sudo systemctl reload nginx
现在访问http://blog.example.com和http://shop.example.com,会看到不同的网站内容。
3.3 server_name的通配符匹配
server_name支持通配符,这在管理大量子域名时非常方便:
nginx
# 匹配所有 example.com 的子域名
server {
listen 80;
server_name *.example.com;
...
}
# 精确匹配多个域名
server {
listen 80;
server_name example.com www.example.com;
...
}
匹配优先级 :精确名称 > *开头的通配符 > *结尾的通配符 > 正则表达式。
四、反向代理:Nginx的第二重身份
4.1 什么是反向代理?
先理解正向代理:你的浏览器 → 代理服务器 → 目标网站。你主动找代理,让它帮你访问外界。
反向代理正好反过来:用户的请求 → Nginx → 内部应用服务器。用户根本不知道后面还有谁,他只和Nginx对话。
反向代理的好处:
-
隐藏后端:内部应用服务器不需要暴露公网IP
-
统一入口:多个应用(Node.js/Python/Go)通过同一个80端口对外服务
-
SSL终结:在Nginx处理HTTPS,内部服务器只需处理HTTP
4.2 实战:用Nginx反向代理Node.js应用
假设你有一个Node.js应用运行在本地3000端口,想让外部用户通过app.example.com访问,而用户不知道3000端口的存在。
nginx
server {
listen 80;
server_name app.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;
}
}
逐行解读:
-
proxy_pass http://127.0.0.1:3000:核心指令------把匹配到的请求全部转发到本机的3000端口 -
proxy_set_header Host $host:把原始请求的域名传给后端(否则后端收到的Host是127.0.0.1) -
proxy_set_header X-Real-IP $remote_addr:把用户的真实IP传给后端(否则后端只能看到127.0.0.1) -
proxy_set_header X-Forwarded-For:记录完整的代理链,用户的真实IP会追加到这个头部 -
proxy_set_header X-Forwarded-Proto $scheme:告诉后端原始请求是HTTP还是HTTPS
proxy_pass的斜杠陷阱:
proxy_pass中地址末尾有没有斜杠,行为截然不同:
| 配置 | 请求 /api/users |
转发地址 |
|---|---|---|
proxy_pass http://backend/ |
/api/users |
http://backend/users |
proxy_pass http://backend |
/api/users |
http://backend/api/users |
加上末尾斜杠,location中匹配到的路径会被移除;不加斜杠,完整URI原样追加。这个细节经常让人踩坑。
五、nginx -s reload:平滑重启的秘密
5.1 为什么需要平滑重启?
传统的systemctl restart nginx会先停止所有worker进程,再启动新的。这会导致一个短暂的"真空期"------此时Nginx无法处理任何请求。
对于生产环境,哪怕中断0.1秒都可能导致部分用户请求失败。nginx -s reload就是为解决这个问题设计的。
5.2 reload的完整流程
nginx -s reload向master进程发送SIGHUP信号,master进程随后执行以下步骤:
第一步:检查配置语法
master进程首先对新配置文件做语法检查。如果检查失败,继续使用旧配置运行,服务不受任何影响。
第二步:打开新的监听端口
如果新配置中增加了之前没有的监听端口(比如新增了443的SSL配置),master进程会打开这些新端口。所有worker进程作为master的子进程,会继承这些端口。
第三步:启动新worker进程
master用新配置 启动一批新的worker进程。注意:此时新老worker同时存在,新连接由新worker处理,老worker继续处理已建立的请求。
第四步:通知老worker优雅退出
master向老worker发送SIGQUIT信号。老worker收到后:
-
关闭监听的端口句柄(不再接收新连接)
-
处理完当前正在进行的请求
-
主动退出
示意图:
text
reload前:[老worker-1] [老worker-2] [老worker-3] [老worker-4]
↓ master收到reload信号 ↓
过渡期: [老worker-1] [老worker-2] [老worker-3] [老worker-4]
[新worker-1] [新worker-2] [新worker-3] [新worker-4]
↓ 老worker处理完已有请求后退出 ↓
reload后:[新worker-1] [新worker-2] [新worker-3] [新worker-4]
5.3 reload vs restart vs stop
| 命令 | 底层信号 | 行为 | 是否中断服务 |
|---|---|---|---|
nginx -s reload |
SIGHUP | 新配置启动新worker,老worker优雅退出 | 否 |
nginx -s stop |
SIGTERM | 立即停止所有worker | 是 |
nginx -s quit |
SIGQUIT | 优雅停止:处理完当前请求后退出 | 只处理完已有请求 |
systemctl restart |
先stop再start | 完全停止再启动 | 是 |
日常修改配置后,永远优先使用nginx -s reload或systemctl reload nginx。
5.4 可能遇到的问题
老worker长时间不退出:某些请求可能处理得很慢(如大文件上传),导致老worker一直等待。新版本Nginx提供了worker_shutdown_timeout指令,可以设置最长等待时间,超时后强制退出。
六、本篇小结
安装与管理:
bash
sudo apt install nginx
sudo nginx -t # 测试配置
sudo systemctl reload nginx # 平滑重载
虚拟主机核心配置:
nginx
server {
listen 80;
server_name example.com; # 域名匹配[citation:5]
root /var/www/example.com; # 网站根目录
index index.html; # 默认首页
}
反向代理核心配置:
nginx
location / {
proxy_pass http://127.0.0.1:3000; # 转发目标
proxy_set_header Host $host; # 传递域名
proxy_set_header X-Real-IP $remote_addr; # 传递真实IP
}
平滑重启原理 :
master进程收到reload信号 → 检查语法 → 开新端口 → 启新worker → 老worker优雅退出 → 全程不中断服务
动手练习
bash
# 1. 安装并启动Nginx
sudo apt update && sudo apt install nginx -y
sudo systemctl start nginx
# 2. 查看默认页面(本机访问)
curl http://localhost
# 3. 创建自己的虚拟主机配置
sudo mkdir -p /var/www/test.local
echo "My Test Site" | sudo tee /var/www/test.local/index.html
sudo vim /etc/nginx/sites-available/test.local
# 填入server块配置(listen 80; server_name test.local; root /var/www/test.local)
sudo ln -s /etc/nginx/sites-available/test.local /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# 4. 本机测试(修改hosts或直接用curl指定Host头)
curl -H "Host: test.local" http://localhost
# 5. 观察worker进程变化
ps aux | grep nginx
# 修改个配置,执行reload,再次查看worker进程(PID应该变化)
排错小技巧
502 Bad Gateway :后端服务挂了或proxy_pass地址写错了,检查后端是否在运行、端口是否匹配。
404 Not Found :root路径不对或文件不存在,确认网站目录下有首页文件。
403 Forbidden :权限问题,chown -R www-data:www-data改属主。
七、下篇预告
Nginx常常搭配数据库一起工作。下一篇我们将进入数据库MySQL/MariaDB的安装与基础调优 ,学习数据库的安全初始化流程、创建用户和授权、以及my.cnf中几个必须调整的内存参数。这是完整Web应用栈的第二块基石。
延伸思考 :nginx -s reload为什么比restart更优雅?打个比方:restart相当于饭店关门装修------新客旧客全请出去。reload相当于厨师换菜单------已经在用餐的顾客不受影响,新来的顾客按新菜单点菜。这就是生产环境的"零停机更新"。