为什么大包会被丢
MTU 是一条链路单帧能承载的最大字节数,以太网默认 1500。容器网卡看到的 MTU 由所用 Docker 网络决定:bridge 网络一般沿用宿主默认值,overlay 网络要为 VXLAN 封装头留出空间,Docker 给的默认值是 1450。当路径上真正可用的 MTU 更小(隧道、VPN、云专线常见),而发送方又置了不分片位时,超出的包会在中途被丢弃,表现为小请求正常、大响应或握手阶段长时间挂住。
基本信息
| 症状 | 小包正常,大包超时或长时间挂起,通常没有明确报错 |
|---|---|
| 容器侧查看 | docker exec <容器> ip link show eth0 看 MTU 字段 |
| 路径探测 | ping -M do -s <负载> <目标>,IPv4 下负载 = MTU 减 28 |
| 按网络调整 | docker network create -o com.docker.network.driver.mtu=1400 |
| daemon 级默认 | /etc/docker/daemon.json 的 mtu 字段,只影响新建网络 |
| 封装开销 | VXLAN 增加 50 字节,overlay 默认 MTU 因此为 1450 |
| 生效时机 | 仅在网络创建时写入,运行中不可修改 |
典型使用场景
- 跨主机 overlay 网络上传输大文件或大响应接口时连接挂住
- 容器经 VPN 或专线访问远端数据库,连接能建立但大查询不返回
- 登录宿主机 SSH 时认证握手中途卡死,而 ping 一直是通的
- 同一个小接口用 curl 请求正常,换大数据量就超时
- 排查含糊的慢问题前,先确认两端与路径 MTU 是否一致
探测与调整
探测与调整
docker exec web ip link show eth0 | head -3 # 看容器网卡 MTU 字段
ip link show eth0 # 宿主侧同名字段做对比
ping -M do -s 1472 -c 3 10.0.0.10 # 1472+28=1500,不通说明路径 MTU 更小
ping -M do -s 1372 -c 3 10.0.0.10 # 逐级下调,试探真实可用值
docker network create -o com.docker.network.driver.mtu=1400 appnet-mtu # 按网络设定
docker network inspect appnet-mtu -f '{{.Options}}' # 确认选项已写入MTU 在各层的来源
| 位置 | 默认值来源 | 调整方式 |
|---|---|---|
| 容器网卡(bridge) | 跟随 docker0 或网络创建时的设置 | 创建网络时指定,或改 daemon 的 mtu 后重启 |
| 容器网卡(overlay) | 默认 1450,为 VXLAN 封装预留 50 字节 | 创建 overlay 网络时用 driver opt 指定 |
| 宿主物理网卡 | 网卡配置或 DHCP | ip link set 临时改,持久化写法随发行版而定 |
| 路径中间设备 | 隧道、专线、VPN 的封装层 | 端到端评估,必要时对 TCP 做 MSS 钳制 |
网络的 MTU 只在创建时写入,运行中改不了。已经在用的网络只能重建,上面的容器也要跟着重建;临时验证可以在容器里执行 ip link set eth0 mtu 1400,但容器重启后就失效,别把它当持久方案。
宿主上对 TCP 做 MSS 钳制(把 MSS 限制在路径 MTU 内)能缓解 TCP 大包被丢的问题,但它只管 TCP,UDP 与 ICMP 不受益,属于绕过而不是修好,长期应把 MTU 调到一致。
别只调一侧:容器侧调小而宿主与对端仍是 1500,问题会转移而不是消失;宿主调小了而容器还用旧值,同样会出问题。改动要沿着整条路径一起评估。