站点上线与排障
把「网站打不开」这句话,变成一条可执行的判定路径。
核心不是记住更多命令,是先归因、再动手——因为这几类故障的症状几乎一模一样:
> 用户看到的都是「打不开 / 白屏 / 502 / 转圈」,
> 但病因可能在第 3 层(DNS)、第 4 层(TCP/防火墙)、第 7 层(nginx / 后端 / 证书)中的任意一层。
> 重启服务能修好其中不到三成,剩下七成会复发。
核心是四个产物:
| 产物 | 位置 | 解决什么 |
|---|---|---|
| 上线流水线 | references/launch-playbook.md |
空 VPS → 域名 → HTTPS → 多站点 → 备份,一次做对 |
| 判定树 | references/triage-tree.md |
10 类症状 → 归属层 → 第一条要跑的命令 |
| 故障库 | references/failure-library.md |
18 条真实故障(带原话报错与真病因) |
| 体检脚本 | scripts/doctor.py |
DNS / TLS / HTTP / 端口 / 磁盘 / 反代配置漂移,一次全查 |
何时使用
- 新买 VPS / 新域名,要从零把站点跑起来,并且以后能自己修
- 线上报 502 / 504 / 404 风暴 / 白屏 / 「无法访问此网站」/
ERR_CONNECTION_* - 证书过期、
SSL_ERROR、NET::ERR_CERT_*、手机上提示不安全 - 域名解析了但访问不到;换了服务器但流量还打到旧 IP
Address already in use、「端口被占用」、服务起不来- 磁盘写满导致 MySQL / 日志 / 上传全挂
- 站点挂在 Cloudflare 后面,怀疑源站被直接打穿 / 统计数字明显不对
- 一台机器要跑多个站点、多个域名,改一个配置把另一个搞挂
典型提问
帮我体检 example.com,看 DNS、证书、HTTP、反代这四层哪一层不对,别只告诉我要重启。网站 502,我先重启了 nginx 也没用,帮我按层定位到底是后端挂了还是反代配错了。我从零开一台 VPS,要把 example.com 和 en.example.com 两个站都上 HTTPS,给我一条能照着敲的流水线。证书昨天到期了,我要能自动续期 + 到期前告警,别再靠人记。站点挂在 Cloudflare 后面,我怎么确认源站没被绕过 CDN 直接打?磁盘满了,MySQL 起不来,先删什么、后删什么,别删错。
快速开始
# 1) 体检一个线上域名(四层一次查完,退出码 0=OK / 1=WARN / 2=FAIL)
python scripts/doctor.py --domain example.com --domain en.example.com \
--expect-ip 203.0.113.10
# 2) 只有症状,不知道去哪查 —— 先过判定树
python scripts/triage.py "502 bad gateway"
python scripts/triage.py "证书过期"
python scripts/triage.py "Address already in use"
# 3) 本机体检:端口占用 / 磁盘 / 关键进程 / 时间同步
python scripts/doctor.py --local
# 4) 反代配置体检(找出会让监控失真、让缓存错乱的那几行)
python scripts/doctor.py --nginx-dir /etc/nginx/conf.d
doctor.py 只读不写:不 reload、不改配置、不删文件。它是体检,不是医生动手。
上线:五段流水线
> 完整命令与判定见 references/launch-playbook.md,这里只给结构与不可越过的判据。
| 段 | 动作 | 不可越过的判据 |
|---|---|---|
| ① 地基 | 建用户、SSH 换密钥、关密码登录、开防火墙 | ssh -o PasswordAuthentication=no 能进;ufw status 只有 22/80/443 |
| ② 名字 | A 记录指向本机公网 IP,先灰云(DNS only) | dig +short example.com = 本机 IP,curl -I http://<IP> 拿到站点 |
| ③ 证书 | certbot --nginx -d example.com -d www.example.com |
openssl s_client 看到 CN/SAN 匹配且有效期 > 60 天 |
| ④ 站点 | nginx server 块 + 反代 + 上传目录 | nginx -t 通过;curl -I https://example.com 200;curl -I http:// 301 |
| ⑤ 兜底 | 续期定时任务、日志轮转、每日备份、磁盘告警 | certbot renew --dry-run 成功;备份能恢复(不只是能生成) |
顺序不能换。 先开 Cloudflare 橙云再去申证书,80 端口验证会被 CDN 拦掉(或直接走 DNS-01);
先上 HTTPS 再配 DNS,certbot 找不到域名。这两步的顺序错误占新手失败的一半以上。
归因:先分层,再动手
任何「打不开」先按这四层自上而下过一遍,每层只跑一条命令:
| 层 | 一条命令 | 通过的样子 | 不通说明什么 |
|---|---|---|---|
| 1 名字 | dig +short example.com |
返回你预期的 IP | 空 → DNS 没配/没生效;旧 IP → 改了没传播或 TTL 未过 |
| 2 通路 | curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' --max-time 8 http://<IP>/ -H 'Host: example.com' |
200/301,且 time < 3s | 超时 → 防火墙/安全组;拒绝 → 没监听 |
| 3 加密 | openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \| openssl x509 -noout -dates -subject |
日期未过、CN/SAN 匹配 | 过期 → 续期;名称不符 → 证书装错站 |
| 4 应用 | curl -sI https://example.com + journalctl -u nginx -n 50 --no-pager |
200,日志无 upstream 报错 |
502 → 后端;404 风暴 → 路径/缓存/文件被覆盖 |
只有第 4 层不通才轮到看代码。 前三层不通去看代码是纯浪费时间——这是最高频的误判。
六条不变量
- 先
nginx -t再 reload。nginx -s reload在配置语法错时会静默保留旧配置继续跑,
你以为改了,其实没生效。先测再 reload,出错立刻知道。 error_page 404 ... 302 /是毒药。把所有错误重定向到首页,会让所有错误都返回 200,
监控、CDN 统计、你自己全看不见真故障。要么返回真状态码,要么单独建错误页。- 静态资源必须带版本号。
style.css?v=20261025。HTML 不缓存、CSS 长缓存是正确的配置,
但改了 CSS 不换 URL = 用户看到「新 HTML + 旧 CSS」,表现是「样式全乱了」而不是「没更新」。 - 源站不能直连。挂了 CDN 就把 80/443 限制到 CDN 回源段(或至少加鉴权头),
否则机器人绕开 CDN 直接打源站,限流和 WAF 全都白配。注意:灰云(DNS only)的子域名不走 CDN,
锁源站会打断它们——上线前先dig一遍所有子域,确认哪些是灰云。 - 上传和替换必须原子。
.part写完 → 校验字节数 →mv覆盖。
直接覆盖的窗口期(哪怕 3 秒)会产出一批 404 和半截文件,而且排查时已经恢复,最难复现。 - 备份要能恢复才算备份。
tar生成成功不等于能恢复:权限、属主、软链、数据库字符集、
nginx 配置里的绝对路径,任一项错都会在恢复当天才发现。每次改配置前先备份 + 实际恢复一次到临时目录。
参考资料
references/launch-playbook.md—— 从空 VPS 到多站点 HTTPS:五段命令、SSH 加固、certbot 两种验证方式、多站点与反代、备份与把恢复演练references/triage-tree.md—— 10 类症状的完整判定树(502 / 504 / 404 风暴 / 白屏 / 证书 / DNS / 端口 / 磁盘 / 权限 / CDN)references/failure-library.md—— 18 条真实故障,每条带原话报错、真病因、处置、验证命令scripts/doctor.py—— 四层体检(DNS/TLS/HTTP/本机)+ 反代配置反模式扫描scripts/triage.py—— 输入症状原文,输出归属层与第一条命令templates/nginx-site.txt—— 多站点 + 反代 + 缓存策略 + 错误页的正确写法(带行内注释说明为什么)templates/deploy_site.sh—— 原子上传 + 逐域名校验templates/site_manifest.json—— 一个站点的事实清单(域名 / 源站 IP / 证书到期 / 反代目标 / 备份位置)
边界
- 不替代云厂商控制台操作:安全组、快照、弹性 IP 的绑定解绑需要你在控制台点,脚本只能验证结果。
- 不碰你的私钥:所有凭据只从环境变量或本机
~/.ssh/config读,脚本与文档里不落任何明文密码。 - 不做渗透测试:本文的「源站是否暴露」是配置自检(端口与回源段),不是攻击验证。
- 不管域名注册与备案合规:备案、实名、ICP 展示位置以你所在地法规与注册商要求为准。