cmdb CMDB运维实战:MTU故障排查两天,云原生环境避坑指南
前言
干运维这么多年,见过各种各样的故障,但有些问题真的是让人抓狂。前段时间遇到的一个MTU问题,差点让我怀疑人生。表面上看是简单的丢包,实际上折腾了整整两天才定位到根因。今天就把这个案例完整地记录下来,顺便把MTU相关的知识点系统地梳理一遍,希望能帮到遇到类似问题的兄弟们。
说实话,MTU这个东西,很多人觉得不就是个数字嘛,有什么难的。但真正遇到问题的时候,你会发现水很深。特别是现在云原生环境下,各种网络、隧道封装,MTU问题比以前复杂多了。
一、故障背景
1、环境介绍
先说下我们的环境背景。公司用的是混合云架构,自建机房跑着集群,同时也用了阿里云和AWS。业务是做在线教育的,有直播、点播、互动白板等模块。
基础设施情况:
2、问题现象
那天下午3点多,突然接到告警,业务方反馈直播推流出现卡顿,而且是间歇性的。看了下监控,发现一个奇怪的现象:
# 某个服务的网络监控数据
TCP重传率: 2.3% (正常应该在0.1%以下)
丢包率: 1.8%
延迟: P99从5ms飙到了200ms
第一反应是网络抖动,让网络组的兄弟查了下核心交换机,没发现异常。接着排查了服务本身,CPU、内存、磁盘IO都正常。
最诡异的是,这个问题只在某些特定场景下才会出现。小文件传输没问题,一旦传大文件或者大数据包就开始丢包。ping是通的,端口也是通的,但就是业务数据传不过去。
3、初步排查
按照常规套路,先看网络基础指标:
# 查看网卡统计信息
ip -s link show eth0
# 输出结果
2: eth0: mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether fa:16:3e:xx:xx:xx brd ff:ff:ff:ff:ff:ff
RX: bytes packets errors dropped overrun mcast
892734821 6823421 0 0 0 0
TX: bytes packets errors dropped carrier collsns
723891234 5234123 0 1823 0 0
TX 有1823个包被丢弃了,这个数字在增长。继续深入:
# 查看更详细的网卡统计
ethtool -S eth0 | grep -i drop
# 输出
tx_dropped: 1823
rx_dropped: 0
tx_window_errors: 0
丢包发生在发送端。再看看系统日志:
dmesg | grep -i "too long"
# 发现了这个
[89234.123456] eth0: dropped packet, size 1514 > 1500
看到这个日志,心里大概有数了,八成是MTU问题。
二、MTU基础知识回顾
在深入排查之前,先把MTU相关的基础知识捋一遍。这些东西可能有些老生常谈,但确实很重要。
1、什么是MTU
MTU( Unit)就是网络设备能够传输的最大数据包大小。这个概念是在数据链路层定义的,不同的网络技术有不同的MTU值:
以太网的1500字节MTU是最常见的,这个数字从1980年代沿用至今。当时的考量是在传输效率和错误概率之间取得平衡。
2、 MTU vs MSS
很多人分不清MTU和MSS的区别,这里说明一下:
+------------------+------------------+------------------+------------------+
| 以太网帧头 | IP头部 | TCP头部 | 数据 |
| 14字节 | 20字节 | 20字节 | 最大1460字节 |
+------------------+------------------+------------------+------------------+
|<-------------- MTU 1500字节 ---------------->|
|<- MSS ->|
TCP三次握手的时候,双方会协商MSS值。如果MSS设置不当,会导致分片或者丢包。
3、Path MTU
PMTUD(Path MTU )是用来自动发现整条链路上最小MTU的机制。工作原理:
问题是,很多防火墙会把ICMP包干掉,导致PMTUD失效。这就是著名的"PMTUD黑洞"问题。
# 测试PMTUD是否正常工作
ping -M do -s 1472 192.168.1.1
# -M do: 设置DF标志,不允许分片
# -s 1472: 1472 + 8(ICMP头) + 20(IP头) = 1500
如果1472能ping通但1473 ping不通,说明MTU是1500且PMTUD工作正常。
4、分片与重组
当数据包大于MTU时,如果DF标志没有设置,IP层会进行分片:
# 查看分片统计
cat /proc/net/snmp | grep -i frag
# 输出示例
Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates
Ip: 1 64 12893456 0 0 0 0 0 12893456 10234567 0 0 0 0 0 0 0 0 0
分片会带来几个问题:
所以现代网络一般都不建议分片,而是在应用层控制数据包大小。
三、深入排查过程
回到我们的故障。既然怀疑是MTU问题,就开始针对性排查。
1、确认MTU配置
首先检查各个节点的MTU配置:
# 物理机网卡MTU
ip link show eth0 | grep mtu
# 输出: mtu 1500
# K8s节点的CNI网卡
ip link show cilium_host | grep mtu
# 输出: mtu 1500
# Pod内部网卡
kubectl exec -it test-pod -- ip link show eth0
# 输出: mtu 1500
# 检查VXLAN隧道接口
ip link show cilium_vxlan | grep mtu
# 输出: mtu 1500 <-- 问题出在这里!
发现问题了!VXLAN隧道接口的MTU也是1500,但VXLAN封装会额外增加50字节的开销:
VXLAN封装开销:
- 外层以太网头: 14字节
- 外层IP头: 20字节
- 外层UDP头: 8字节
- VXLAN头: 8字节
总共: 50字节
也就是说,原始数据包如果是1500字节,加上VXLAN封装后会变成1550字节,超过了物理网卡的MTU限制,导致丢包。
2、抓包确认
用抓包确认问题:
# 在物理机上抓包
tcpdump -i eth0 -nn 'icmp[0]=3 and icmp[1]=4'
# 在Pod内发送大包
kubectl exec -it test-pod -- ping -M do -s 1472 10.244.1.100
果然抓到了ICMP 的包:
15:23:45.123456 IP 192.168.1.1 > 10.244.0.5: ICMP 10.244.1.100 unreachable - need to frag (mtu 1450), length 556
交换机告诉我们MTU应该是1450,但我们的VXLAN接口设置的是1500,所以大包就被丢了。
3、验证问题
写个简单的脚本来验证不同包大小的通信情况:
#!/bin/bash
# mtu_test.sh - 测试不同包大小的连通性
TARGET_IP=$1
START_SIZE=1400
END_SIZE=1500
echo "Testing MTU to $TARGET_IP"
echo "========================="
for size in $(seq $START_SIZE $END_SIZE); do
if ping -M do -c 1 -s $size -W 1 $TARGET_IP > /dev/null 2>&1; then
echo "Size $size: OK"
else
echo "Size $size: FAIL <-- MTU boundary"
break
fi
done
运行结果:
Testing MTU to 10.244.1.100
=========================
Size 1400: OK
Size 1401: OK
...
Size 1422: OK
Size 1423: FAIL <-- MTU boundary
1422 + 8(ICMP头) + 20(IP头) = 1450,确认了实际的Path MTU就是1450。
4、为什么之前没问题
这个问题困扰了我很久:配置一直是这样的,为什么之前没事,现在才出问题?
后来查了的更新日志才发现,1.15版本修改了默认的PMTUD行为。之前版本会自动处理MTU ,新版本默认关闭了这个功能,需要手动开启。
# 查看Cilium版本
cilium version
# 查看相关配置
kubectl -n kube-system get configmap cilium-config -o yaml | grep -i mtu
# 输出
enable-pmtu-discovery: "false" # 这个是关闭的
mtu: "0" # 0表示自动检测,但检测的是本地MTU
另外,我们的业务最近上线了一个新功能,传输的数据包变大了。以前的小包刚好不超过1450,所以没问题。这就解释了为什么问题是突然出现的。
四、解决方案
找到根因后,解决方案就比较清晰了。
1、方案一:调整Pod网络MTU
最直接的方法是把Pod网络的MTU调小,留出VXLAN封装的空间:
# Cilium ConfigMap修改
apiVersion: v1
kind: ConfigMap
metadata:
name: cilium-config
namespace: kube-system
data:
mtu: "1450"
enable-pmtu-discovery: "true"
重启:
kubectl -n kube-system rollout restart daemonset/cilium
验证配置生效:
# 检查cilium_host接口MTU
kubectl -n kube-system exec -it cilium-xxxxx -- ip link show cilium_host
# 检查Pod内的MTU
kubectl exec -it test-pod -- ip link show eth0
2、方案二:开启Jumbo Frame
如果你的网络环境支持,可以考虑开启巨型帧(Jumbo Frame),把MTU设置为9000:
# 在所有物理节点上设置
ip link set eth0 mtu 9000
# 永久生效,修改网卡配置
# CentOS/RHEL
cat >> /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
MTU=9000
EOF
# Ubuntu/Debian
cat >> /etc/netplan/01-netcfg.yaml << EOF
ethernets:
eth0:
mtu: 9000
EOF
# 应用配置
netplan apply
注意,Jumbo Frame需要整条链路上的所有设备都支持,包括:
如果中间有任何设备不支持9000 MTU,就会出问题。
# 检查交换机端口是否支持Jumbo Frame
# 华为设备
display interface GigabitEthernet0/0/1 | include MTU
# H3C设备
display interface GigabitEthernet1/0/1 | include MTU
3、方案三:TCP MSS
如果没法改MTU,可以通过调整TCP MSS:
# 在FORWARD链上做MSS clamping
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
# 或者指定固定值
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1400
# 持久化规则
iptables-save > /etc/iptables/rules.v4
在环境中,可以通过的BPF程序实现类似功能:
# Cilium配置
apiVersion: v1
kind: ConfigMap
metadata:
name: cilium-config
namespace: kube-system
data:
enable-bpf-masquerade: "true"
enable-endpoint-routes: "true"
auto-direct-node-routes: "true"
4、我们采用的方案
综合考虑后,我们采用了方案一和方案三的组合:
# 完整的解决脚本
#!/bin/bash
# fix_mtu.sh
# 1. 更新Cilium配置
kubectl -n kube-system patch configmap cilium-config --type merge -p '
{
"data": {
"mtu": "1450",
"enable-pmtu-discovery": "true"
}
}'
# 2. 重启Cilium
kubectl -n kube-system rollout restart daemonset/cilium
# 3. 等待重启完成
kubectl -n kube-system rollout status daemonset/cilium
# 4. 添加MSS clamping(在所有节点执行)
for node in $(kubectl get nodes -o name | cut -d/ -f2); do
ssh $node "iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu"
done
# 5. 验证修复
kubectl exec -it test-pod -- ping -M do -s 1422 10.244.1.100
echo "MTU fix completed!"
五、不同场景下的MTU配置
MTU问题在不同的网络环境下表现不同,这里总结一下各种场景的最佳配置。
1、物理机环境
最简单的场景,直接设置网卡MTU即可:
# 临时设置
ip link set eth0 mtu 1500
# 永久设置 - systemd-networkd
cat > /etc/systemd/network/10-eth0.network << EOF
[Match]
Name=eth0
[Network]
DHCP=yes
[Link]
MTUBytes=1500
EOF
systemctl restart systemd-networkd
# 验证
ip link show eth0 | grep mtu
2、虚拟机环境
虚拟化环境要考虑虚拟交换机的MTU:
# KVM/libvirt环境
# 修改虚拟网络配置
virsh net-edit default
# 添加MTU配置
default
...
# VMware环境
# 在vSphere中设置分布式交换机的MTU
# vSphere Client -> Networking -> DVS -> Edit Settings -> MTU
环境需要同时设置多个组件:
# /etc/neutron/plugins/ml2/ml2_conf.ini
[ml2]
path_mtu = 1500
physical_network_mtus = provider:1500
# /etc/neutron/plugins/ml2/openvswitch_agent.ini
[ovs]
of_inactivity_probe = 10
# /etc/nova/nova.conf
[DEFAULT]
network_device_mtu = 1450
3、容器环境
单机环境:
# 修改Docker daemon配置
cat > /etc/docker/daemon.json << EOF
{
"mtu": 1450
}
EOF
systemctl restart docker
# 验证
docker network inspect bridge | grep -i mtu
环境要根据CNI插件配置:
# Calico
apiVersion: crd.projectcalico.org/v1
kind: FelixConfiguration
metadata:
name: default
spec:
mtu: 1450
wireguardMTU: 1420
# Flannel
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-system
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"MTU": 1450
}
}
# Cilium
apiVersion: v1
kind: ConfigMap
metadata:
name: cilium-config
namespace: kube-system
data:
mtu: "1450"
4、云环境

各大云厂商的MTU限制不同:
AWS VPC配置Jumbo Frame:
# 检查实例是否支持Jumbo Frame
aws ec2 describe-instances --instance-ids i-1234567890abcdef0 \
--query 'Reservations[].Instances[].NetworkInterfaces[].Groups'
# 在实例内设置MTU
sudo ip link set eth0 mtu 9001
# 持久化 - Amazon Linux 2
echo 'MTU=9001' | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth0
阿里云环境注意事项:
# 阿里云的ENI弹性网卡有MTU限制
# 主网卡固定1500,不可修改
# 如果使用Terway CNI(阿里云官方K8s网络方案)
# 需要考虑ENI的MTU限制
# 检查当前MTU
ip link show eth0
# 阿里云VPC内Pod网络推荐MTU
# - 普通VPC网络: 1500
# - ENI多IP模式: 1500
# - IPVLAN模式: 1500
5、VPN和隧道环境
各种隧道封装的MTU开销:
# 常见隧道协议的开销
IPsec (ESP, tunnel mode): 52-73字节
IPsec (ESP, transport mode): 38-59字节
GRE: 24字节
VXLAN: 50字节
Geneve: 50字节 + 可变长度选项
WireGuard: 60字节
# WireGuard配置
[Interface]
PrivateKey = ...
Address = 10.0.0.1/24
MTU = 1420 # 1500 - 60(WireGuard) - 20(IP头)
# IPsec strongSwan配置
# /etc/ipsec.conf
conn myvpn
...
fragmentation = yes
# /etc/strongswan.d/charon.conf
charon {
fragment_size = 1400
}
6、SD-WAN和网络
企业SD-WAN环境的MTU配置:
# 以Cisco SD-WAN为例
# 边缘设备配置
interface GigabitEthernet0/0
ip mtu 1400
tcp adjust-mss 1360
# 控制平面配置
system
overlay-mtu 1450
control-mtu 1500
六、MTU问题排查工具箱
这里整理一套完整的MTU排查工具和方法。
1、基础诊断命令
# 查看所有接口的MTU
ip -d link show | grep -E "^[0-9]+:|mtu"
# 查看路由的MTU
ip route get 10.0.0.1
# 输出示例
10.0.0.1 via 192.168.1.1 dev eth0 src 192.168.1.100 uid 0
cache mtu 1450
# 查看TCP连接的MSS
ss -ti | head -20
# 输出示例
cubic wscale:7,7 rto:204 rtt:3.5/2 ato:40 mss:1448 pmtu:1500
# 查看系统的分片统计
cat /proc/net/snmp | grep -E "^Ip:" | head -2
# 查看网卡的详细统计
ethtool -S eth0 | grep -E "drop|error|frag"
2、PMTUD测试脚本
#!/bin/bash
# pmtud_test.sh - 完整的PMTUD测试脚本
TARGET=$1
START_MTU=${2:-1500}
if [ -z "$TARGET" ]; then
echo "Usage: $0 [start_mtu]"
exit 1
fi
echo "Testing Path MTU to $TARGET"
echo "Starting from MTU: $START_MTU"
echo "================================"
# 二分查找最大可用MTU
low=576
high=$START_MTU
last_success=0
while [ $low -le $high ]; do
mid=$(( (low + high) / 2 ))
payload=$(( mid - 28 )) # 减去IP头(20)和ICMP头(8)
if ping -M do -c 1 -s $payload -W 2 $TARGET > /dev/null 2>&1; then
last_success=$mid
low=$(( mid + 1 ))
else
high=$(( mid - 1 ))
fi
done
if [ $last_success -gt 0 ]; then
echo ""
echo "Path MTU to $TARGET: $last_success bytes"
echo "Recommended TCP MSS: $(( last_success - 40 )) bytes"
else
echo "Error: Cannot determine Path MTU"
fi
3、检测
比ping更适合检测MTU问题:
# 使用tracepath检测路径MTU
tracepath 10.0.0.1
# 输出示例
1?: [LOCALHOST] pmtu 1500
1: gateway 0.234ms
1: gateway 0.198ms
2: 10.0.0.1 0.456ms reached
Resume: pmtu 1500 hops 2 back 2
会自动发现整条路径的MTU,并显示在哪一跳发生了MTU变化。
4、抓包分析
# 抓取ICMP Fragmentation Needed消息
tcpdump -i eth0 -nn 'icmp[0]=3 and icmp[1]=4' -w /tmp/frag_needed.pcap
# 抓取所有ICMP错误消息
tcpdump -i eth0 -nn 'icmp[0]=3' -v
# 抓取带DF标志的大包
tcpdump -i eth0 -nn 'ip[6:2] & 0x4000 != 0 and len > 1400'
# 分析抓包文件
tshark -r /tmp/frag_needed.pcap -T fields -e ip.src -e ip.dst -e icmp.mtu
# 使用Wireshark过滤器
# icmp.type == 3 && icmp.code == 4
5、内核参数调优
# 查看当前PMTUD相关参数
sysctl -a | grep -E "pmtu|mtu"
# 关键参数说明
net.ipv4.ip_no_pmtu_disc = 0 # 0=启用PMTUD, 1=禁用
net.ipv4.tcp_mtu_probing = 1 # 0=禁用, 1=黑洞检测时启用, 2=始终启用
net.ipv4.tcp_base_mss = 1024 # TCP MTU探测的初始MSS
net.ipv4.route.min_pmtu = 552 # 最小PMTU值
# 优化配置
cat >> /etc/sysctl.d/99-mtu.conf << EOF
# MTU优化
net.ipv4.ip_no_pmtu_disc = 0
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_base_mss = 1024
# 路由缓存优化
net.ipv4.route.gc_timeout = 100
net.ipv4.route.min_pmtu = 552
EOF
sysctl -p /etc/sysctl.d/99-mtu.conf
6.6 一键诊断脚本
#!/bin/bash
# mtu_diagnosis.sh - MTU问题一键诊断
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "=========================================="
echo "MTU Diagnosis Tool v1.0"
echo "=========================================="
# 1. 检查所有接口MTU
echo -e "\n${YELLOW}[1/6] Network Interfaces MTU${NC}"
ip -o link show | awk '{print $2, $5}' | column -t
# 2. 检查路由MTU
echo -e "\n${YELLOW}[2/6] Route MTU Cache${NC}"
ip route show cache | grep -i mtu || echo "No cached MTU entries"
# 3. 检查内核参数
echo -e "\n${YELLOW}[3/6] Kernel MTU Parameters${NC}"
echo "ip_no_pmtu_disc: $(sysctl -n net.ipv4.ip_no_pmtu_disc)"
echo "tcp_mtu_probing: $(sysctl -n net.ipv4.tcp_mtu_probing)"
echo "tcp_base_mss: $(sysctl -n net.ipv4.tcp_base_mss)"
# 4. 检查丢包统计
echo -e "\n${YELLOW}[4/6] Drop Statistics${NC}"
for iface in $(ls /sys/class/net/); do
tx_dropped=$(cat /sys/class/net/$iface/statistics/tx_dropped 2>/dev/null)
if [ "$tx_dropped" != "0" ] && [ -n "$tx_dropped" ]; then
echo -e "${RED}$iface: tx_dropped=$tx_dropped${NC}"
fi
done
# 5. 检查最近的ICMP错误
echo -e "\n${YELLOW}[5/6] Recent ICMP Errors${NC}"
netstat -s | grep -i -E "frag|mtu|icmp" | head -10
# 6. 检查iptables MSS规则
echo -e "\n${YELLOW}[6/6] iptables MSS Rules${NC}"
iptables -t mangle -L -n | grep -i mss || echo "No MSS clamping rules found"
echo -e "\n${GREEN}Diagnosis completed!${NC}"
七、环境MTU最佳实践
环境下MTU问题更复杂,这里专门讲讲K8s的MTU配置。
1、CNI插件MTU配置对比
不同CNI插件的MTU处理方式不同:
# Calico - 自动检测或手动配置
kubectl -n kube-system get configmap calico-config -o yaml | grep -A5 cni_network_config
# Calico 推荐配置
apiVersion: crd.projectcalico.org/v1
kind: FelixConfiguration
metadata:
name: default
spec:
mtu: 1440 # VXLAN模式
vxlanMTU: 1410 # VXLAN接口
wireguardMTU: 1380 # WireGuard加密
# Flannel - 在ConfigMap中配置
kubectl -n kube-system get configmap kube-flannel-cfg -o yaml
# Cilium - 支持自动MTU检测
kubectl -n kube-system get configmap cilium-config -o yaml | grep mtu
# Canal (Calico + Flannel)
kubectl -n kube-system get configmap canal-config -o yaml
2、服务网格的MTU考虑
Istio Envoy 会增加网络复杂度:
# Istio MTU配置
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
defaultConfig:
proxyMetadata:
# 调整Envoy的buffer大小
ISTIO_META_NETWORK: network1
values:
global:
proxy:
# 资源配置
resources:
requests:
memory: 128Mi
Envoy本身不会改变MTU,但需要确保注入不影响MTU设置:
# 检查注入Sidecar后的MTU
kubectl exec -it my-pod -c istio-proxy -- ip link show eth0
# 检查Envoy的连接状态
kubectl exec -it my-pod -c istio-proxy -- curl localhost:15000/stats | grep -i mss
3、跨集群网络MTU
多集群场景下MTU更需要注意:
# Submariner 跨集群网络配置
apiVersion: submariner.io/v1alpha1
kind: Broker
metadata:
name: submariner-broker
spec:
globalCIDR: 242.0.0.0/8
---
apiVersion: submariner.io/v1alpha1
kind: ClusterGlobalEgressIP
metadata:
name: cluster-egress
spec:
# MTU需要考虑跨集群隧道开销
# IPsec: ~50字节
# WireGuard: ~60字节
Mesh配置:
# Cilium跨集群配置
apiVersion: v1
kind: ConfigMap
metadata:
name: cilium-config
namespace: kube-system
data:
cluster-name: cluster1
cluster-id: "1"
# 跨集群MTU设置
mtu: "1400"
enable-endpoint-routes: "true"
tunnel: "vxlan"
4、GPU和RDMA网络MTU
高性能计算场景需要特殊的MTU配置:
# NVIDIA GPU Direct RDMA需要Jumbo Frame
# Mellanox网卡配置
mlxconfig -d /dev/mst/mt4123_pciconf0 set LINK_TYPE_P1=2 LINK_TYPE_P2=2
# 设置IB网络MTU
ip link set ib0 mtu 4092
# RoCE v2配置
echo 4096 > /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/ndevs/0/mtu
# K8s RDMA Device Plugin配置
apiVersion: v1
kind: ConfigMap
metadata:
name: rdma-devices
data:
config.json: |
{
"mode": "shared",
"maxPods": 100
}
5、与MTU
通常不影响MTU,但某些实现可能会引入额外的处理开销:
# 确保NetworkPolicy不影响PMTUD
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-icmp
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- ports:
# 允许ICMP,确保PMTUD正常工作
- protocol: ICMP
# 检查是否有丢弃ICMP的规则
iptables -L -n | grep -i icmp
# Cilium的ICMP策略
kubectl get cnp -A -o yaml | grep -i icmp
八、高级场景和边界情况
1、IPv6环境的MTU
IPv6的最小MTU是1280字节,比IPv4的576字节大:
# 检查IPv6 MTU
ip -6 link show eth0 | grep mtu
# IPv6 Path MTU Discovery
ping6 -M do -s 1452 2001:db8::1
# IPv6的PMTUD使用ICMPv6
# Packet Too Big消息代替ICMP Fragmentation Needed
tcpdump -i eth0 'icmp6 and ip6[40] = 2'
IPv6隧道的MTU计算:
IPv6-in-IPv4 隧道: 1500 - 20(IPv4头) = 1480
IPv6-in-IPv6 隧道: 1500 - 40(IPv6头) = 1460
2、 容器运行时差异
不同容器运行时对MTU的处理:
# Docker
docker network create --driver bridge --opt com.docker.network.driver.mtu=1450 mynet
# containerd
# CNI配置文件
cat /etc/cni/net.d/10-containerd-net.conflist
{
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"mtu": 1450
}
]
}
# CRI-O
# 修改CNI配置
cat /etc/cni/net.d/100-crio-bridge.conf
{
"type": "bridge",
"mtu": 1450
}
3、负载均衡器MTU
各种LB的MTU处理:
# HAProxy - 不直接处理MTU,但可以调整buffer
# /etc/haproxy/haproxy.cfg
global
tune.bufsize 16384
tune.maxrewrite 1024
# Nginx - 可以调整proxy buffer
upstream backend {
server 10.0.0.1:80;
}
server {
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# IPVS/LVS - DR模式需要MTU一致
# 检查IPVS连接
ipvsadm -Ln
云负载均衡器通常有自己的MTU限制:
# AWS ALB/NLB
# MTU自动处理,无需配置
# 阿里云SLB
# 经典网络: 1500
# VPC网络: 1500
# 跨可用区: 注意MTU差异
# GCP Load Balancer
# 默认1460,因为GCP网络MTU是1460
4、eBPF和XDP对MTU的影响
eBPF程序可能影响数据包处理:
// XDP程序需要考虑MTU
SEC("xdp")
int xdp_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// 检查包大小
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
return XDP_PASS;
struct ethhdr *eth = data;
struct iphdr *iph = data + sizeof(struct ethhdr);
// 可以在这里检查和处理MTU相关逻辑
__u16 tot_len = ntohs(iph->tot_len);
if (tot_len > 1500 - sizeof(struct ethhdr)) {
// 包太大,记录或处理
}
return XDP_PASS;
}
的eBPF MTU处理:
# 查看Cilium的BPF程序
bpftool prog show
# 查看MTU相关的map
bpftool map show name cilium_metrics
九、生产环境MTU配置规范
根据多年的经验,总结一套生产环境的MTU配置规范。
1、配置清单模板
# mtu-config-template.yaml
# 生产环境MTU配置清单
# 1. 物理网络层
physical_network:
datacenter:
core_switch_mtu: 9000 # 如果支持Jumbo Frame
access_switch_mtu: 9000
server_nic_mtu: 9000
wan:
mtu: 1500 # 公网固定1500
# 2. 虚拟化层
virtualization:
hypervisor:
virtual_switch_mtu: 1500
vm_nic_mtu: 1500
# 3. 容器网络层
container_network:
cni_mtu: 1450 # VXLAN环境
pod_mtu: 1450
service_mesh_overhead: 0 # Istio不增加头部
# 4. 隧道和VPN
tunnels:
vxlan_mtu: 1450 # 1500 - 50
ipsec_mtu: 1400 # 保守值
wireguard_mtu: 1420 # 1500 - 80
# 5. 云环境
cloud:
aws_vpc_mtu: 9001 # 支持Jumbo Frame
aliyun_vpc_mtu: 1500
gcp_vpc_mtu: 1460
2、变更流程
#!/bin/bash
# mtu_change_procedure.sh - MTU变更标准流程
# 1. 变更前检查
echo "=== Pre-change Checks ==="
ip link show | grep mtu
ip route get 10.0.0.1
netstat -s | grep -i frag
# 2. 备份当前配置
echo "=== Backup Current Config ==="
ip link show > /tmp/mtu_backup_$(date +%Y%m%d).txt
cp /etc/network/interfaces /tmp/ 2>/dev/null
cp /etc/sysconfig/network-scripts/ifcfg-* /tmp/ 2>/dev/null
# 3. 灰度变更(先在一台机器测试)
echo "=== Gradual Change ==="
# 这里执行实际变更
# 4. 验证变更
echo "=== Post-change Verification ==="
# 测试连通性
ping -M do -s 1422 10.0.0.1
# 测试业务
curl -o /dev/null -w "%{time_total}" http://service:8080/health
# 5. 监控观察
echo "=== Monitoring ==="
# 观察丢包率和重传率变化
watch -n 1 'cat /proc/net/snmp | grep -E "^Tcp:"'
3、监控告警配置
# Prometheus告警规则
groups:
- name: mtu-alerts
rules:
# 高丢包率告警
- alert: HighPacketDrop
expr: rate(node_network_transmit_drop_total[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "High packet drop on {{ $labels.instance }}"
description: "Interface {{ $labels.device }} has high TX drops"
# TCP重传率告警
- alert: HighTCPRetransmit
expr: rate(node_netstat_Tcp_RetransSegs[5m]) / rate(node_netstat_Tcp_OutSegs[5m]) > 0.01
for: 5m
labels:
severity: warning
annotations:
summary: "High TCP retransmit rate on {{ $labels.instance }}"
# MTU不一致告警(自定义exporter)
- alert: MTUMismatch
expr: mtu_config_current != mtu_config_expected
for: 1m
labels:
severity: critical
annotations:
summary: "MTU mismatch detected on {{ $labels.instance }}"
面板配置:
{
"panels": [
{
"title": "Network Interface MTU",
"type": "table",
"targets": [
{
"expr": "node_network_mtu_bytes",
"legendFormat": "{{ device }}"
}
]
},
{
"title": "Packet Drop Rate",
"type": "graph",
"targets": [
{
"expr": "rate(node_network_transmit_drop_total[1m])",
"legendFormat": "TX Drop - {{ device }}"
},
{
"expr": "rate(node_network_receive_drop_total[1m])",
"legendFormat": "RX Drop - {{ device }}"
}
]
}
]
}
4、自动化检查脚本
#!/bin/bash
# mtu_health_check.sh - MTU健康检查自动化脚本
set -e
LOG_FILE="/var/log/mtu_health_check.log"
EXPECTED_MTU=1450
ALERT_THRESHOLD=100
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}
check_interface_mtu() {
log "Checking interface MTU..."
for iface in $(ls /sys/class/net/ | grep -v lo); do
current_mtu=$(cat /sys/class/net/$iface/mtu)
if [ "$current_mtu" != "$EXPECTED_MTU" ]; then
log "WARNING: $iface MTU is $current_mtu, expected $EXPECTED_MTU"
return 1
fi
done
log "All interface MTU values are correct"
return 0
}
check_packet_drops() {
log "Checking packet drops..."
for iface in $(ls /sys/class/net/ | grep -v lo); do
tx_dropped=$(cat /sys/class/net/$iface/statistics/tx_dropped)
if [ "$tx_dropped" -gt "$ALERT_THRESHOLD" ]; then
log "ALERT: $iface has $tx_dropped TX drops"
return 1
fi
done
log "Packet drop counts are within threshold"
return 0
}
check_pmtud() {
log "Checking PMTUD functionality..."
# 测试目标IP(需要替换为实际的测试目标)
TEST_TARGET="10.0.0.1"
if ping -M do -c 1 -s 1422 -W 2 $TEST_TARGET > /dev/null 2>&1; then
log "PMTUD is working correctly"
return 0
else
log "WARNING: PMTUD may not be working"
return 1
fi
}
check_kernel_params() {
log "Checking kernel MTU parameters..."
pmtu_disc=$(sysctl -n net.ipv4.ip_no_pmtu_disc)
mtu_probing=$(sysctl -n net.ipv4.tcp_mtu_probing)
if [ "$pmtu_disc" != "0" ]; then
log "WARNING: PMTU discovery is disabled"
fi
if [ "$mtu_probing" == "0" ]; then
log "INFO: TCP MTU probing is disabled"
fi
return 0
}
# 主函数
main() {
log "========== MTU Health Check Started =========="
errors=0
check_interface_mtu || ((errors++))
check_packet_drops || ((errors++))
check_pmtud || ((errors++))
check_kernel_params || ((errors++))
if [ $errors -gt 0 ]; then
log "Health check completed with $errors issues"
exit 1
else
log "Health check passed"
exit 0
fi
}
main
十、总结和经验教训
1、这次故障的教训
回顾这次MTU问题的排查过程,有几点教训:
1)升级前要看 Notes: 1.15的变更导致了这个问题,如果升级前仔细看了 Notes,可能就不会踩坑。
2)监控要覆盖网络层指标:之前我们的监控主要关注应用层,网络层的丢包、重传这些指标覆盖不够。
3)变更要有对照组:如果当时有个没升级的集群做对照,可能更快定位问题。
4)文档要及时更新:环境的MTU配置没有统一的文档记录,排查时浪费了不少时间。
2、MTU问题排查思路总结
MTU问题排查流程图:
1. 现象确认
├── 大包丢失,小包正常? → 很可能是MTU问题
├── 特定路径丢包? → 检查路径上的MTU
└── 随机丢包? → 可能不是MTU问题
2. 快速诊断
├── ping -M do -s 1472 目标IP
├── tracepath 目标IP
└── tcpdump抓ICMP错误
3. 定位问题点
├── 检查所有接口MTU
├── 检查隧道/overlay的MTU
└── 检查云环境的MTU限制
4. 验证修复
├── 调整MTU配置
├── 测试不同大小的包
└── 观察监控指标
3、预防措施
为了避免类似问题再次发生,我们做了以下改进:
1)标准化MTU配置:所有环境使用统一的MTU值(1450),并写入 自动化配置。
2)增加监控告警:在中添加了丢包率、重传率的告警规则。
3)变更:升级CNI插件前必须检查MTU相关配置变化。
4)定期健康检查:每天自动运行MTU健康检查脚本。
5)文档化:把所有网络配置(包括MTU)都记录在CMDB中。
4、参考资料
写这篇文章的时候参考了一些资料,列在这里供大家参考:
5、 写在最后
MTU这个东西,说简单也简单,就是个数字;说复杂也复杂,涉及到整个网络栈的方方面面。特别是在现在这种云原生、多层封装的环境下,MTU问题比以前更容易出现,也更难排查。
希望这篇文章能帮到遇到类似问题的兄弟们。如果有什么问题或者更好的方法,欢迎交流讨论。
最后说一句,做运维这行,遇到问题不可怕,可怕的是遇到问题不记录、不总结。每次故障都是一次学习的机会,把经验积累下来,下次遇到类似问题就能更快解决。
























