# Docker 与 OpenStack MTU 不匹配如何破坏 CI/CD:诊断与修复
周五晚上的构建失败往往不是随机故障——通常是 Docker 容器与 OpenStack 覆盖网络之间的 MTU 不匹配。只有在大规模数据传输时才会暴露问题,比如克隆仓库,小请求则一切正常。这会造成网络看似正常,直到真实负载来袭。
为什么小请求成功,大请求却失败
这个问题典型特征是选择性。像 curl -I 或 ping 这样的小数据包命令总是成功。问题出现在需要大块数据传输的操作上:git clone、docker pull、工件下载。罪魁祸首?最大传输单元(MTU)——无需分片即可传输的最大 IP 数据包大小。
标准以太网网络 MTU 为 1500 字节。但在 OpenStack 等云环境中,VXLAN 或 GRE 等覆盖技术通过为每个数据包添加约 50 字节开销来构建隔离网络。因此,即便物理网络支持 1500,虚拟机数据包实际只剩 1450 字节。
Docker 默认使用 MTU 1500 的桥接网络,忽略底层基础设施特性。容器按此规格构建数据包。当抵达主机 1450 MTU 接口时,因 DF(Don't Fragment,不分片)标志无法传输。理想情况下,路径 MTU 发现机制会介入,但防火墙、安全组或路由器问题常阻挡“需要分片”的 ICMP 消息。
30 秒诊断问题
用这三个命令快速发现 MTU 不匹配:
- 检查主机网络接口 MTU:
```
ip link show ens3 | grep mtu
```
在 OpenStack 中,预期值为 mtu 1450。
- 检查容器内 MTU:
```
docker run --rm alpine ip link show eth0 | grep mtu
```
Docker 默认 mtu 1500。如果高于主机,即为问题所在。
- 运行禁止分片的 ping 测试:
```
docker run --rm alpine ping -s 1422 -M do -c 3 gitlab.example.com
docker run --rm alpine ping -s 1423 -M do -c 3 gitlab.example.com
```
第一个(1422 = 1450 − 28)应成功;第二个应报 Message too long。
逐步修复
配置 Docker MTU
核心修复:让 Docker 使用正确 MTU。编辑 /etc/docker/daemon.json:
{
"mtu": 1450
}
重启 Docker 服务:
sudo systemctl restart docker
处理现有网络
注意:daemon.json 修改仅影响新网络。现存网络——尤其是 Docker Compose 创建的——保留旧 MTU。两种方案:
- 全部重建:
```
docker compose down && docker compose up -d
```
- 在 Docker Compose 中显式设置 MTU:
```yaml
networks:
app-net:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1450
```
收尾步骤
重启 GitLab Runner 并测试:
sudo gitlab-runner restart
docker run --rm alpine ping -s 1422 -M do -c 3 gitlab.example.com
测试通过,即大功告成。
常见陷阱与最佳实践
- 别盲目降低 MTU “以防万一”。降到 1300 会因额外数据包导致吞吐量下降 10-15%。使用精确诊断值。
- 自动化配置。将 MTU 写入基础设施配置(Terraform、Ansible、cloud-init)——避免手动调整。
- 注意自定义网络。
daemon.json不影响现有网络。用docker network inspect检查。 - 别指望路径 MTU 发现。云环境常因 ICMP 阻挡而失效。
关键要点
- MTU 不匹配仅在大规模数据传输时发作,隐蔽性强。
- OpenStack 覆盖网络头部占用 50 字节 MTU。
- Docker 默认 1500 在云中冲突。
- 修复:让 Docker MTU 匹配主机接口。
- 在部署流水线中自动化设置。
— Editorial Team
暂无评论。