VPS 迁移后 TCP 能连上,但 TLS 握手永远超时。这次排查全程交给 pi agent(背后是 GLMkk-5.2)完成——从登录服务器、定位根因到修复并持久化,一条龙。
背景
CloudCone 发了公告说 VPS 要迁移。迁移完成后,部署在上面的 reddit-summary 服务状态变成了 unhealthy。
reddit-summary 是一个 Next.js 应用,连接 Neon(Serverless PostgreSQL)作为数据库。迁移后,公网 IP 发生了变化,应用日志开始报数据库连接错误。
我对 pi agent 说:"登录 CloudCone,重启下 reddit-summary。"
接下来的事情,全部是 agent 自主完成的。
经过
第一步:以为是 DNS
Agent 登录服务器,读容器日志,赫然写着:
Error: getaddrinfo EAI_AGAIN ep-xxx-pooler.c-2.us-west-2.aws.neon.tech
DNS 解析失败?Agent 立刻测试,结果 DNS 完全正常。继续往下查。
第二步:重启容器——还是不行
重启 reddit-summary 容器,应用日志显示 ✓ Ready in 0ms,但 curl localhost:8000/en 直接超时。
Agent 进入容器,发现是数据库连接卡住了——查询一直 pending,最终超时。
第三步:逐层排查——TCP 通,TLS 死
这是排查中最关键的一步。Agent 开始逐层测试到 Neon 数据库的连通性:
| 测试项 | 结果 |
|---|---|
| DNS 解析 | ✅ 正常 |
| TCP 连接 Neon:5432 | ✅ 成功 |
| PG SSLRequest 握手 | ✅ 收到 'S' 响应 |
| TLS 握手 | ❌ 30 秒超时,卡死 |
TCP 能连上,PostgreSQL 协议层握手也正常,但 TLS 握手永远完不成。
更诡异的是,这不是 Neon 独有的问题——所有 AWS 区域的 TLS 连接都超时:
ec2.us-west-2.amazonaws.com: ❌ 超时
ec2.us-east-1.amazonaws.com: ❌ 超时
ec2.ap-northeast-1.amazonaws.com: ❌ 超时
而 Cloudflare 前端的站点却正常(ifconfig.me HTTP 200)。
第四步:排除嫌疑
Neon 的 IP 白名单? Neon 开启了公网访问,没有 IP 限制。DNS 返回的 IP 也完全一致,没有被劫持。
从其他服务器测试呢? Agent 换一台没有迁移的服务器测试同一个 Neon 数据库,TLS 握手完全正常。同样的数据库,别的机器能连,CloudCone 不能连。问题出在 CloudCone 迁移后的网络。
第五步:灵光一现——MTU
"TCP 能连上,但 TLS 握手超时"——Agent 识别出这个症状是 MTU 问题的典型表现。
TCP 握手用的是小包(SYN/SYN-ACK/ACK 都几十字节),PG 的 SSLRequest 只有 8 字节。但 TLS 握手中服务端要返回证书链,这是一个大包。如果路径中某处的 MTU 比预期低,大包就会被丢弃。
先测 MTU——带 DF 位的 ICMP ping 全部通过,1472 字节都没问题。ICMP 大包没问题,但 TCP/TLS 有问题。
区别在于:TCP 依赖 Path MTU Discovery (PMTUD),而 PMTUD 依赖 ICMP "需要分片" 消息。如果这些 ICMP 消息被屏蔽,TCP 就无法发现正确的 Path MTU。
Agent 直接验证——临时添加一条低 MTU 的路由:
$ ip route add <neon-ip> via <gateway> dev eth0 mtu 1200
$ python3 tls_test.py
TLS_OK: TLS_AES_256_GCM_SHA384 # ✅ 成功了!
实锤了。 降低 MTU 后 TLS 握手立即成功。
第六步:找到精确的 Path MTU
Agent 二分搜索定位边界:
| MTU | TLS 握手 |
|---|---|
| 1492 | ❌ 超时 |
| 1480 | ❌ 超时 |
| 1470 | ✅ 成功 |
| 1450 | ✅ 成功 |
Path MTU ≈ 1470(标准 1500 减去约 30 字节,这是 VXLAN/隧道封装的典型开销)。
第七步:修复——比想象中复杂
降低宿主机 eth0 的 MTU 到 1400:
$ ip link set eth0 mtu 1400
宿主机 TLS 连接恢复了。但 Docker 容器内的数据库连接还是超时!
Agent 发现原因:Docker 容器使用独立的网络命名空间,容器的虚拟网卡(veth)MTU 仍然是 1500。容器内 TCP 栈按 1500 计算分段大小,发出的数据包带 DF 位,超过路径 MTU 被丢弃。
先试了 iptables MSS clamping:
iptables -t mangle -A POSTROUTING -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360
规则匹配了 SYN 包,但容器发出的数据包仍然超限——TCPMSS target 只修改 SYN 包的 MSS 选项,不会拆分已经生成的数据包。容器的 TCP 栈根本不知道实际 Path MTU 是多少。
最终方案:让 Docker 网络本身使用 MTU 1400。
Agent 在四个层面做了持久化配置:
1. 宿主机网卡 /etc/network/interfaces → mtu 1400
2. Docker daemon /etc/docker/daemon.json → "mtu": 1400
3. Compose 网络 docker-compose.yml → networks.default.driver_opts.mtu: "1400"
4. iptables 兜底 TCPMSS --set-mss 1360 (netfilter-persistent 持久化)
重建容器网络后,容器内的 veth 接口 MTU 变为 1400,TCP 栈正确感知到较低的 MTU,所有连接恢复正常。
结果
服务恢复
| 检查项 | 结果 |
|---|---|
| reddit-summary 容器 | ✅ healthy |
| 数据库连接 | ✅ 276ms / 930 条记录 |
| HTTP 健康检查 | ✅ HTTP 200 |
重启验证
Agent 主动提议重启 VPS 验证持久化。重启后所有配置自动生效,容器自启动恢复,无需任何手动干预。
总结
根因:CloudCone VPS 迁移后,底层网络引入了 VXLAN 或类似的隧道封装,使实际 Path MTU 从 1500 降到了约 1470。同时 ICMP 分片消息被屏蔽,导致 PMTUD 失败。表现为 TCP 连接正常但大包传输失败——服务端返回的 TLS 证书链(大包)被静默丢弃,所有到 AWS 的 TLS 连接超时。
关键教训:
- "TCP 通但 TLS 超时" ≈ MTU/PMTUD 问题。 这个症状非常有辨识度,值得记住。
- ICMP ping 测 MTU 可能骗你。 ICMP 可能走不同路径或不设 DF 位。真正的问题是 TCP 大包在转发时被丢弃。
- Docker 容器的 MTU 需要单独配置。 宿主机 MTU 改了不够,容器的虚拟网卡、bridge 网络、compose 网络都要同步。最可靠的方式是在 Docker daemon 和 compose 网络层面设置
"mtu": 1400。 - iptables TCPMSS clamping 不是万能的。 它只改 SYN 包的 MSS 选项,对容器内 TCP 栈已经生成的大数据包无效。必须从网络接口 MTU 层面解决。
排查路径图:
服务 unhealthy
└── 日志: DB 连接超时
└── DNS 正常、TCP 正常、PG 握手正常
└── TLS 握手超时 ← 关键症状
└── 所有 AWS 区域都超时,Cloudflare 正常
└── 别的机器能连,CloudCone 不能连
└── 降低路由 MTU → TLS 成功 ← 实锤 MTU 问题
└── 宿主机修复 + Docker 网络修复 → 完全恢复
整个过程,Agent 自主完成了登录、诊断、修复、持久化、重启验证——从最初的一句指令到最终修复,中间没有人工介入。VPS 迁移后网络出问题,如果 TCP 能通但 TLS/HTTPS 卡住,第一个该查的就是 MTU。
我记得早有人说了改mtu到1420了
=。=
当时直接秒回的
到期一定会和这家说再见,永远不会再买。
这 cc 是不是得修复
@sinstar #1 哈哈,不太注意。只能说cloudcone太草台了,还得浪费我token给它擦屁股~~
tm 的我的3天了还没上线。
学到了,ai威武~
为什么迁移后 MTU 出了问题
正常情况下,网络包的 MTU 是 1500 字节。数据包从你的服务器发出去,沿途任何一
段链路的 MTU 如果低于 1500,路由器会回发一个 ICMP "包太大,需要分片" 消息,
你的 TCP 栈收到后就会自动降低发送的包大小。这个机制叫 PMTUD(Path MTU
Discovery)。
迁移后发生了什么:
实际 Path MTU 从 1500 降到了 ~1470
节
为什么是"小包通、大包死"
┌────────────────────┬─────────────────┬──────────────────────┐
│ 包类型 │ 大小 │ 结果 │
├────────────────────┼─────────────────┼──────────────────────┤
│ TCP 握手 (SYN/ACK) │ ~几十字节 │ ✅ 通过 │
├────────────────────┼─────────────────┼──────────────────────┤
│ PG SSLRequest │ 8 字节 │ ✅ 通过 │
├────────────────────┼─────────────────┼──────────────────────┤
│ TLS 证书链 │ 可达 1400+ 字节 │ ❌ 超过 1470,被丢弃 │
└────────────────────┴─────────────────┴──────────────────────┘
所以表现为:TCP 连接成功,但 TLS 握手永远完不成。
为什么修改 MTU 能修复
把服务器 MTU 从 1500 降到 1400,TCP 栈从一开始就只发 1400 字节以内的包,根本
不会触发那个坏掉的隧道瓶颈,绕开了 PMTUD 失效的问题。
简单说就是:隧道让路变窄了,ICMP 通知机制又坏了,那我就主动把车改小,不等你
通知了。
多谢!解决了相同的问题。
我敲,改完宿主机还得该容器配置,阿三不管了嘛
真的纯纯垃圾 还买了个30$一年的想着稳定了 稳个锤子...