主题
ai-ear.cn 静态站故障排查与 nginx → Traefik 迁移记录
服务器:root@8.153.84.140(CentOS 7,2C2G)| 站点:https://ai-ear.cn/ 日期:2026-08-12 | 状态:已完成并验证
本目录同时保存了服务器现行配置备份:
traefik.yaml、dynamic.yaml、nginx.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 -enddate→notAfter=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。
实施步骤
- 新 nginx.conf:删掉所有 TLS/反代 server 块,只保留
listen 127.0.0.1:8080的静态站块 (gzip_static、open_file_cache、缓存头、4 条旧页 301、/doc/ basic auth、error_page 全部保留) - 重写 dynamic.yaml(干净版本,见本目录备份):
tls.stores.default.defaultCertificate复用/etc/pki/nginx/ai-ear.cn.crt/.keyweb → websecure用redirectSchememiddleware +noop@internalservice- 主站 router 指向
http://127.0.0.1:8080 - 9000/9001 挂
basicAuth(usersFile 从/etc/nginx/.htpasswd复制到/etc/traefik/.htpasswd) - Traefik Dashboard 挂在 9000 端口
/dashboard+/api路径(basic auth)
- 上线顺序(避免端口冲突):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.xml | 200 |
| 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 内走本机 IP | curl 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 链的全量快照。
修复
- 新建幂等脚本
/etc/iptables/jumpserver-apply.sh:只用iptables -C || -A管理自定义规则 (INPUT 基础策略 + DOCKER-USER 中 36000/2222 的访问控制),不碰 docker 的链 jumpserver-iptables.service的 ExecStart 改为执行该脚本(原快照备份为 jumpserver.rules.stale.bak)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 重启不再需要人工干预。