前言
干运维这么多年,见过各种各样的故障,但有些问题真的是让人抓狂。前段时间遇到的一个MTU问题,差点让我怀疑人生。表面上看是简单的丢包,实际上折腾了整整两天才定位到根因。今天就把这个案例完整地记录下来,顺便把MTU相关的知识点系统地梳理一遍,希望能帮到遇到类似问题的兄弟们。
说实话,MTU这个东西,很多人觉得不就是个数字嘛,有什么难的。但真正遇到问题的时候,你会发现水很深。特别是现在云原生环境下,各种overlay网络、隧道封装,MTU问题比以前复杂多了。
一、故障背景
1.1 环境介绍
先说下我们的环境背景。公司用的是混合云架构,自建机房跑着Kubernetes集群,同时也用了阿里云和AWS。业务是做在线教育的,有直播、点播、互动白板等模块。
基础设施情况:
自建机房:100多台物理服务器,跑着3个K8s集群
网络架构:核心交换机是华为CE12800,接入层是H3C S6800
Kubernetes版本:1.29.3
CNI插件:Cilium 1.15
服务网格:Istio 1.21
1.2 问题现象
那天下午3点多,突然接到告警,业务方反馈直播推流出现卡顿,而且是间歇性的。看了下监控,发现一个奇怪的现象:
# 某个服务的网络监控数据 TCP重传率: 2.3% (正常应该在0.1%以下) 丢包率: 1.8% 延迟: P99从5ms飙到了200ms
第一反应是网络抖动,让网络组的兄弟查了下核心交换机,没发现异常。接着排查了服务本身,CPU、内存、磁盘IO都正常。
最诡异的是,这个问题只在某些特定场景下才会出现。小文件传输没问题,一旦传大文件或者大数据包就开始丢包。ping是通的,telnet端口也是通的,但就是业务数据传不过去。
1.3 初步排查
按照常规套路,先看网络基础指标:
# 查看网卡统计信息 ip -s link show eth0 # 输出结果 2: eth0:mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 link/ether fa3exx:xx brd ffffff: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 dropped有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相关的基础知识捋一遍。这些东西可能有些老生常谈,但确实很重要。
2.1 什么是MTU
MTU(Maximum Transmission Unit)就是网络设备能够传输的最大数据包大小。这个概念是在数据链路层定义的,不同的网络技术有不同的MTU值:
| 网络类型 | MTU值 |
|---|---|
| Ethernet II | 1500字节 |
| PPPoE | 1492字节 |
| GRE隧道 | 1476字节 |
| VXLAN | 1450字节 |
| IPsec (ESP+AH) | 约1400字节 |
| Jumbo Frame | 9000字节 |
以太网的1500字节MTU是最常见的,这个数字从1980年代沿用至今。当时的考量是在传输效率和错误概率之间取得平衡。
2.2 MTU vs MSS
很多人分不清MTU和MSS的区别,这里说明一下:
+------------------+------------------+------------------+------------------+ | 以太网帧头 | IP头部 | TCP头部 | 数据 | | 14字节 | 20字节 | 20字节 | 最大1460字节 | +------------------+------------------+------------------+------------------+ |<-------------- MTU 1500字节 ---------------->| |<- MSS ->|
MTU:包含IP头和TCP头,以太网默认1500字节
MSS(Maximum Segment Size):只计算TCP数据部分,默认1460字节(1500-20-20)
TCP三次握手的时候,双方会协商MSS值。如果MSS设置不当,会导致分片或者丢包。
2.3 Path MTU Discovery
PMTUD(Path MTU Discovery)是用来自动发现整条链路上最小MTU的机制。工作原理:
发送端发送DF(Don't Fragment)标志位设置为1的数据包
如果中间路由器的MTU小于数据包大小,会返回ICMP "Fragmentation Needed"消息
发送端根据ICMP消息调整数据包大小
重复上述过程,直到数据包能够成功传输
问题是,很多防火墙会把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工作正常。
2.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
分片会带来几个问题:
增加CPU开销
任何一个分片丢失,整个包都要重传
分片攻击的安全风险
状态防火墙可能无法正确处理分片包
所以现代网络一般都不建议分片,而是在应用层控制数据包大小。
三、深入排查过程
回到我们的故障。既然怀疑是MTU问题,就开始针对性排查。
3.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限制,导致丢包。
3.2 抓包确认
用tcpdump抓包确认问题:
# 在物理机上抓包 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 Fragmentation Needed的包:
1545.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.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。
3.4 为什么之前没问题
这个问题困扰了我很久:配置一直是这样的,为什么之前没事,现在才出问题?
后来查了Cilium的更新日志才发现,1.15版本修改了默认的PMTUD行为。之前版本会自动处理MTU mismatch,新版本默认关闭了这个功能,需要手动开启。
# 查看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,所以没问题。这就解释了为什么问题是突然出现的。
四、解决方案
找到根因后,解决方案就比较清晰了。
4.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"
重启Cilium:
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
4.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
4.3 方案三:TCP MSS Clamping
如果没法改MTU,可以通过iptables调整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
在Kubernetes环境中,可以通过Cilium的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.4 我们采用的方案
综合考虑后,我们采用了方案一和方案三的组合:
把Cilium的MTU设置为1450
同时开启PMTUD
添加MSS clamping作为兜底
# 完整的解决脚本
#!/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问题在不同的网络环境下表现不同,这里总结一下各种场景的最佳配置。
5.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
5.2 虚拟机环境
虚拟化环境要考虑虚拟交换机的MTU:
# KVM/libvirt环境 # 修改虚拟网络配置 virsh net-edit default # 添加MTU配置# VMware环境 # 在vSphere中设置分布式交换机的MTU # vSphere Client -> Networking -> DVS -> Edit Settings -> MTU default ...
OpenStack环境需要同时设置多个组件:
# /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
5.3 容器环境
Docker单机环境:
# 修改Docker daemon配置
cat > /etc/docker/daemon.json << EOF
{
"mtu": 1450
}
EOF
systemctl restart docker
# 验证
docker network inspect bridge | grep -i mtu
Kubernetes环境要根据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"
5.4 云环境
各大云厂商的MTU限制不同:
| 云厂商 | 默认MTU | 最大MTU | 备注 |
|---|---|---|---|
| AWS | 1500 | 9001 | VPC内部支持Jumbo Frame |
| 阿里云 | 1500 | 1500 | 跨可用区有限制 |
| 腾讯云 | 1500 | 1500 | VPC内部统一 |
| Azure | 1500 | 1500 | ExpressRoute支持更大 |
| GCP | 1460 | 1460 | 因为使用了封装 |
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.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
}
5.6 SD-WAN和Overlay网络
企业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排查工具和方法。
6.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"
6.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
6.3 tracepath检测
tracepath比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
tracepath会自动发现整条路径的MTU,并显示在哪一跳发生了MTU变化。
6.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
6.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='�33[0;31m'
GREEN='�33[0;32m'
YELLOW='�33[1;33m'
NC='�33[0m'
echo "=========================================="
echo "MTU Diagnosis Tool v1.0"
echo "=========================================="
# 1. 检查所有接口MTU
echo -e "
${YELLOW}[1/6] Network Interfaces MTU${NC}"
ip -o link show | awk '{print $2, $5}' | column -t
# 2. 检查路由MTU
echo -e "
${YELLOW}[2/6] Route MTU Cache${NC}"
ip route show cache | grep -i mtu || echo "No cached MTU entries"
# 3. 检查内核参数
echo -e "
${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 "
${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 "
${YELLOW}[5/6] Recent ICMP Errors${NC}"
netstat -s | grep -i -E "frag|mtu|icmp" | head -10
# 6. 检查iptables MSS规则
echo -e "
${YELLOW}[6/6] iptables MSS Rules${NC}"
iptables -t mangle -L -n | grep -i mss || echo "No MSS clamping rules found"
echo -e "
${GREEN}Diagnosis completed!${NC}"
七、Kubernetes环境MTU最佳实践
Kubernetes环境下MTU问题更复杂,这里专门讲讲K8s的MTU配置。
7.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
7.2 服务网格的MTU考虑
Istio Envoy Sidecar会增加网络复杂度:
# 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,但需要确保Sidecar注入不影响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
7.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字节
Cilium Cluster 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"
7.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
}
7.5 NetworkPolicy与MTU
NetworkPolicy通常不影响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
八、高级场景和边界情况
8.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: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
8.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
}
8.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
8.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;
}
Cilium的eBPF MTU处理:
# 查看Cilium的BPF程序 bpftool prog show # 查看MTU相关的map bpftool map show name cilium_metrics
九、生产环境MTU配置规范
根据多年的经验,总结一套生产环境的MTU配置规范。
9.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
9.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:"'
9.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 }}"
Grafana面板配置:
{
"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 }}"
}
]
}
]
}
9.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
十、总结和经验教训
10.1 这次故障的教训
回顾这次MTU问题的排查过程,有几点教训:
升级前要看Release Notes:Cilium 1.15的变更导致了这个问题,如果升级前仔细看了Release Notes,可能就不会踩坑。
监控要覆盖网络层指标:之前我们的监控主要关注应用层,网络层的丢包、重传这些指标覆盖不够。
变更要有对照组:如果当时有个没升级的集群做对照,可能更快定位问题。
文档要及时更新:环境的MTU配置没有统一的文档记录,排查时浪费了不少时间。
10.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配置 ├── 测试不同大小的包 └── 观察监控指标
10.3 预防措施
为了避免类似问题再次发生,我们做了以下改进:
标准化MTU配置:所有环境使用统一的MTU值(1450),并写入Ansible Playbook自动化配置。
增加监控告警:在Prometheus中添加了丢包率、重传率的告警规则。
变更CheckList:升级CNI插件前必须检查MTU相关配置变化。
定期健康检查:每天自动运行MTU健康检查脚本。
文档化:把所有网络配置(包括MTU)都记录在CMDB中。
10.4 参考资料
写这篇文章的时候参考了一些资料,列在这里供大家参考:
RFC 1191 - Path MTU Discovery
RFC 8899 - Packetization Layer Path MTU Discovery for Datagram Transports
Cilium Documentation - MTU Configuration
Calico Documentation - Configure MTU
Linux Kernel Documentation - ip-sysctl.txt
各云厂商的VPC网络文档
10.5 写在最后
MTU这个东西,说简单也简单,就是个数字;说复杂也复杂,涉及到整个网络栈的方方面面。特别是在现在这种云原生、多层封装的环境下,MTU问题比以前更容易出现,也更难排查。
希望这篇文章能帮到遇到类似问题的兄弟们。如果有什么问题或者更好的方法,欢迎交流讨论。
最后说一句,做运维这行,遇到问题不可怕,可怕的是遇到问题不记录、不总结。每次故障都是一次学习的机会,把经验积累下来,下次遇到类似问题就能更快解决。
全部0条评论
快来发表一下你的评论吧 !