📢 写在前面
哈喽各位小伙伴!👋 今天这篇绝对是 Web运维入门到进阶的硬核干货!
你是不是也遇到过这些问题:
- 😫 装完Nginx只会放个静态页面,虚拟主机、HTTPS、反向代理完全不会配?
- 😱 location匹配规则记不住,配完404完全不知道为什么?
- 🤯 proxy_pass后面带不带斜杠
/有什么区别?踩坑无数?- 🤔 负载均衡的轮询、权重、ip_hash、least_conn到底怎么选?
别慌!今天这篇文章我将用 最通俗的语言 + 最详细的命令注释 ,带你从零掌握Nginx六大核心实验:
基础部署 → 虚拟主机 → HTTPS加密 → Basic认证 → PHP动态站点 → 反向代理 → 七层负载均衡
每一行命令都给你掰碎了讲,小白也能轻松跟上~建议先收藏⭐再看!
📚 全文知识地图
| 模块 | 核心内容 | 运维实战价值 | 难度指数 |
|---|---|---|---|
| Nginx基础 | 架构原理、master-worker、热重载、常用命令 | 理解Nginx为什么快,面试高频考点 | ⭐⭐ |
| 基础部署 | 安装、静态站点、防火墙放行 | 最基础的Web服务搭建 | ⭐ |
| 虚拟主机 | 基于域名、基于端口 | 一台服务器跑多个网站,节约成本 | ⭐⭐ |
| HTTPS/SSL | openssl生成证书、443配置、301跳转 | 生产网站标配,防抓包防劫持 | ⭐⭐⭐ |
| Basic认证 | htpasswd生成密码、auth_basic配置 | 后台管理页面简单加密防护 | ⭐⭐ |
| PHP站点 | php-fpm安装、FastCGI配置 | 动静分离架构,PHP网站标准部署 | ⭐⭐⭐ |
| 反向代理 | location匹配、proxy_pass、路径重写 | 隐藏后端服务,统一入口,企业标配 | ⭐⭐⭐⭐ |
| 七层负载均衡 | upstream、轮询/权重/ip_hash/least_conn | 集群流量分发,高并发必备 | ⭐⭐⭐⭐ |
💡 小贴士 :本文所有命令都在 CentOS 7 环境下验证通过,所有主机名中的
laogao已统一替换为jhl,放心抄作业!
🧠 第一部分:Nginx核心原理(先懂原理再动手)
🤔 1.1 Nginx是什么?
Nginx(发音:engine-x)是一款 高性能、事件驱动的开源Web服务器,同时也可做反向代理、负载均衡、HTTP缓存、静态资源服务器。由俄罗斯工程师 Igor Sysoev 开发,现在是互联网主流服务组件。
🎯 核心数据 :在高连接并发情况下,能够支持高达 5万个并发连接 的响应,而内存、CPU等系统资源消耗却非常低,运行非常稳定。
⚡ 1.2 Nginx三大核心特点
特点1:事件驱动模型(epoll/kqueue)
采用 异步非阻塞IO,单进程可以处理大量并发连接,高并发消耗资源少,万级并发很轻松,远优于传统Apache多进程模型。
| 对比项 | 传统Apache | Nginx |
|---|---|---|
| 模型 | 一连接一进程/线程 | 少量worker进程 + IO多路复用 |
| 1万并发 | 需要1万个进程,内存暴涨 | 一个worker同时处理上万个连接 |
| 资源消耗 | 上下文切换开销巨大 | 大部分连接空闲等待,几乎不消耗CPU |
特点2:轻量
占用内存、CPU低,部署简单,配置文件简洁。
特点3:多角色能力(不止Web服务器)
| 角色 | 说明 |
|---|---|
| Web服务器 | 直接托管静态文件(html/js/css/图片) |
| 反向代理 | 把客户端请求转发给后端服务(Tomcat、SpringBoot、Node、PHP-FPM) |
| 负载均衡 | 多台后端服务器分发流量,做集群 |
| HTTP缓存 | 缓存后端响应,减轻后端压力 |
| SSL/TLS终端 | 处理HTTPS证书解密加密 |
| 其他 | 限流、防盗链、gzip压缩、访问控制、虚拟主机 |
🏗️ 1.3 Nginx架构:master-worker多进程模型
Nginx是多进程模型,采用 master-worker架构:
| 进程 | 运行权限 | 核心工作 | 是否处理业务请求 |
|---|---|---|---|
| master主进程 | root | 读取解析配置文件、管理所有worker子进程、接收系统信号(启动/停止/reload/重开日志) | ❌ 只做管理 |
| worker工作进程 | nginx | 真正处理网络请求,执行TCP连接、HTTP解析、转发、读写文件 | ✅ 全部业务逻辑 |
| cache loader/manager | nginx | cache manager管理磁盘缓存清理过期文件;cache loader启动时加载缓存元数据 | ❌ 缓存管理(可选) |
💡 建议配置 :
worker_processes auto;,自动等于CPU物理核心数。worker之间互相独立,不共享内存。
🔄 1.4 热重载原理(nginx -s reload)
这是Nginx最牛的特性之一,更新配置不中断服务!
1. master读取新配置,校验语法
↓
2. 启动一批新worker进程,新worker加载新配置处理新请求
↓
3. 向旧worker发送信号,旧worker不再接收新连接,处理完已有连接后退出
↓
✅ 整个过程服务不中断,无宕机
📋 1.5 Nginx常用命令
| 命令 | 作用 |
|---|---|
nginx |
启动Nginx |
nginx -s stop |
快速停止(强制终止) |
nginx -s quit |
优雅停止,处理完现有连接再退出 |
nginx -s reload |
重载配置(最常用,热重载不中断服务) |
nginx -t |
校验配置文件语法是否正确(改完配置必执行!) |
🚀 第二部分:实验一 Nginx基础部署与静态站点
🤔 为什么要做这个实验?运维作用
这是所有Web服务的基础。学会安装Nginx、配置静态站点、放行防火墙,才能在此基础上做虚拟主机、HTTPS、反向代理等进阶功能。线上90%的网站前端都是Nginx。
💡 实验完整思路
- 两台机器配置主机名和静态IP(一台服务端、一台客户端);
- 服务端配置阿里EPEL源(CentOS7默认源没有Nginx);
- yum安装Nginx;
- 设置开机自启并启动;
- 替换默认首页为自定义内容;
- 防火墙放行HTTP 80端口;
- 客户端配置hosts域名解析,curl访问测试。
📋 节点规划
| 节点名称 | 节点IP | 作用 |
|---|---|---|
| nginx-server | 10.1.8.10/24 | 搭建Nginx服务端 |
| nginx-client | 10.1.8.11/24 | 客户端访问测试 |
🔧 操作命令(逐行注释)
步骤1:nginx-server基础配置
bash
# ============================================================
# hostnamectl set-hostname:永久修改系统主机名,重启后永久生效
# 参数:nginx-server 为新主机名
# ============================================================
[root@localhost ~]# hostnamectl set-hostname nginx-server
# ============================================================
# nmcli connection modify:修改网卡连接配置
# ens33:网卡设备名
# ipv4.method manual:关闭DHCP,启用手动静态IP模式
# ipv4.addresses 10.1.8.10/24:设置IPv4地址,/24表示24位子网掩码
# ipv4.gateway 10.1.8.2:默认网关地址
# ipv4.dns 10.1.8.2:DNS服务器地址
# autoconnect yes:开机自动激活网卡
# ============================================================
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.10/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
# ============================================================
# nmcli connection up:激活(重载)网卡连接,使配置立即生效,无需重启
# ============================================================
[root@localhost ~]# nmcli connection up ens33
步骤2:nginx-client基础配置
bash
[root@localhost ~]# hostnamectl set-hostname nginx-client
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.11/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
[root@localhost ~]# nmcli connection up ens33
步骤3:安装Nginx(服务端)
bash
# ============================================================
# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
# 下载阿里云EPEL软件源到yum源配置目录
# -O:指定下载文件的输出路径(大写O)
# CentOS7默认base源没有Nginx,必须先配置EPEL源
# ============================================================
[root@nginx-server ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
# ============================================================
# yum -y install nginx
# yum在线安装Nginx
# -y:自动确认所有交互提示,无需手动输入y
# ============================================================
[root@nginx-server ~]# yum -y install nginx
步骤4:启动Nginx
bash
# ============================================================
# systemctl enable nginx --now
# enable:设置Nginx开机自启
# --now:同时立即启动服务(不用再单独执行start)
# ============================================================
[root@nginx-server ~]# systemctl enable nginx --now
步骤5:准备自定义首页
bash
# ============================================================
# mv /usr/share/nginx/html/index.html{,.ori}
# 备份默认首页,大括号扩展等价于:
# mv /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.ori
# {,.ori} 表示在原文件名后加.ori后缀
# ============================================================
[root@nginx-server ~]# mv /usr/share/nginx/html/index.html{,.ori}
# ============================================================
# echo Hello World From Nginx > /usr/share/nginx/html/index.html
# 将文本写入新的首页文件
# >:覆盖写入(如果文件存在则覆盖)
# ============================================================
[root@nginx-server ~]# echo Hello World From Nginx > /usr/share/nginx/html/index.html
步骤6:防火墙放行HTTP
bash
# ============================================================
# firewall-cmd --add-service=http --permanent
# 永久放行HTTP服务(对应80端口)
# --add-service:按服务名称放行(http对应TCP 80)
# --permanent:永久写入防火墙规则,重启后不丢失
# ============================================================
[root@nginx-server ~]# firewall-cmd --add-service=http --permanent
# ============================================================
# firewall-cmd --reload
# 重载防火墙配置,使永久规则立即生效
# ============================================================
[root@nginx-server ~]# firewall-cmd --reload
步骤7:客户端测试访问
bash
# ============================================================
# 配置域名解析(Windows客户端修改 C:\Windows\System32\drivers\etc\hosts)
# Linux客户端修改 /etc/hosts
# 添加如下记录:将www.jhl.cloud解析到10.1.8.10
# ============================================================
[root@nginx-client ~]# vim /etc/hosts
# 添加:10.1.8.10 www.jhl.cloud
# ============================================================
# curl http://www.jhl.cloud
# 通过域名访问Nginx站点,输出页面内容
# ============================================================
[root@nginx-client ~]# curl http://www.jhl.cloud
Hello World From Nginx
✅ 实验现象解释 :curl输出
Hello World From Nginx,说明Nginx安装成功、静态站点正常工作、防火墙80端口已放行、域名解析正确。
⚙️ 第三部分:Nginx配置文件详解
📂 配置文件路径
主配置文件:/etc/nginx/nginx.conf
🏗️ 配置结构(五层嵌套)
Nginx配置采用层级化、模块化的组织方式:
全局块 → events块 → http块 → server块 → location块
| 配置块 | 作用范围 | 核心配置 |
|---|---|---|
| 全局块 | 整个Nginx实例 | user、worker_processes、error_log、pid |
| events块 | 网络连接、并发参数 | worker_connections、use epoll、multi_accept |
| http块 | 所有HTTP/HTTPS服务共用 | mime.types、日志格式、sendfile、keepalive_timeout |
| server块 | 单个虚拟主机(站点) | listen、server_name、root、error_page |
| location块 | 站点内不同URI路径规则 | proxy_pass、root、alias、try_files |
1. 全局配置块示例
nginx
user nginx; # 运行Nginx的用户/用户组
worker_processes auto; # 工作进程数(核心参数,建议设为CPU核心数)
error_log /var/log/nginx/error.log; # 错误日志路径
pid /run/nginx.pid; # 主进程PID文件路径
include /usr/share/nginx/modules/*.conf; # 加载外部模块配置(全局级引入)
2. events块(核心模块)
nginx
events {
worker_connections 1024; # 单个worker最大并发连接数
use epoll; # IO多路复用模型,Linux默认epoll
multi_accept on; # 允许一个进程一次性接受多个新连接
}
3. http块(业务模块)
nginx
http {
include /etc/nginx/mime.types; # 加载MIME类型映射
default_type application/octet-stream; # 默认响应类型
log_format main '$remote_addr - $remote_user [$time_local] "$request"'; # 日志格式
access_log /var/log/nginx/access.log main; # 访问日志
sendfile on; # 高效发送文件,静态文件性能优化
keepalive_timeout 65; # HTTP长连接超时(秒)
# server块:虚拟主机配置(一个http块可包含多个server)
server {
listen 80; # 监听端口(80=HTTP,443=HTTPS)
server_name localhost; # 域名/IP(可配置多个,空格分隔)
root /usr/share/nginx/html; # 网站根目录
# location块:匹配URI路径,处理请求
location / {
index index.html index.htm; # 默认首页
try_files $uri $uri/ /index.html; # 路径匹配规则
}
# 错误页面配置
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
}
}
4. HTTPS专属配置块
nginx
server {
listen 443 ssl; # 监听HTTPS端口并启用SSL
server_name example.com;
ssl_certificate /etc/nginx/cert/server.crt; # 公钥文件
ssl_certificate_key /etc/nginx/cert/server.key; # 私钥文件
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
}
🎯 location匹配规则(优先级从高到低)
| 优先级 | 语法 | 说明 |
|---|---|---|
| 1(最高) | location = /uri |
精确匹配,完全一致才命中 |
| 2 | location ^~ /uri |
前缀匹配,命中后不执行正则 |
| 3 | location ~ regex |
大小写敏感正则匹配 |
| 4 | location ~* regex |
大小写不敏感正则匹配 |
| 5 | location /uri |
普通前缀匹配 |
| 6(最低) | location / |
默认兜底匹配 |
📦 Nginx内置常用变量
| 变量 | 含义 |
|---|---|
$uri |
请求的URI,不带参数 |
$args |
URL后面的GET查询参数 |
$request_uri |
完整URI + 参数 |
$remote_addr |
客户端真实IP |
$http_host |
请求头Host |
$http_user_agent |
客户端浏览器UA |
$scheme |
协议 http/https |
$server_name |
当前server的域名 |
$request_method |
请求方法 GET/POST |
🌐 第四部分:实验二 虚拟主机(一台服务器跑多个网站)
🤔 为什么要做这个实验?运维作用
虚拟主机让一台服务器可以托管多个独立网站,大大节约硬件成本。企业官网、博客、后台系统可以共用一台Nginx服务器。
💡 实验思路
虚拟主机支持三种方式:
- 基于主机名(域名):最常用,不同域名区分站点;
- 基于端口号:同一域名不同端口区分站点;
- 基于IP地址:不同IP区分站点(基本不用)。
🔧 2.1 基于名称的虚拟主机
实验思路
- 复制主配置模板到conf.d目录(模块化管理);
- 编写两个server块,不同server_name对应不同root目录;
- 创建两个网站根目录和首页;
- 重启Nginx;
- 客户端配置hosts,分别curl两个域名验证。
bash
# ============================================================
# cp /etc/nginx/nginx.conf /etc/nginx/conf.d/vhost-name.conf
# 复制主配置文件作为虚拟主机配置模板
# /etc/nginx/conf.d/ 目录下的.conf文件会被主配置的include自动加载
# ============================================================
[root@nginx-server ~]# cp /etc/nginx/nginx.conf /etc/nginx/conf.d/vhost-name.conf
# ============================================================
# vim 编辑虚拟主机配置文件
# ============================================================
[root@nginx-server ~]# vim /etc/nginx/conf.d/vhost-name.conf
配置文件内容:
nginx
# 第一个虚拟主机:web1.jhl.cloud
server {
server_name web1.jhl.cloud; # 匹配域名web1.jhl.cloud
root /usr/share/nginx/web1; # 网站根目录
}
# 第二个虚拟主机:web2.jhl.cloud
server {
server_name web2.jhl.cloud; # 匹配域名web2.jhl.cloud
root /usr/share/nginx/web2; # 网站根目录
}
bash
# ============================================================
# mkdir /usr/share/nginx/web{1,2}
# 大括号扩展,同时创建web1和web2两个目录
# 等价于 mkdir /usr/share/nginx/web1 /usr/share/nginx/web2
# ============================================================
[root@nginx-server ~]# mkdir /usr/share/nginx/web{1,2}
# 分别写入两个站点的首页
[root@nginx-server ~]# echo web1.jhl.cloud > /usr/share/nginx/web1/index.html
[root@nginx-server ~]# echo web2.jhl.cloud > /usr/share/nginx/web2/index.html
# ============================================================
# systemctl restart nginx
# 重启Nginx使配置生效
# (生产环境推荐用 nginx -s reload 热重载,不中断服务)
# ============================================================
[root@nginx-server ~]# systemctl restart nginx
客户端测试
bash
# 配置名称解析,假设web服务器IP为10.1.8.10
[root@nginx-client ~]# vim /etc/hosts
# 添加:10.1.8.10 web1.jhl.cloud web2.jhl.cloud
# 访问web1
[root@nginx-client ~]# curl http://web1.jhl.cloud/
web1.jhl.cloud
# 访问web2
[root@nginx-client ~]# curl http://web2.jhl.cloud/
web2.jhl.cloud
✅ 实验现象:访问不同域名返回不同页面内容,说明基于名称的虚拟主机配置成功。
清理环境(避免影响后续实验)
bash
[root@nginx-server ~]# mkdir /etc/nginx/conf.d/vhosts
[root@nginx-server ~]# mv /etc/nginx/conf.d/vhost-name.conf /etc/nginx/conf.d/vhosts
🔧 2.2 基于端口的虚拟主机
实验思路
- 编写两个server块,相同server_name但不同listen端口;
- 创建对应目录和首页;
- 重启Nginx;
- 客户端通过不同端口访问验证。
bash
[root@nginx-server ~]# vim /etc/nginx/conf.d/vhost-port.conf
配置文件内容:
nginx
# 第一个站点:监听8081端口
server {
listen 8081; # 监听8081端口
server_name www.jhl.cloud; # 域名
root /usr/share/nginx/8081; # 网站根目录
}
# 第二个站点:监听8082端口
server {
listen 8082; # 监听8082端口
server_name www.jhl.cloud; # 相同域名
root /usr/share/nginx/8082; # 不同根目录
}
bash
# 创建目录(大括号扩展创建8081和8082)
[root@nginx-server ~]# mkdir /usr/share/nginx/808{1,2}
# 写入首页
[root@nginx-server ~]# echo 8081 > /usr/share/nginx/8081/index.html
[root@nginx-server ~]# echo 8082 > /usr/share/nginx/8082/index.html
# 重启Nginx
[root@nginx-server ~]# systemctl restart nginx
客户端测试
bash
# 配置域名解析
[root@nginx-client ~]# vim /etc/hosts
# 添加:10.1.8.10 www.jhl.cloud
# 访问8081端口
[root@nginx-client ~]# curl http://www.jhl.cloud:8081
8081
# 访问8082端口
[root@nginx-client ~]# curl http://www.jhl.cloud:8082
8082
✅ 实验现象:同一域名不同端口返回不同页面,基于端口的虚拟主机配置成功。
⚠️ 注意:生产环境优先使用基于域名的虚拟主机,多端口需要用户手动输入端口,体验差。
清理环境
bash
[root@nginx-server ~]# mv /etc/nginx/conf.d/vhost-port.conf /etc/nginx/conf.d/vhosts
🔒 第五部分:实验三 配置SSL/TLS(HTTPS加密)
🤔 为什么要做这个实验?运维作用
HTTPS = HTTP + SSL/TLS,加密传输防止数据被抓包篡改,是生产网站标配。浏览器对HTTP站点会标记"不安全",小程序、微信公众号也要求HTTPS。
💡 实验思路
- 使用openssl生成私钥(key);
- 生成证书请求文件(csr);
- 使用私钥自签名生成公钥证书(crt);
- 将证书文件移动到标准目录;
- 配置Nginx监听443端口,指定证书路径;
- 配置80端口301永久重定向到HTTPS;
- 防火墙放行HTTPS 443端口;
- 客户端curl测试(-k忽略自签名证书警告,-L跟随重定向)。
🔧 步骤1:生成证书
bash
# ============================================================
# mkdir certs && cd certs
# 创建证书目录并进入该目录
# &&:前一条命令成功才执行后一条
# ============================================================
[root@nginx-server ~]# mkdir certs && cd certs
# ============================================================
# openssl genrsa -out www.key 2048
# 生成RSA私钥
# genrsa:生成RSA私钥子命令
# -out www.key:输出私钥文件名为www.key
# 2048:密钥长度2048位(越长越安全,性能开销越大)
# ============================================================
[root@nginx-server certs]# openssl genrsa -out www.key 2048
# ============================================================
# openssl req -new -key www.key -out www.csr -subj "/C=CN/ST=JS/L=NJ/O=LG/OU=DEVOPS/CN=www.jhl.cloud/emailAddress=webadmin@jhl.cloud"
# 生成证书签名请求文件(CSR)
# req:证书请求子命令
# -new:生成新的证书请求
# -key www.key:使用刚才生成的私钥
# -out www.csr:输出CSR文件
# -subj:一次性填写证书信息(免交互)
# /C=CN:国家(China)
# /ST=JS:省份(JiangSu)
# /L=NJ:城市(NanJing)
# /O=LG:组织名称
# /OU=DEVOPS:部门名称
# /CN=www.jhl.cloud:通用名称(必须是网站域名!最重要)
# /emailAddress=webadmin@jhl.cloud:管理员邮箱
# ============================================================
[root@nginx-server certs]# openssl req -new -key www.key -out www.csr -subj "/C=CN/ST=JS/L=NJ/O=LG/OU=DEVOPS/CN=www.jhl.cloud/emailAddress=webadmin@jhl.cloud"
# ============================================================
# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
# 使用自己的私钥对CSR签名,生成自签名证书
# x509:X.509证书子命令
# -req:输入是CSR请求文件
# -days 3650:证书有效期3650天(10年)
# -in www.csr:输入CSR文件
# -signkey www.key:使用私钥签名
# -out www.crt:输出公钥证书文件
# ============================================================
[root@nginx-server certs]# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
📌 三个文件的作用:
www.key:私钥(必须保密!)www.csr:证书请求(中间文件,提交给CA签名用)www.crt:公钥证书(配置HTTPS用,发给客户端)
🔧 步骤2:配置HTTPS站点
bash
# 创建证书存放目录
[root@nginx-server certs]# mkdir /etc/ssl/certs/www.jhl.cloud
# 将所有证书相关文件移动到标准目录
[root@nginx-server certs]# mv www* /etc/ssl/certs/www.jhl.cloud
# 复制主配置模板
[root@nginx-server ~]# cp /etc/nginx/nginx.conf /etc/nginx/conf.d/vhost-www.jhl.cloud-ssl.conf
# 编辑配置文件
[root@nginx-server ~]# vim /etc/nginx/conf.d/vhost-www.jhl.cloud-ssl.conf
配置文件内容(HTTPS站点):
nginx
server {
listen 443 ssl http2; # 监听443端口,启用SSL和HTTP/2
listen [::]:443 ssl http2; # IPv6的443端口
server_name www.jhl.cloud; # 网站域名
root /usr/share/nginx/html; # 网站根目录
# 证书(公钥)
ssl_certificate "/etc/ssl/certs/www.jhl.cloud/www.crt";
# 私钥(必须保密)
ssl_certificate_key "/etc/ssl/certs/www.jhl.cloud/www.key";
}
bash
# 重启Nginx
[root@nginx-server ~]# systemctl restart nginx
# 防火墙放行HTTPS
[root@nginx-server ~]# firewall-cmd --add-service=https --permanent
[root@nginx-server ~]# firewall-cmd --reload
🔧 步骤3:配置HTTP强制跳转HTTPS
编辑同一个配置文件,增加80端口的server块:
nginx
# HTTPS站点
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.jhl.cloud;
root /usr/share/nginx/html;
ssl_certificate "/etc/ssl/certs/www.jhl.cloud/www.crt";
ssl_certificate_key "/etc/ssl/certs/www.jhl.cloud/www.key";
}
# 配置HTTP重定向到HTTPS
server {
listen 80;
listen [::]:80;
server_name www.jhl.cloud;
root /usr/share/nginx/html;
# 添加重定向:301永久重定向到HTTPS
# $host:客户端请求的域名;$request_uri:完整请求URI+参数
return 301 https://$host$request_uri;
}
bash
[root@nginx-server ~]# systemctl restart nginx
[root@nginx-server ~]# firewall-cmd --add-service=https --permanent
[root@nginx-server ~]# firewall-cmd --reload
🔧 步骤4:客户端测试
bash
# 测试HTTP访问(应该返回301跳转页面)
[root@nginx-client ~]# curl http://www.jhl.cloud/
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>
# ============================================================
# curl -k https://www.jhl.cloud
# -k:忽略自签名证书不安全警告(生产环境用正规CA证书则不需要)
# ============================================================
[root@nginx-client ~]# curl -k https://www.jhl.cloud
Hello World From Nginx
# ============================================================
# curl -Lk http://www.jhl.cloud
# -L:跟随301重定向(自动跳转到HTTPS)
# -k:忽略证书警告
# ============================================================
[root@nginx-client ~]# curl -Lk http://www.jhl.cloud
Hello World From Nginx
✅ 实验现象解释:
- 直接访问HTTP返回301跳转页面,说明重定向配置生效;
- 加
-Lk后自动跳转到HTTPS并返回页面内容,说明HTTP→HTTPS跳转+HTTPS站点都正常工作。
🔐 第六部分:实验四 配置Basic基本认证
🤔 为什么要做这个实验?运维作用
给网站的特定路径(如后台管理、监控页面)添加用户名密码认证,防止匿名访问。Basic认证用户名密码使用明文传输,所以最好配合SSL/TLS使用。
💡 实验思路
- 安装httpd-tools(包含htpasswd工具);
- 在HTTPS的server块中添加
/auth-basic/的location,配置auth_basic; - 使用htpasswd生成密码文件;
- 创建测试页面;
- 客户端使用
-u 用户名:密码测试访问。
🔧 操作命令
bash
# ============================================================
# yum -y install httpd-tools
# 安装Apache工具包,包含htpasswd密码生成工具
# ============================================================
[root@nginx-server ~]# yum -y install httpd-tools
编辑HTTPS配置文件,在server块中添加location:
nginx
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.jhl.cloud;
root /usr/share/nginx/html;
ssl_certificate "/etc/ssl/certs/www.jhl.cloud/www.crt";
ssl_certificate_key "/etc/ssl/certs/www.jhl.cloud/www.key";
# ============================================================
# location /auth-basic/:匹配以/auth-basic/开头的路径
# auth_basic "Basic Auth":开启基本认证,弹窗提示文字为"Basic Auth"
# auth_basic_user_file:指定密码文件路径
# ============================================================
location /auth-basic/ {
auth_basic "Basic Auth";
auth_basic_user_file "/etc/nginx/.htpasswd";
}
}
server {
listen 80;
listen [::]:80;
server_name www.jhl.cloud;
root /usr/share/nginx/html;
return 301 https://$host$request_uri;
}
bash
# 重启Nginx
[root@nginx-server ~]# systemctl restart nginx
# ============================================================
# htpasswd -b -c /etc/nginx/.htpasswd jhl 123456
# 生成密码文件
# -b:命令行直接传入密码(batch模式,免交互)
# -c:创建新文件(如果文件已存在会覆盖!)
# /etc/nginx/.htpasswd:密码文件路径
# jhl:用户名
# 123456:密码
# ============================================================
[root@nginx-server ~]# htpasswd -b -c /etc/nginx/.htpasswd jhl 123456
# 创建认证页面目录
[root@nginx-server ~]# mkdir /usr/share/nginx/html/auth-basic
# 创建测试页面
[root@nginx-server ~]# vim /usr/share/nginx/html/auth-basic/index.html
测试页面内容:
html
<html>
<body>
<div style="width: 100%; font-size: 40px; font-weight: bold; text-align: center;">
Test Page for Basic Authentication
</div>
</body>
</html>
客户端测试
bash
# ============================================================
# curl -ku jhl:123456 https://www.jhl.cloud/auth-basic/
# -k:忽略自签名证书警告
# -u jhl:123456:指定用户名和密码进行Basic认证
# ============================================================
[root@nginx-client ~]# curl -ku jhl:123456 https://www.jhl.cloud/auth-basic/
<html>
<body>
<div style="width: 100%; font-size: 40px; font-weight: bold; text-align: center;">
Test Page for Basic Authentication
</div>
</body>
</html>
✅ 实验现象:带正确用户名密码访问返回测试页面;不带密码或密码错误会返回401 Unauthorized。
🐘 第七部分:实验五 PHP动态站点
🤔 为什么要做这个实验?运维作用
Nginx本身只能处理静态文件,PHP等动态页面需要转发给PHP-FPM处理。这是 动静分离架构 的基础:Nginx处理静态资源(html/css/js/图片),PHP-FPM处理动态PHP代码。
💡 实验思路
- 安装php和php-fpm(php-fpm负责接收Web发来的PHP代码,php负责解析执行);
- 安装PHP常用扩展(gd、mbstring、mcrypt等);
- 启动php-fpm服务(默认监听9000端口);
- 准备phpinfo测试页面;
- 在Nginx配置中添加
location ~ \.php$,将PHP请求转发给127.0.0.1:9000; - 客户端访问info.php验证PHP解析正常。
🔧 步骤1:安装PHP和PHP-FPM
bash
# ============================================================
# yum install -y php php-fpm
# php:PHP解析器,负责解析和执行PHP代码
# php-fpm:PHP FastCGI进程管理器,负责接收Web程序发来的PHP代码
# 工作流程:Nginx接收请求 → 转发给php-fpm → php-fpm调用php解析 → 返回结果给Nginx → 返回客户端
# ============================================================
[root@nginx-server ~]# yum install -y php php-fpm
# ============================================================
# systemctl enable php-fpm --now
# 设置php-fpm开机自启并立即启动
# php-fpm默认监听127.0.0.1:9000
# ============================================================
[root@nginx-server ~]# systemctl enable php-fpm --now
# 安装常用PHP扩展包
[root@nginx-server ~]# yum install -y php-gd php-common php-pear php-mbstring php-mcrypt
🔧 步骤2:测试PHP是否正常
bash
# 查看PHP版本
[root@nginx-server ~]# php -v
# 直接运行一段PHP代码
[root@nginx-server ~]# php -r "echo 'Hello PHP';"
Hello PHP
# 运行一个PHP文件
[root@nginx-server ~]# echo "<?php echo 'PHP Test Page'.\"\n\"; ?>" > php_test.php
[root@nginx-server ~]# php php_test.php
PHP Test Page
# 准备phpinfo测试页面(查看PHP详细配置信息)
[root@nginx-server ~]# echo "<?php phpinfo(); ?>" > /usr/share/nginx/html/info.php
🔧 步骤3:配置Nginx支持PHP
编辑HTTPS配置文件,在443的server块中添加PHP的location:
nginx
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.jhl.cloud;
root /usr/share/nginx/html;
ssl_certificate "/etc/ssl/certs/www.jhl.cloud/www.crt";
ssl_certificate_key "/etc/ssl/certs/www.jhl.cloud/www.key";
# ============================================================
# location ~ \.php$:正则匹配所有以.php结尾的URL
# ~:大小写敏感正则匹配
# \.php$:\.转义点号,$表示以.php结尾
# ============================================================
location ~ \.php$ {
# try_files:检测请求的PHP文件($uri)是否存在,不存在直接返回404
# 安全防护:防止伪造PHP路径的恶意请求(如/xxx.php/yyy.jpg)被PHP-FPM解析
try_files $uri =404;
# fastcgi_pass:指定FastCGI服务地址,将PHP请求转发到本地9000端口的PHP-FPM
fastcgi_pass 127.0.0.1:9000;
# fastcgi_index:定义FastCGI默认索引文件,请求目录时默认用index.php
fastcgi_index index.php;
# fastcgi_param:设置传递给PHP-FPM的核心环境变量
# SCRIPT_FILENAME:指定要执行的PHP文件绝对路径
# $document_root是网站根目录,$fastcgi_script_name是请求的脚本名
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include:引入Nginx默认的FastCGI参数配置文件
# 包含QUERY_STRING、REQUEST_METHOD等PHP运行必需的环境变量
include fastcgi_params;
}
location /auth-basic/ {
auth_basic "Basic Auth";
auth_basic_user_file "/etc/nginx/.htpasswd";
}
}
server {
listen 80;
listen [::]:80;
server_name www.jhl.cloud;
root /usr/share/nginx/html;
return 301 https://$host$request_uri;
}
💡 另一种方式 :也可以将PHP配置分离到独立文件,在server块中用
include /etc/nginx/default.d/*.conf;引入,和上面的方法二选一。
bash
# 方式二:分离配置(可选)
[root@nginx-server ~]# vim /etc/nginx/default.d/php.conf
nginx
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
bash
# 配置完成后,务必重启Nginx服务
[root@nginx-server ~]# systemctl restart nginx
🔧 步骤4:客户端测试
bash
# Linux客户端通过HTTPS访问info.php
[root@nginx-client ~]# curl -k https://www.jhl.cloud/info.php
✅ 实验现象:返回PHP信息页面(大量PHP配置信息),说明Nginx已成功将PHP请求转发给PHP-FPM处理。
🔄 第八部分:反向代理核心原理
🤔 正向代理 vs 反向代理
| 类型 | 代理对象 | 隐藏谁 | 生活案例 |
|---|---|---|---|
| 正向代理 | 代理客户端 | 隐藏客户端 | 你让室友帮你买奶茶,奶茶店只知道是室友买的 |
| 反向代理 | 代理服务器 | 隐藏后端服务器 | 餐厅前台点餐,你不用管是哪个厨师做的 |
💡 反向代理定义:代理外网用户的请求到内部指定服务器,并将数据返回给用户。客户端不直接与后端服务器通信,隐藏了后端服务器的IP地址。
🎯 反向代理的主要作用
- 负载均衡:将请求分发给多个后端服务器,平衡负载;
- 缓存功能:缓存静态文件或动态页面,减轻后端压力;
- 动静分离:动态内容和静态资源分别处理;
- 多站点代理:代理多个域名,共享端口。
📍 Location匹配规则优先级
| 优先级 | 语法 | 说明 |
|---|---|---|
| 1(最高) | location = /path |
精确匹配,URL完全一致才命中 |
| 2 | location ^~ /path |
前缀匹配,命中后跳过正则 |
| 3 | location ~ /regex |
大小写敏感正则 |
| 4 | location ~* /regex |
大小写不敏感正则 |
| 5 | location /path |
普通前缀匹配(最长前缀优先) |
| 6(最低) | location / |
兜底匹配 |
⚠️ proxy_pass末尾是否带 / 的区别(核心坑点!)
这是Nginx反向代理最容易踩的坑!
| 场景 | proxy_pass写法 | 转发效果 |
|---|---|---|
| 带 / | proxy_pass http://backend/; |
剔除location匹配的路径前缀 |
| 不带 / | proxy_pass http://backend; |
保留location匹配的路径前缀 |
示例 :location匹配 /api/,客户端请求 /api/user/list
nginx
# 带/:转发到 http://backend/user/list(剔除了/api/)
location /api/ {
proxy_pass http://192.168.1.102:9090/;
}
# 不带/:转发到 http://backend/api/user/list(保留了/api/)
location /api/ {
proxy_pass http://192.168.1.102:9090;
}
🖥️ 第九部分:实验六 反向代理实践(5台机器集群)
🤔 为什么要做这个实验?运维作用
这是企业最常用的架构:前端一台Nginx做反向代理/负载均衡,后端多台Nginx/Web服务器处理业务。客户端只访问代理服务器,后端服务对外不暴露,安全且易扩展。
📋 环境架构
client.jhl.cloud (10.1.8.11)
↓
proxy.jhl.cloud (10.1.8.20) ← 反向代理/负载均衡
↙ ↓ ↘
nginx1 (10.1.8.21) nginx2 (10.1.8.22) nginx3 (10.1.8.23) ← 后端Web服务器
| 主机名 | IP地址 | 服务器角色 |
|---|---|---|
| client.jhl.cloud | 10.1.8.11 | 测试客户端 |
| proxy.jhl.cloud | 10.1.8.20 | Nginx代理服务器 |
| nginx1.jhl.cloud | 10.1.8.21 | Nginx Web服务器1 |
| nginx2.jhl.cloud | 10.1.8.22 | Nginx Web服务器2 |
| nginx3.jhl.cloud | 10.1.8.23 | Nginx Web服务器3 |
💡 完整实验思路
- 5台机器配置主机名和静态IP;
- 所有节点配置/etc/hosts(域名解析);
- proxy、nginx1、nginx2、nginx3安装Nginx并启动;
- 三台后端Nginx配置不同首页(用于区分请求落到哪台);
- proxy配置反向代理,先做本地静态测试,再做远端代理测试;
- 逐步验证location匹配优先级(无符号→正则→精确);
- 配置upstream负载均衡,验证轮询/权重/ip_hash等算法。
🔧 步骤1:5台机器基础配置
client
bash
[root@localhost ~]# hostnamectl set-hostname client.jhl.cloud
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.11/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
[root@localhost ~]# nmcli connection up ens33
proxy
bash
[root@localhost ~]# hostnamectl set-hostname proxy.jhl.cloud
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.20/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
[root@localhost ~]# nmcli connection up ens33
nginx1
bash
[root@localhost ~]# hostnamectl set-hostname nginx1.jhl.cloud
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.21/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
[root@localhost ~]# nmcli connection up ens33
nginx2
bash
[root@localhost ~]# hostnamectl set-hostname nginx2.jhl.cloud
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.22/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
[root@localhost ~]# nmcli connection up ens33
nginx3
bash
[root@localhost ~]# hostnamectl set-hostname nginx3.jhl.cloud
[root@localhost ~]# nmcli connection modify ens33 ipv4.method manual ipv4.addresses 10.1.8.23/24 ipv4.gateway 10.1.8.2 ipv4.dns 10.1.8.2 autoconnect yes
[root@localhost ~]# nmcli connection up ens33
🔧 步骤2:所有节点配置/etc/hosts
bash
# 所有节点都执行
[root@所有节点 ~]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
############ proxy ##################
10.1.8.11 client.jhl.cloud client
10.1.8.20 www.jhl.cloud www
10.1.8.20 proxy.jhl.cloud proxy
10.1.8.21 nginx1.jhl.cloud nginx1
10.1.8.22 nginx2.jhl.cloud nginx2
10.1.8.23 nginx3.jhl.cloud nginx3
🔧 步骤3:后端Nginx服务器配置(proxy、nginx1、nginx2、nginx3)
bash
# 除了客户端,所有节点安装Nginx
[root@proxy,nginx1,nginx2,nginx3 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@proxy,nginx1,nginx2,nginx3 ~]# yum -y install nginx
# 启动并启用服务
[root@proxy,nginx1,nginx2,nginx3 ~]# systemctl enable nginx --now
# 防火墙设置(永久+临时)
[root@proxy,nginx1,nginx2,nginx3 ~]# firewall-cmd --add-service=http --permanent
[root@proxy,nginx1,nginx2,nginx3 ~]# firewall-cmd --add-service=http
后端节点准备不同首页(区分请求落到哪台)
bash
[root@nginx1 ~]# echo Welcome to $(hostname) > /usr/share/nginx/html/index.html
[root@nginx2 ~]# echo Welcome to $(hostname) > /usr/share/nginx/html/index.html
[root@nginx3 ~]# echo Welcome to $(hostname) > /usr/share/nginx/html/index.html
客户端测试后端节点
bash
[root@nginx-client ~]# curl http://nginx1.jhl.cloud/
Welcome to nginx1.jhl.cloud
[root@nginx-client ~]# curl http://nginx2.jhl.cloud/
Welcome to nginx2.jhl.cloud
[root@nginx-client ~]# curl http://nginx3.jhl.cloud/
Welcome to nginx3.jhl.cloud
🔧 步骤4:proxy代理服务器准备本地测试文件
bash
# 准备代理节点主页
[root@proxy ~]# echo Welcome to www.jhl.cloud > /usr/share/nginx/html/index.html
# 创建本地测试目录
[root@proxy ~]# mkdir /var/nginx
[root@proxy ~]# echo "Hello, Nginx" > /var/nginx/index.html
[root@proxy ~]# echo "Hello, jhl" > /var/nginx/test.txt
[root@proxy ~]# cp /usr/share/nginx/html/nginx-logo.png /var/nginx/
[root@proxy ~]# ls /var/nginx/
index.html nginx-logo.png test.txt
配置proxy基础配置
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
# 匹配根位置
location / {
root /var/nginx; # 网站根目录
index index.html; # 默认首页
}
}
bash
# 重新加载Nginx配置(热重载,不中断服务)
[root@proxy ~]# nginx -s reload
测试本地静态站点
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/
Hello, Nginx
[root@nginx-client ~]# curl http://www.jhl.cloud/test.txt
Hello, jhl
🤔 思考题 :如果在Nginx目录中创建一个
test.nn文件,通过www.jhl.cloud/test.nn访问时,系统是否会返回对应文件内容?答:不可以。因为"nn"类型的文件不在mime.type中,Nginx无法对该文件进行解析,此时会将文件下载到本地。
🔧 步骤5:反向代理基础实践------代理本地(验证location优先级)
准备测试目录结构
bash
# 创建/var/nginx下的子目录
[root@proxy ~]# mkdir /var/nginx/nginx{1,2}
[root@proxy ~]# echo "Hello, I'm here /var/nginx/nginx1" > /var/nginx/nginx1/index.html
[root@proxy ~]# echo "Hello, I'm here /var/nginx/nginx2" > /var/nginx/nginx2/index.html
# 创建/var下的独立目录(用于root /var 测试)
[root@proxy ~]# mkdir /var/nginx{1,2}
[root@proxy ~]# echo "Hello, Nginx1" > /var/nginx1/index.html
[root@proxy ~]# echo "Hello, Nginx2" > /var/nginx2/index.html
# 查看目录结构
[root@proxy ~]# tree /var/nginx*
/var/nginx
├── index.html # Hello,Nginx
├── nginx1
│ └── index.html # Hello, I'm here /var/nginx/nginx1
├── nginx2
│ └── index.html # Hello, I'm here /var/nginx/nginx2
├── nginx-logo.png
└── test.txt # Hello,jhl
/var/nginx1
└── index.html # Hello, Nginx1
/var/nginx2
└── index.html # Hello, Nginx2
bash
# 创建www1/www2测试目录(用于正则/精确匹配测试)
[root@proxy ~]# for path1 in www{1..2}
do
for path2 in nginx{1..2}
do
mkdir -p /var/$path1/$path2
echo "Hello, I'm here /var/$path1/$path2" > /var/$path1/$path2/index.html
done
done
[root@proxy ~]# tree /var/www*
/var/www1
├── nginx1
│ └── index.html # Hello, I'm here /var/www1/nginx1
└── nginx2
└── index.html # Hello, I'm here /var/www1/nginx2
/var/www2
├── nginx1
│ └── index.html # Hello, I'm here /var/www2/nginx1
└── nginx2
└── index.html # Hello, I'm here /var/www2/nginx2
基础测试(验证默认/匹配)
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/
Hello, Nginx
# 显示结果是目录/var/nginx/nginx1中内容
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Hello, I'm here /var/nginx/nginx1
# 显示结果是目录/var/nginx/nginx2中内容
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Hello, I'm here /var/nginx/nginx2
实践1:无符号匹配(普通前缀匹配)
💡 root拼接规则:root + URI原始请求路径
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
# 匹配根位置(兜底)
location / {
root /var/nginx;
index index.html;
}
# 匹配/nginx1时,root /var 表示在/var目录下找nginx1
# 完整路径是 /var + /nginx1 = /var/nginx1
location /nginx1 {
root /var;
# 等效于下面的 alias 语句,必须使用绝对路径
# alias /var/nginx1;
index index.html;
}
}
bash
[root@proxy ~]# nginx -s reload
bash
# nginx1后面必须添加 / 符号
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Hello, Nginx1
# 显示结果是目录/var/nginx1中内容(因为root /var + /nginx1 = /var/nginx1)
# nginx2没有匹配的location,走兜底/,root /var/nginx + /nginx2 = /var/nginx/nginx2
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Hello, I'm here /var/nginx/nginx2
# 显示结果是目录/var/nginx/nginx2中内容
✅ 实验结果 :无符号匹配(普通前缀)优先级高于默认的
/。
实践2:正则表达式匹配
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
location / {
root /var/nginx;
index index.html;
}
location /nginx1 {
root /var;
index index.html;
}
# 正则表达式匹配 /nginx.* (~表示大小写敏感)
# 匹配 /nginx1、/nginx2、/nginxxxx 等
location ~ /nginx.* {
root /var/www1;
index index.html;
}
}
bash
[root@proxy ~]# nginx -s reload
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Hello, I'm here /var/www1/nginx1
# 显示结果是目录/var/www1/nginx1中内容(正则匹配优先级高于无符号)
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Hello, I'm here /var/www1/nginx2
# 显示结果是目录/var/www1/nginx2中内容
✅ 实验结果:正则表达式匹配优先级高于无符号(普通前缀)匹配。
实践3:精确匹配
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
location / {
root /var/nginx;
index index.html;
}
location /nginx1 {
root /var;
index index.html;
}
location ~ /nginx.* {
root /var/www1;
index index.html;
}
# 精确匹配 = /nginx2/index.html(优先级最高)
location = /nginx2/index.html {
root /var/www2;
index index.html;
}
}
bash
[root@proxy ~]# nginx -s reload
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Hello, I'm here /var/www1/nginx1
# 走正则匹配,/var/www1/nginx1
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Hello, I'm here /var/www2/nginx2
# 精确匹配命中,/var/www2/nginx2
✅ 实验结果 :精确匹配(
=)优先级高于正则表达式匹配。🎯 优先级总结 :精确匹配(
=) > 正则匹配(~/~*) > 普通前缀匹配(无符号) > 兜底(/)
🔧 步骤6:反向代理基础实践------代理远端
实践1:无符号匹配(代理到远端nginx1)
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
location / {
root /var/nginx;
index index.html;
}
# 匹配 /nginx1/ 开头,代理到nginx1.jhl.cloud
# proxy_pass末尾带 /,/nginx1/ 不组合到后端服务器
# 访问 /nginx1/ 相当于直接访问 http://nginx1.jhl.cloud/
location /nginx1/ {
proxy_pass http://nginx1.jhl.cloud/; # 注意:代理后端后面有 /
index index.html;
}
}
bash
[root@proxy ~]# nginx -s reload
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Welcome to nginx1.jhl.cloud
# 显示结果是服务器 nginx1.jhl.cloud 内容(代理远端成功)
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Hello, I'm here /var/nginx/nginx2
# nginx2没有匹配location,走本地兜底/
✅ 实验结果 :无符号匹配优先级高于默认的
/,请求被代理到远端nginx1。
实践2:正则表达式匹配(代理到远端nginx2)
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
location / {
root /var/nginx;
index index.html;
}
location /nginx1/ {
proxy_pass http://nginx1.jhl.cloud/;
index index.html;
}
# 正则匹配 /nginx[12].* (匹配nginx1或nginx2开头)
location ~ /nginx[12].* {
# rewrite:手动重写路径,去掉 /nginx 前缀
# ^/nginx[12](.*)$:匹配/nginx[12]开头的完整路径,$1表示后面的所有内容
# break:重写后不再匹配其他rewrite规则
rewrite ^/nginx[12](.*)$ $1 break;
# proxy_pass不带URI(无末尾的/),配合rewrite实现路径替换
proxy_pass http://nginx2.jhl.cloud;
index index.html;
}
}
bash
[root@proxy ~]# nginx -s reload
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Welcome to nginx2.jhl.cloud
# 正则匹配优先级更高,被代理到nginx2
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Welcome to nginx2.jhl.cloud
# 同样被正则匹配,代理到nginx2
✅ 实验结果:正则表达式匹配优先级高于无符号匹配。
实践3:精确匹配(代理到远端nginx1)
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
location / {
root /var/nginx;
index index.html;
}
location /nginx1/ {
proxy_pass http://nginx1.jhl.cloud/;
index index.html;
}
location ~ /nginx[12].* {
rewrite ^/nginx[12](.*)$ $1 break;
proxy_pass http://nginx2.jhl.cloud;
index index.html;
}
# 精确匹配 = /nginx1/(优先级最高)
location = /nginx1/ {
proxy_pass http://nginx1.jhl.cloud/;
index index.html;
}
}
bash
[root@proxy ~]# nginx -s reload
bash
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx1/
Welcome to nginx1.jhl.cloud
# 精确匹配命中,代理到nginx1
[root@nginx-client ~]# curl http://www.jhl.cloud/nginx2/
Welcome to nginx2.jhl.cloud
# 正则匹配命中,代理到nginx2
✅ 实验结果:精确匹配优先级高于正则表达式匹配。
⚖️ 第十部分:实验七 七层负载均衡(upstream)
🤔 为什么要做这个实验?运维作用
单台后端服务器性能有限,高并发时会成为瓶颈。通过Nginx的upstream模块将流量分发到多台后端服务器,实现集群扩容、故障自动摘除。这是企业高并发网站的标配架构。
💡 实验思路
- 在proxy上配置upstream块,定义后端服务器池(nginx1、nginx2、nginx3);
- location中用proxy_pass指向upstream名称;
- 分别测试不同调度算法:轮询、权重、ip_hash、通用哈希、最少连接;
- 用for循环批量请求,统计各后端被访问的次数。
🔧 步骤1:配置七层负载均衡
bash
[root@proxy ~]# vim /etc/nginx/conf.d/proxy.conf
nginx
server {
listen 80;
server_name www.jhl.cloud;
location / {
proxy_pass http://backends/; # 转发到upstream集群
}
}
# upstream定义后端服务器池
upstream backends {
server nginx1.jhl.cloud:80;
server nginx2.jhl.cloud:80;
server nginx3.jhl.cloud:80;
}
bash
[root@proxy ~]# nginx -s reload
# 防火墙设置
[root@proxy ~]# firewall-cmd --add-service=http --permanent
[root@proxy ~]# firewall-cmd --add-service=http
测试轮询(默认算法)
bash
# ============================================================
# for n in {1..90}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
# 循环90次请求,排序后统计每个结果出现次数
# -s:静默模式,不输出进度信息
# sort | uniq -c:排序并统计每行出现次数
# ============================================================
[root@client ~]# for n in {1..90}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
30 Welcome to nginx1.jhl.cloud
30 Welcome to nginx2.jhl.cloud
30 Welcome to nginx3.jhl.cloud
✅ 实验现象:90次请求平均分配到3台后端,每台30次,轮询算法正常工作。
📊 upstream模块详解
upstream语法
nginx
upstream name {
server address [parameters];
}
server指令常用参数
| 参数 | 说明 | 默认值 |
|---|---|---|
weight=N |
权重,数值越大分到请求越多 | 1 |
max_fails=N |
最大失败连接次数 | 1 |
fail_timeout=时间 |
失败后暂停服务的时长 | 10s |
backup |
备份节点,其他全部故障才启用 | - |
down |
手动标记下线,不处理请求 | - |
示例:
nginx
upstream backends {
keepalive 32; # 每个worker进程缓存到upstream的连接数
server nginx1.jhl.cloud:80 max_fails=3 fail_timeout=30s;
server nginx2.jhl.cloud:80 max_fails=3 fail_timeout=30s weight=2;
server nginx3.jhl.cloud:80 max_fails=3 fail_timeout=30s backup;
server nginx4.jhl.cloud:80 max_fails=3 fail_timeout=30s down;
}
🔧 步骤2:六大调度算法详解与测试
算法1:轮询(round-robin,默认)
按客户端请求顺序逐一分配到不同后端。后端宕机自动剔除。
nginx
upstream backends {
server nginx1.jhl.cloud:80;
server nginx2.jhl.cloud:80;
server nginx3.jhl.cloud:80;
}
bash
[root@client ~]# for i in {1..60}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
20 Welcome to nginx1.jhl.cloud
20 Welcome to nginx2.jhl.cloud
20 Welcome to nginx3.jhl.cloud
算法2:权重(weight)
在轮询基础上加权重,权重和访问量成正比。解决新旧服务器性能不均问题。
nginx
upstream backends {
server nginx1.jhl.cloud:80 weight=10;
server nginx2.jhl.cloud:80 weight=20;
server nginx3.jhl.cloud:80 weight=30;
}
bash
[root@proxy ~]# nginx -s reload
[root@client ~]# for i in {1..60}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
10 Welcome to nginx1.jhl.cloud
20 Welcome to nginx2.jhl.cloud
30 Welcome to nginx3.jhl.cloud
✅ 实验现象:60次请求按1:2:3分配,权重越高分到请求越多。
算法3:IP哈希(ip_hash)
每个请求按客户端IP的哈希结果分配。同一客户端IP永远落到同一台后端,解决session会话共享问题。
nginx
upstream backends {
ip_hash;
server nginx1.jhl.cloud:80;
server nginx2.jhl.cloud:80;
server nginx3.jhl.cloud:80;
}
bash
[root@proxy ~]# nginx -s reload
[root@client ~]# for i in {1..60}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
60 Welcome to nginx2.jhl.cloud
✅ 实验现象:60次请求全部落到同一台后端(因为客户端IP固定,哈希结果相同)。
⚠️ 注意:国内大多数公司是NAT上网,多个客户端对应一个外部IP,会导致请求分配不均。ip_hash模式下weight参数无效,也不支持backup节点。
算法4:通用哈希(generic hash)
请求发送到的服务器由用户定义的键确定,可以是文本字符串、变量或组合(如URI、cookie、header)。
nginx
upstream backends {
hash $request_uri; # 按请求URI哈希
#hash_method crc32; # 哈希算法(Nginx不支持url_hash,需安装hash模块)
server nginx1.jhl.cloud:80;
server nginx2.jhl.cloud:80;
server nginx3.jhl.cloud:80;
}
bash
[root@proxy ~]# nginx -s reload
[root@client ~]# for i in {1..60}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
60 Welcome to nginx3.jhl.cloud
✅ 实验现象 :同一URI哈希结果固定,全部落到同一台后端。加
consistent参数可开启一致性哈希,后端上下线时流量抖动更小。
算法5:最少连接数(least_conn)
将请求分发给当前活跃连接数最少的后端。适合后端处理请求耗时差异大、长连接场景。
nginx
upstream backends {
least_conn;
server nginx1.jhl.cloud:80;
server nginx2.jhl.cloud:80;
server nginx3.jhl.cloud:80;
}
bash
[root@proxy ~]# nginx -s reload
[root@client ~]# for i in {1..60}; do curl http://www.jhl.cloud -s; done | sort | uniq -c
20 Welcome to nginx1.jhl.cloud
20 Welcome to nginx2.jhl.cloud
20 Welcome to nginx3.jhl.cloud
✅ 实验现象:测试环境下后端响应快,连接数都差不多,所以平均分配。生产环境长连接场景下差异明显。least_conn也支持weight权重。
算法6:最少时间(least_time,仅限Nginx Plus商业版)
分配给响应时间最短的后端。开源Nginx没有此功能。
nginx
upstream backends {
least_time header; # header:接收第一个字节的时间;last_byte:接收完整响应时间
server nginx1.jhl.cloud:80;
server nginx2.jhl.cloud:80;
server nginx3.jhl.cloud:80;
}
📋 六大调度算法总结表
| 算法 | 配置写法 | 核心逻辑 | 使用场景 | 特点 |
|---|---|---|---|---|
| 轮询round-robin(默认) | 不写任何策略 | 请求依次分配给后端 | 后端性能差不多,无会话保持需求 | 默认策略,故障自动剔除,不做会话绑定 |
| 权重weight | server x weight=N; |
按权重比例分配流量 | 后端机器性能不一样,性能好的多承担 | 权重越大分到请求越多;仅轮询模式生效;ip_hash模式weight无效 |
| ip_hash | ip_hash; |
根据客户端TCP源IP哈希,同一IP永远落到同一台 | 需要会话保持,无前置代理 | 基于TCP源IP,不读HTTP头;前面有SLB/CDN会全部打到同一台;不支持backup节点 |
| 通用哈希generic hash | hash $变量 [consistent]; |
自定义变量做哈希(ip/cookie/header/url等) | 需要基于cookie、url做会话绑定;前置代理环境 | Nginx1.7.2+;加consistent开启一致性哈希,后端上下线流量抖动变小 |
| 最少连接least_conn | least_conn; |
把请求分给当前活跃连接数最少的后端 | 后端处理请求耗时差异大,长连接场景 | 优先找连接数少的机器;weight权重仍然生效 |
| 最少时间least_time | least_time header; |
分配给响应时间最短的后端 | Nginx Plus商业版,根据历史响应速度 | 开源Nginx没有,根据后端历史响应速度择优分配 |
🎯 快速记忆口诀
- 轮询:挨个来(默认)
- weight:能力强多干活
- ip_hash:同一个IP锁一台后端(TCP源IP)
- hash:想拿什么就哈希什么(cookie/header/url),consistent保稳定
- least_conn:谁闲分给谁(看活跃连接)
- least_time:谁响应快分给谁(商业版)
🐛 第十一部分:Nginx常见问题与日志
📋 Nginx常见日志
1. access.log(访问日志)
记录每一次HTTP请求。
nginx
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
| 字段 | 含义 |
|---|---|
$remote_addr |
客户端IP |
$remote_user |
远程用户(Basic认证时) |
$time_local |
访问时间 |
$request |
请求行(方法+URI+协议) |
$status |
响应状态码 |
$body_bytes_sent |
响应体字节数 |
$http_referer |
来源页面 |
$http_user_agent |
浏览器UA |
$http_x_forwarded_for |
代理IP链 |
2. error.log(错误日志)
502、配置错误、连接失败都在这里排查。
bash
# 实时查看错误日志
tail -f /var/log/nginx/error.log
🚨 生产常见问题
| 问题 | 原因 |
|---|---|
| 502 Bad Gateway | Nginx无法连接后端服务,后端挂了、端口不对、防火墙拦截 |
| 504 Gateway Timeout | 后端处理请求太慢,代理超时 |
| 499 | 客户端主动断开连接 |
| 403 Forbidden | 权限不足、目录不存在、没有index文件 |
| 并发上不去 | 检查worker_processes、worker_connections、系统文件句柄数ulimit |
📝 文末总结
🎯 核心知识点回顾
| 模块 | 核心要点 |
|---|---|
| Nginx架构 | master-worker多进程,异步非阻塞IO,热重载不中断服务 |
| 配置结构 | 全局块→events→http→server→location,五层嵌套 |
| location优先级 | 精确(=) > 前缀(^~) > 正则(~/~*) > 普通前缀 > 兜底(/) |
| 虚拟主机 | 基于域名(最常用)、基于端口、基于IP |
| HTTPS | openssl生成证书,443端口配置ssl_certificate/key,80端口301跳转 |
| PHP站点 | php-fpm监听9000,location ~ .php$ 用fastcgi_pass转发 |
| 反向代理 | proxy_pass末尾带/剔除前缀,不带/保留前缀;必加proxy_set_header传真实IP |
| 负载均衡 | upstream定义后端池,六大算法:轮询/权重/ip_hash/hash/least_conn/least_time |
💡 生产环境最佳实践
- 改配置必校验 :
nginx -t检查语法,没问题再nginx -s reload; - 热重载不重启:更新配置用reload,不要restart,避免业务中断;
- 代理必传真实IP :
proxy_set_header X-Real-IP $remote_addr;否则后端日志全是代理IP; - upstream加健康检查 :
max_fails和fail_timeout配置故障自动摘除; - 日志分割:access.log和error.log要做logrotate轮转,避免占满磁盘;
- 静态资源缓存:图片、CSS、JS配置expires缓存,减轻后端压力。
💬 互动话题
你们线上Nginx用的什么负载均衡算法?反向代理踩过proxy_pass斜杠的坑吗?
欢迎在评论区交流~
如果这篇文章对你有帮助,别忘了 点赞👍 收藏⭐ 关注👀 三连支持!
我们下篇文章见~拜拜!👋