Skip to content

ai-ear.cn 静态站故障排查与 nginx → Traefik 迁移记录

服务器:root@8.153.84.140(CentOS 7,2C2G)| 站点:https://ai-ear.cn/ 日期:2026-08-12 | 状态:已完成并验证

本目录同时保存了服务器现行配置备份:traefik.yamldynamic.yamlnginx.conf


一、故障:站点 HTTPS 挂了(2026-08-12)

现象

  • https://ai-ear.cn/ 连接被拒(curl 返回 000)
  • http://ai-ear.cn/(80 端口)正常返回 200
  • nginx 进程运行正常,磁盘(11%)、内存(可用 1.3G)均正常

排查过程

bash
ssh root@8.153.84.140
systemctl status nginx            # active (running),无异常
df -h / ; free -h                 # 排除磁盘/内存
ss -tlnp | grep -E ":80|:443"     # 关键发现:只有 :80 在监听,443 没有!

检查配置发现 /etc/nginx/nginx.conf 主站点 server 块中:

nginx
server {
    #listen       443 ssl http2;   # ← 被注释掉了,原因未知
    server_name  ai-ear.cn;
    ...
}

该 server 块没有任何生效的 listen 指令,回退为默认 *:80,因此 HTTPS 完全无人监听。

排除其他可能:

  • 证书有效:openssl x509 -enddatenotAfter=Oct 15 2026
  • 443 端口无其他进程占用
  • nginx 编译支持 http_v2 模块

修复

bash
cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.20260812
sed -i "s|#listen       443 ssl http2;|listen       443 ssl http2;|" /etc/nginx/nginx.conf
nginx -t && systemctl reload nginx
curl -sk -o /dev/null -w "%{http_code}\n" https://ai-ear.cn/   # 200

教训

  • 站点"挂了"先看监听端口(ss -tlnp),再看进程状态 —— 本例进程健康但端口没听
  • nginx server 块没有 listen不会报错,会静默回退到 80 端口

二、迁移:全部 nginx 配置改为 Traefik(2026-08-12)

背景

服务器上已安装 Traefik v3.7.10(/usr/local/bin/traefik + systemd unit),但服务未启用; /etc/traefik/dynamic.yaml 是一次失败的迁移尝试 —— routers: 键重复定义了十几次, YAML 重复键后写覆盖前写,实际路由几乎全部丢失。

目标架构

外网 :80/:443/:9000-9009/:7500
        │  Traefik v3(TLS 终止 + 路由 + basic auth + 80→443 跳转)

443 主站 → 127.0.0.1:8080(nginx 纯静态后端:gzip_static/缓存头/301//doc/ 认证)
9000-9001 → 127.0.0.1:19000-19001(frp 隧道,basic auth)
9002-9009 → 127.0.0.1:19002-19009(frp 隧道)
7500      → 127.0.0.1:17500(frp dashboard)

Traefik 不能发静态文件,因此 nginx 保留为本地静态后端,只做 127.0.0.1:8080

实施步骤

  1. 新 nginx.conf:删掉所有 TLS/反代 server 块,只保留 listen 127.0.0.1:8080 的静态站块 (gzip_static、open_file_cache、缓存头、4 条旧页 301、/doc/ basic auth、error_page 全部保留)
  2. 重写 dynamic.yaml(干净版本,见本目录备份):
    • tls.stores.default.defaultCertificate 复用 /etc/pki/nginx/ai-ear.cn.crt/.key
    • web → websecureredirectScheme middleware + noop@internal service
    • 主站 router 指向 http://127.0.0.1:8080
    • 9000/9001 挂 basicAuth(usersFile 从 /etc/nginx/.htpasswd 复制到 /etc/traefik/.htpasswd
    • Traefik Dashboard 挂在 9000 端口 /dashboard + /api 路径(basic auth)
  3. 上线顺序(避免端口冲突):
    bash
    systemctl reload nginx          # 先释放 80/443/900x/7500,只留 127.0.0.1:8080
    systemctl enable --now traefik  # 再启动 Traefik 接管所有对外端口

踩到的坑

坑 1:301 跳转泄露内网端口

旧页 301 的 Location 变成 http://ai-ear.cn:8080/... —— nginx 默认 absolute_redirect on, 会用自己监听的端口和 scheme 拼绝对路径。修复:

nginx
absolute_redirect off;   # Location 改为相对路径

坑 2:900x 端口 502

900x 后端是 frps 映射的 127.0.0.1:1900x,frpc 客户端未连接时连接被直接关闭, 表现为 502 / "upstream prematurely closed connection"。迁移前 nginx 时代同样如此, 属后端 frpc 隧道在线状态问题,非代理配置问题。

验证结果

项目结果
https://ai-ear.cn/200
http://ai-ear.cn/301 → https
4 条旧页 301 合并跳转✅ 指向 textbook 章节(相对路径,不泄露 :8080)
/doc/ 无凭据401
sitemap.xml200
9000-9009 TLS 握手正常(后端 frpc 离线时 502 为预期行为)

服务器上的备份文件

文件说明
/etc/nginx/nginx.conf.bak.20260812故障修复前的配置(listen 443 被注释版本)
/etc/nginx/nginx.conf.pre-traefik迁移前的完整 nginx 配置
/etc/traefik/dynamic.yaml.pre-migration.*损坏的旧 dynamic.yaml

回滚方法

bash
ssh root@8.153.84.140
systemctl disable --now traefik
cp -a /etc/nginx/nginx.conf.pre-traefik /etc/nginx/nginx.conf
nginx -t && systemctl reload nginx

三、日常运维速查

bash
# 查看 Traefik 状态与日志
ssh root@8.153.84.140 'systemctl status traefik; journalctl -u traefik -n 50 --no-pager'

# 查看各端口监听归属
ssh root@8.153.84.140 'ss -tlnp | grep -E "traefik|nginx"'

# 修改路由后无需重启:providers.file.watch=true 会自动热加载 dynamic.yaml
vi /etc/traefik/dynamic.yaml   # 保存即生效

# 检查证书有效期
ssh root@8.153.84.140 'openssl x509 -in /etc/pki/nginx/ai-ear.cn.crt -noout -enddate'

关联文档:本记录已归档为 KnoAI 技术站 Traefik 网关实战 板块。


四、故障:frpc 报 jumpserver-web-36000 connection refused(2026-08-13)

现象

宿主机 frpc 日志持续报错(约 22 次/小时):

[jumpserver-web-36000] connect to local service [192.168.122.250:36000] error: dial tcp: connect: connection refused

公网 https://ai-ear.cn:36000/(frps → frpc → jumpserver VM)不通。

排查链

检查结果
frpc 本体systemctl / 控制连接 8.153.84.140:7000✅ 正常
jumpserver VM(192.168.122.250)ping / ssh / docker ps✅ 8 个容器全部 healthy,docker-proxy 监听 36000
VM 内回环curl https://127.0.0.1:36000✅ 200
VM 内走本机 IPcurl https://192.168.122.250:36000❌ 000

→ 问题锁定在 VM 的 iptables。

根因

VM 上的 jumpserver-iptables.service 开机执行 iptables-restore /etc/iptables/jumpserver.rules, 而该文件是早期一次全量规则快照,包含旧的 docker 网桥名 br-8c4a8b5495c0 和旧容器 IP 192.168.250.5。 docker 网络重建后实际网桥为 br-756200469d32、jms_web 实际 IP 为 192.168.250.6

开机时这份过期快照在 docker 之后加载,把 docker 动态生成的 DOCKER 链覆盖成旧版本, DNAT 把 36000 流量送到旧 IP 192.168.250.5(该地址容器未监听 36000),于是直接回 RST → connection refused。

教训:自定义防火墙规则绝不能用 iptables-restore 保存/恢复包含 docker 链的全量快照。

修复

  1. 新建幂等脚本 /etc/iptables/jumpserver-apply.sh:只用 iptables -C || -A 管理自定义规则 (INPUT 基础策略 + DOCKER-USER 中 36000/2222 的访问控制),不碰 docker 的链
  2. jumpserver-iptables.service 的 ExecStart 改为执行该脚本(原快照备份为 jumpserver.rules.stale.bak)
  3. systemctl restart docker(容器均 restart:always,自动恢复)→ 运行脚本

验证

  • 宿主机 curl https://192.168.122.250:36000/ → 200 ✅
  • 公网 curl https://ai-ear.cn:36000/ → 200 ✅
  • frpc 日志无新报错,8 个容器全部 healthy ✅

重启持久性验证(2026-08-13)

直接 reboot jumpserver VM 实测:容器自动恢复 healthy,docker 重建正确 DNAT(容器 IP 每次重建都会变,本次为 .4→.7), 修复后的 jumpserver-iptables.service(apply 脚本)自动补齐 DOCKER-USER 自定义 ACL。 宿主机直连与公网链路均 200,frpc 无新报错 —— 宿主机/VM 重启不再需要人工干预