我的运维笔记

服务器运维学习记录 —— Nginx 多站点部署(Rocky 9)

第 1 讲 · Nginx 配置结构与虚拟主机

一台服务器、一个 IP,如何同时承载多个网站?

1.1 虚拟主机:按域名分流

服务器只有一个公网 IP,想再做一个"运维笔记"网站并不需要再买服务器。浏览器请求中携带的 Host 请求头就是区分依据:Nginx 按域名把请求分给不同的站点, 这就是虚拟主机——在 Nginx 中,每个网站对应一个 server 块。

先看看 Nginx 在服务器上是怎么跑的:

# 列出所有进程,grep 筛出 nginx 相关的行;[n] 是个小技巧,避免把 grep 自身也显示出来
ps -ef | grep [n]ginx
# 查看 CPU 核数(工作进程数与它相等)
nproc

会看到一个 master process(主进程)和若干 worker process(工作进程): 主进程以 root 身份运行,负责读取配置、管理工作进程;工作进程以 nginx 用户身份运行,真正处理请求。 第 2 章 403 实验里"Nginx 以 nginx 用户读取网页文件",说的就是工作进程。

1.2 主配置文件的层次结构

# 去掉注释和空行再看主配置:第一个 grep -v 去掉以 # 开头的注释行,第二个去掉空行
grep -v '^\s*#' /etc/nginx/nginx.conf | grep -v '^\s*$'

Nginx 的配置是一层套一层的结构:

nginx.conf
├── 全局:user nginx; worker_processes auto; error_log ...; pid ...;
├── events { ... }   连接处理方式,如每个工作进程的最大连接数
└── http { ... }     所有网站共用的设置
    ├── log_format、access_log、sendfile、keepalive_timeout、include mime.types ...
    ├── include /etc/nginx/conf.d/*.conf;   ← 我们的 mysite.conf 就是从这里加载的
    └── server { ... }   系统自带的默认站点
        └── location { ... }
关键指令含义和我们的关联
user nginx;工作进程以 nginx 用户身份运行第 2 章的 403 实验
worker_processes auto;工作进程数自动等于 CPU 核数进程列表里看到 2 个工作进程
include /etc/nginx/conf.d/*.conf;把 conf.d 下所有 .conf 文件插入到这个位置mysite.conf 能生效的原因;也是备份文件不能以 .conf 结尾的原因
access_log /var/log/nginx/access.log main;默认的访问日志各站点各自另写了 access_log

1.3 继承与覆盖

外层的指令,内层默认继承;内层自己写了同名指令,就以内层为准。 例如 http 块中写了 access_log,所有站点默认都写这个文件; 而 mysite.conf 的 server 块里自己写了自己的日志路径,主站的访问记录就进了自己的日志文件。

原则:通用设置写在 http 块,网站特有设置写在各自 server 块; 新增站点遵循"一站一文件、一站一日志"。

1.4 用 nginx -T 查看完整生效配置

# 查看完整生效配置;按空格翻页,按 / 输入关键词搜索,按 q 退出
sudo nginx -T | less

# 只看各站点监听的端口和域名:-n 显示行号,-E 使用扩展正则
sudo nginx -T 2>/dev/null | grep -n -E "^\s*(listen|server_name)"

# 确认默认站点:带 default_server 的就是默认站点
grep -n "default_server" /etc/nginx/nginx.conf

nginx -T 会先检查语法,再把主配置和所有被 include 的文件按加载顺序合并输出—— 看到的就是 Nginx 实际使用的完整配置,是排查问题的利器。

1.5 location 的匹配规则

一个请求路径可能同时匹配多个 location。Nginx 的规则是:普通前缀匹配取最长的那个。 例如请求 /.well-known/acme-challenge/abc 既匹配 location / 也匹配 location /.well-known/acme-challenge/,走后者,证书验证文件才能被正常发出。

写法含义优先级
location = /路径精确匹配最高,命中即停止
location ^~ /路径前缀匹配,命中后不再检查正则次之
location ~ 正则 / ~*正则匹配(区分 / 不区分大小写),按书写顺序取第一个再次
location /路径普通前缀匹配,取最长的最低

第 2 讲 · 子域名解析与网页准备

给"运维笔记"网站安家:notes.你的域名

2.1 添加子域名解析

第二个网站使用子域名 notes.你的域名。 子域名属于已备案的主域名,一般不需要另行备案,只需添加一条解析记录。 解析需要时间生效,所以先做这一步。

进入阿里云 云解析 DNS → 域名解析 → 解析设置 → 添加记录: 记录类型选 A,主机记录填 notes,记录值填服务器公网 IP,TTL 保持默认。

在自己的电脑上验证(可能需要等几分钟):

# Address 一栏应是你的服务器公网 IP
nslookup notes.你的域名

2.2 准备网站目录

# 为子站点单独建目录,和主站互不干扰
sudo mkdir -p /var/www/notes
# 交给 study 用户,之后上传网页不必用 sudo
sudo chown -R study:study /var/www/notes
# 目录权限 755:Nginx 工作进程才能进入和读取
sudo chmod 755 /var/www/notes
# 核对:drwxr-xr-x,属主为 study
ls -ld /var/www/notes

2.3 制作"我的运维笔记"页面

网站内容就是前三章的运维笔记:每一章做了什么、用到的关键命令、遇到的问题和解决办法。 排版要求:

注意:笔记内容必须自己写,AI 只负责排版;页面中每一条命令都要能说清它的作用; 底部备案号要与主站一致。

2.4 上传到服务器

# 上传到子站点目录
scp index.html study@你的服务器公网IP:/var/www/notes/

第 3 讲 · 子站点上线:证书、HTTPS 与排错

80 端口配置 → 默认站点实验 → 单独申请证书 → 443 端口 → 验证与排错

3.1 先写 80 端口部分(一站一文件)

子站点的配置结构与主站相同:80 端口负责证书验证和跳转,443 端口提供内容。 但现在还没有证书,如果把 443 部分写进去,nginx -t 会报错, 所以先只写 80 端口部分:

sudo vi /etc/nginx/conf.d/notes.conf
# notes 子站点 ① 80 端口:为证书申请和续期提供验证文件;其余请求跳转到 HTTPS
server {
    listen 80;
    server_name notes.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/notes;   # 注意是子站点自己的目录
    }

    location / {
        return 301 https://$host$request_uri;
    }
}
# 检查语法
sudo nginx -t
# 重新加载
sudo systemctl reload nginx

3.2 实验:还没有证书,访问 https 会怎样?

访问 http://notes.你的域名 会跳转到 https,浏览器弹出证书警告—— 拿到的竟然是主站的证书。原因:

在服务器上用两条命令验证:

# 80 端口:用一个没有配置的域名访问本机,返回的是系统默认页(看 <title> 一行)
curl -s -H "Host: nosuch.example.org" http://127.0.0.1 | grep -i "<title>"

# 443 端口:以 notes 域名发起加密连接,看服务器出示的证书属于哪些域名
# s_client 建立 TLS 连接;-servername 告诉服务器要访问的域名;
# </dev/null 表示连接后立即结束;后半段只取证书中的域名列表
openssl s_client -connect 127.0.0.1:443 -servername notes.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName

第二条命令显示的是主站的两个域名,印证了上面的分析。

3.3 单独申请证书

子站点使用单独的证书而不是并入主站证书,好处是互不牵连: 以后撤掉某个子站点,直接删除它的证书即可,不影响主站。 第 3 章已注册过 Let's Encrypt 账户,这次不用再填邮箱。 依然是先演练、再正式:

# 演练:-w 是子站点的目录 /var/www/notes,-d 只写 notes 子域名
sudo certbot certonly --webroot -w /var/www/notes -d notes.example.com --dry-run

# 看到 The dry run was successful. 之后,去掉 --dry-run 正式申请
sudo certbot certonly --webroot -w /var/www/notes -d notes.example.com

# 现在应列出两张证书:主站一张,notes 一张
sudo certbot certificates

3.4 补上 443 端口部分

sudo vi /etc/nginx/conf.d/notes.conf

在文件末尾追加:

# notes 子站点 ② 443 端口:提供网站内容
server {
    listen 443 ssl;
    server_name notes.example.com;

    ssl_certificate     /etc/letsencrypt/live/notes.example.com/fullchain.pem;  # notes 自己的证书
    ssl_certificate_key /etc/letsencrypt/live/notes.example.com/privkey.pem;    # notes 自己的私钥
    ssl_protocols TLSv1.2 TLSv1.3;

    root  /var/www/notes;
    index index.html;

    # 一站一日志:子站点的访问记录单独存放
    access_log /var/log/nginx/notes_access.log;
    error_log  /var/log/nginx/notes_error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}
sudo nginx -t
sudo systemctl reload nginx

3.5 验证与续期

浏览器访问 http://notes.你的域名:自动跳转 https,出现安全锁,显示运维笔记。 再用 3.2 节的 openssl 命令检查一次,这次应只显示 notes 子域名。

续期不需要额外配置:certbot-renew.timer 会检查本机所有证书, deploy 钩子会在任何一张证书续期后重载 Nginx。演练确认:

# 应显示两张证书的模拟续期都成功
sudo certbot renew --dry-run

3.6 盘点全部站点、验证日志分离

# 一条命令盘点:四个自己的 server 块(主站 80/443、notes 80/443)+ 系统默认站点
sudo nginx -T 2>/dev/null | grep -n -E "^\s*(listen|server_name)"

开两个终端分别实时查看两个站点的访问日志,请同学分别访问两个网站—— 访问哪个网站,记录就只出现在哪个网站的日志中:

# 窗口一:主站日志
sudo tail -f /var/log/nginx/mysite_access.log
# 窗口二:notes 站日志
sudo tail -f /var/log/nginx/notes_access.log

一站一日志的好处:某个网站出了问题,直接看它自己的日志,不会被其他网站的记录淹没; 后面做日志分析时,分离的日志也让统计简单得多。

3.7 遇到问题怎么办(排查速查表)

现象可能原因排查方法
nslookup notes.域名 查不到解析未添加或未生效检查云解析中主机记录是否为 notes;等待 TTL 后再试
certbot 报 unauthorized 且有 404验证文件没被访问到核对 notes.conf 中 acme 路径的 root 是否为 /var/www/notes,与 -w 一致
访问 notes 有证书警告,证书上是主站域名notes 的 443 部分未生效检查 443 配置是否已追加并 reload;用 nginx -T 确认
访问 notes 显示的是主站内容server_name 写错用 nginx -T 核对 notes.conf 的 server_name
nginx -t 报 cannot load certificate证书路径写错,或证书还没申请就写了 443 部分对照 sudo certbot certificates 中的路径
nginx -t 报 conflicting server name同一域名在两个 server 块中重复出现用 nginx -T 找出重复的 server_name,删掉多余的
403 Forbidden目录或文件权限不足ls -l 核对:目录 755、文件 644

3.8 本章小结

内容要点
虚拟主机一个 IP 上的多个网站,Nginx 按请求中的域名(Host)分配给对应的 server 块
配置结构全局 → events → http → server → location,外层设置被内层继承,内层可以覆盖
includeconf.d 下的 .conf 文件按文件名顺序插入 http 块
默认站点匹配不到域名时:交给带 default_server 的站点;没有则交给该端口的第一个站点
location精确 = > ^~ > 正则 > 最长前缀
子站点解析 → 目录 → 80 配置 → 证书 → 443 配置;一站一文件、一站一日志、一站一证书