一次网络问题排查

💡 原文中文,约2700字,阅读约需7分钟。
📝

内容提要

文章讲述了在搭建Jenkins时遇到的问题,通过收集信息和抓包分析发现是由于MTU太大导致的丢包问题。解决办法是通过iptables修改TCP握手包中的MSS值,从而解决了在Docker容器中访问HTTPS服务的问题。

🔎

延伸解读

MTU不匹配为何导致HTTPS超时

文章指出,宿主机MTU为1450,而Docker容器内默认MTU为1500,导致容器发出的数据包可能超过底层网络实际支持的1500字节上限。HTTP响应通常较小,不易触发问题;而HTTPS在TLS握手阶段需要交换证书等较大数据,容易产生超限包。这些超限包在中间设备被丢弃,且发送方无法收到ICMP通知,表现为443端口无响应,最终引发SocketTimeoutException。

MSS Clamping:一种有效的临时解决方案

作者通过iptables的TCPMSS目标,在FORWARD链上修改TCP握手包中的MSS值为1410(即1450减去40字节的IP和TCP头),使容器协商出更小的报文段,避免超过路径MTU。这种方法无需修改Docker或宿主机的MTU配置,即可解决因MTU不一致导致的HTTPS访问超时。但需注意,该规则仅对经过FORWARD链的流量生效,且可能影响其他连接。

排查思路:从现象到根因的推理

文章展示了系统的排查过程:先通过tcpdump定位到访问443端口卡住,再抓取DNS包找到域名,然后对比容器与宿主机的网络行为,发现HTTP正常而HTTPS超时。结合MTU差异,推断出大包被丢弃。这种从现象出发,逐步排除网络层、传输层问题,最终聚焦到MTU/MSS的推理方法,对类似网络问题有借鉴意义。

❓

Q&A

Jenkins 在 Docker 中报 java.net.SocketTimeoutException: Read timed out 错误,可能是什么原因?

可能是由于 Docker 容器内 MTU 设置过大(默认 1500),而宿主机 MTU 为 1450,导致 TCP 握手时 MSS 协商不当,使得 HTTPS 数据包超过路径 MTU 被丢弃,从而引发超时。

如何诊断 Docker 容器内 HTTPS 访问超时的问题?

可以使用 tcpdump 抓包分析:先抓 DNS 包找到目标域名,再抓 TCP 包观察连接建立后数据交换是否卡住。同时对比容器和宿主机的网络配置,检查 MTU 差异。

为什么容器内可以访问 HTTP 但 HTTPS 会超时?

因为 HTTPS 在 TLS 握手过程中需要交换证书等大量信息,数据包较大,容易超过 MTU 导致丢包;而 HTTP 响应通常较小,不易触发 MTU 问题。

Docker 容器内 MTU 和宿主机 MTU 不一致会导致什么问题?

如果容器内 MTU 大于宿主机 MTU,容器发出的数据包可能超过路径 MTU,导致中间设备丢弃,表现为连接超时或传输缓慢。

如何通过 iptables 修改 MSS 值来解决 Docker 容器 HTTPS 访问问题?

可以使用命令:iptables -I FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1410,将 TCP 握手包中的 MSS 强制改为 1410,从而避免 MTU 过大导致的丢包。

什么是 MSS clamping,它如何解决 MTU 问题?

MSS clamping 是通过 iptables 修改 TCP 握手包中的 MSS 值,使其小于路径 MTU,从而确保数据包不会因过大而被丢弃,解决因 MTU 不匹配导致的连接问题。

🏷️

标签

➡️

继续阅读