前言
干运维这么多年,见过各种各样的故障,但有些问题真的是让人抓狂。前段时间遇到的一个MTU问题,差点让我怀疑人生。表面上看是简单的丢包,实际上折腾了整整两天才定位到根因。今天就把这个案例完整地记录下来,顺便把MTU相关的知识点系统地梳理一遍,希望能帮到遇到类似问题的兄弟们。
说实话,MTU这个东西,很多人觉得不就是个数字嘛,有什么难的。但真正遇到问题的时候,你会发现水很深。特别是现在云原生环境下,各种overlay网络、隧道封装,MTU问题比以前复杂多了。
一、故障背景
先说下我们的环境背景。公司用的是混合云架构,自建机房跑着Kubernetes集群,同时也用了阿里云和AWS。业务是做在线教育的,有直播、点播、互动白板等模块。
基础设施情况:
自建机房:100多台物理服务器,跑着3个K8s集群
网络架构:核心交换机是华为CE12800,接入层是H3C S6800
Kubernetes版本:1.29.3
CNI插件:Cilium 1.15
服务网格:Istio 1.21
那天下午3点多,突然接到告警,业务方反馈直播推流出现卡顿,而且是间歇性的。看了下监控,发现一个奇怪的现象:
# 某个服务的网络监控数据TCP重传率: 2.3% (正常应该在0.1%以下)丢包率: 1.8%延迟: P99从5ms飙到了200ms
第一反应是网络抖动,让网络组的兄弟查了下核心交换机,没发现异常。接着排查了服务本身,CPU、内存、磁盘IO都正常。
最诡异的是,这个问题只在某些特定场景下才会出现。小文件传输没问题,一旦传大文件或者大数据包就开始丢包。ping是通的,telnet端口也是通的,但就是业务数据传不过去。
按照常规套路,先看网络基础指标:
# 查看网卡统计信息ip -s link show eth0# 输出结果2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000link/ether fa:16:3e:xx:xx:xx brd ff:ff:ff:ff:ff:ffRX: bytes packets errors dropped overrun mcast892734821 6823421 0 0 0 0TX: bytes packets errors dropped carrier collsns723891234 5234123 0 1823 0 0
TX dropped有1823个包被丢弃了,这个数字在增长。继续深入:
# 查看更详细的网卡统计ethtool -S eth0 | grep -i drop# 输出tx_dropped: 1823rx_dropped: 0tx_window_errors: 0
丢包发生在发送端。再看看系统日志:
dmesg | grep -i "too long"[] eth0: dropped packet, size 1514 > 1500
看到这个日志,心里大概有数了,八成是MTU问题。
二、MTU基础知识回顾
在深入排查之前,先把MTU相关的基础知识捋一遍。这些东西可能有些老生常谈,但确实很重要。
MTU(Maximum Transmission Unit)就是网络设备能够传输的最大数据包大小。这个概念是在数据链路层定义的,不同的网络技术有不同的MTU值:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
以太网的1500字节MTU是最常见的,这个数字从1980年代沿用至今。当时的考量是在传输效率和错误概率之间取得平衡。
很多人分不清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设置不当,会导致分片或者丢包。
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工作正常。
当数据包大于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 FragCreatesIp: 1 64 12893456 0 0 0 0 0 12893456 10234567 0 0 0 0 0 0 0 0 0
分片会带来几个问题:
增加CPU开销
任何一个分片丢失,整个包都要重传
分片攻击的安全风险
状态防火墙可能无法正确处理分片包
所以现代网络一般都不建议分片,而是在应用层控制数据包大小。
三、深入排查过程
回到我们的故障。既然怀疑是MTU问题,就开始针对性排查。
首先检查各个节点的MTU配置:
# 物理机网卡MTUip 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限制,导致丢包。
用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的包:
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,所以大包就被丢了。
写个简单的脚本来验证不同包大小的通信情况:
# mtu_test.sh - 测试不同包大小的连通性TARGET_IP=$1START_SIZE=1400END_SIZE=1500echo "Testing MTU to $TARGET_IP"echo "========================="for size in $(seq $START_SIZE $END_SIZE); doif ping -M do -c 1 -s $size -W 1 $TARGET_IP > /dev/null 2>&1; thenecho "Size $size: OK"elseecho "Size $size: FAIL <-- MTU boundary"breakfidone
运行结果:
Testing MTU to 10.244.1.100=========================Size 1400: OKSize 1401: OK...Size 1422: OKSize 1423: FAIL <-- MTU boundary
1422 + 8(ICMP头) + 20(IP头) = 1450,确认了实际的Path MTU就是1450。
这个问题困扰了我很久:配置一直是这样的,为什么之前没事,现在才出问题?
后来查了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,所以没问题。这就解释了为什么问题是突然出现的。
四、解决方案
找到根因后,解决方案就比较清晰了。
最直接的方法是把Pod网络的MTU调小,留出VXLAN封装的空间:
# Cilium ConfigMap修改apiVersion: v1kind: ConfigMapmetadata:name: cilium-confignamespace: kube-systemdata:mtu: "1450"enable-pmtu-discovery: "true"
重启Cilium:
kubectl -n kube-system rollout restart daemonset/cilium
验证配置生效:
# 检查cilium_host接口MTUkubectl -n kube-system exec -it cilium-xxxxx -- ip link show cilium_host# 检查Pod内的MTUkubectl exec -it test-pod -- ip link show eth0
如果你的网络环境支持,可以考虑开启巨型帧(Jumbo Frame),把MTU设置为9000:
# 在所有物理节点上设置ip link set eth0 mtu 9000# 永久生效,修改网卡配置# CentOS/RHELcat >> /etc/sysconfig/network-scripts/ifcfg-eth0 << EOFMTU=9000EOF# Ubuntu/Debiancat >> /etc/netplan/01-netcfg.yaml << EOFethernets:eth0:mtu: 9000EOF# 应用配置netplan apply
注意,Jumbo Frame需要整条链路上的所有设备都支持,包括:
服务器网卡
交换机所有端口
路由器
负载均衡器
如果中间有任何设备不支持9000 MTU,就会出问题。
# 检查交换机端口是否支持Jumbo Frame# 华为设备display interface GigabitEthernet0/0/1 | include MTU# H3C设备display interface GigabitEthernet1/0/1 | include MTU
如果没法改MTU,可以通过iptables调整TCP MSS:
# 在FORWARD链上做MSS clampingiptables -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: v1kind: ConfigMapmetadata:name: cilium-confignamespace: kube-systemdata:enable-bpf-masquerade: "true"enable-endpoint-routes: "true"auto-direct-node-routes: "true"
综合考虑后,我们采用了方案一和方案三的组合:
把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. 重启Ciliumkubectl -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); dossh $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.100echo "MTU fix completed!"
五、不同场景下的MTU配置
MTU问题在不同的网络环境下表现不同,这里总结一下各种场景的最佳配置。
最简单的场景,直接设置网卡MTU即可:
# 临时设置ip link set eth0 mtu 1500# 永久设置 - systemd-networkdcat > /etc/systemd/network/10-eth0.network << EOF[Match]Name=eth0[Network]DHCP=yes[Link]MTUBytes=1500EOFsystemctl restart systemd-networkd# 验证ip link show eth0 | grep mtu
虚拟化环境要考虑虚拟交换机的MTU:
# KVM/libvirt环境# 修改虚拟网络配置virsh net-edit default# 添加MTU配置<network><name>default</name><bridge name='virbr0' mtu='1500'/><mtu size='1500'/>...</network># VMware环境# 在vSphere中设置分布式交换机的MTU# vSphere Client -> Networking -> DVS -> Edit Settings -> MTU
OpenStack环境需要同时设置多个组件:
# /etc/neutron/plugins/ml2/ml2_conf.ini[ml2]path_mtu = 1500physical_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
Docker单机环境:
# 修改Docker daemon配置cat > /etc/docker/daemon.json << EOF{"mtu": 1450}EOFsystemctl restart docker# 验证docker network inspect bridge | grep -i mtu
Kubernetes环境要根据CNI插件配置:
# CalicoapiVersion: crd.projectcalico.org/v1kind: FelixConfigurationmetadata:name: defaultspec:mtu: 1450wireguardMTU: 1420# FlannelapiVersion: v1kind: ConfigMapmetadata:name: kube-flannel-cfgnamespace: kube-systemdata:net-conf.json: |{"Network": "10.244.0.0/16","Backend": {"Type": "vxlan","MTU": 1450}}# CiliumapiVersion: v1kind: ConfigMapmetadata:name: cilium-confignamespace: kube-systemdata:mtu: "1450"
各大云厂商的MTU限制不同:
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AWS VPC配置Jumbo Frame:
# 检查实例是否支持Jumbo Frameaws ec2 describe-instances --instance-ids i-1234567890abcdef0 \--query 'Reservations[].Instances[].NetworkInterfaces[].Groups'# 在实例内设置MTUsudo ip link set eth0 mtu 9001# 持久化 - Amazon Linux 2echo 'MTU=9001' | sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth0
阿里云环境注意事项:
# 阿里云的ENI弹性网卡有MTU限制# 主网卡固定1500,不可修改# 如果使用Terway CNI(阿里云官方K8s网络方案)# 需要考虑ENI的MTU限制# 检查当前MTUip link show eth0# 阿里云VPC内Pod网络推荐MTU# - 普通VPC网络: 1500# - ENI多IP模式: 1500# - IPVLAN模式: 1500
各种隧道封装的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/24MTU = 1420 # 1500 - 60(WireGuard) - 20(IP头)# IPsec strongSwan配置# /etc/ipsec.confconn myvpn...fragmentation = yes# /etc/strongswan.d/charon.confcharon {fragment_size = 1400}
企业SD-WAN环境的MTU配置:
# 以Cisco SD-WAN为例# 边缘设备配置interface GigabitEthernet0/0ip mtu 1400tcp adjust-mss 1360# 控制平面配置systemoverlay-mtu 1450control-mtu 1500
六、MTU问题排查工具箱
这里整理一套完整的MTU排查工具和方法。
# 查看所有接口的MTUip -d link show | grep -E "^[0-9]+:|mtu"# 查看路由的MTUip route get 10.0.0.1# 输出示例10.0.0.1 via 192.168.1.1 dev eth0 src 192.168.1.100 uid 0cache mtu 1450# 查看TCP连接的MSSss -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"
# pmtud_test.sh - 完整的PMTUD测试脚本TARGET=$1START_MTU=${2:-1500}if [ -z "$TARGET" ]; thenecho "Usage: $0 <target_ip> [start_mtu]"exit 1fiecho "Testing Path MTU to $TARGET"echo "Starting from MTU: $START_MTU"echo "================================"# 二分查找最大可用MTUlow=576high=$START_MTUlast_success=0while [ $low -le $high ]; domid=$(( (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; thenlast_success=$midlow=$(( mid + 1 ))elsehigh=$(( mid - 1 ))fidoneif [ $last_success -gt 0 ]; thenecho ""echo "Path MTU to $TARGET: $last_success bytes"echo "Recommended TCP MSS: $(( last_success - 40 )) bytes"elseecho "Error: Cannot determine Path MTU"fi
tracepath比ping更适合检测MTU问题:
# 使用tracepath检测路径MTUtracepath 10.0.0.1# 输出示例1?: [LOCALHOST] pmtu 15001: gateway 0.234ms1: gateway 0.198ms2: 10.0.0.1 0.456ms reachedResume: pmtu 1500 hops 2 back 2
tracepath会自动发现整条路径的MTU,并显示在哪一跳发生了MTU变化。
# 抓取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
# 查看当前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探测的初始MSSnet.ipv4.route.min_pmtu = 552 # 最小PMTU值# 优化配置cat >> /etc/sysctl.d/99-mtu.conf << EOF# MTU优化net.ipv4.ip_no_pmtu_disc = 0net.ipv4.tcp_mtu_probing = 1net.ipv4.tcp_base_mss = 1024# 路由缓存优化net.ipv4.route.gc_timeout = 100net.ipv4.route.min_pmtu = 552EOFsysctl -p /etc/sysctl.d/99-mtu.conf
6.6 一键诊断脚本
# 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. 检查所有接口MTUecho -e "\n${YELLOW}[1/6] Network Interfaces MTU${NC}"ip -o link show | awk '{print $2, $5}' | column -t# 2. 检查路由MTUecho -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/); dotx_dropped=$(cat /sys/class/net/$iface/statistics/tx_dropped 2>/dev/null)if [ "$tx_dropped" != "0" ] && [ -n "$tx_dropped" ]; thenecho -e "${RED}$iface: tx_dropped=$tx_dropped${NC}"fidone# 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}"
七、Kubernetes环境MTU最佳实践
Kubernetes环境下MTU问题更复杂,这里专门讲讲K8s的MTU配置。
不同CNI插件的MTU处理方式不同:
# Calico - 自动检测或手动配置kubectl -n kube-system get configmap calico-config -o yaml | grep -A5 cni_network_config# Calico 推荐配置apiVersion: crd.projectcalico.org/v1kind: FelixConfigurationmetadata:name: defaultspec: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
Istio Envoy Sidecar会增加网络复杂度:
# Istio MTU配置apiVersion: install.istio.io/v1alpha1kind: IstioOperatorspec:meshConfig:defaultConfig:proxyMetadata:# 调整Envoy的buffer大小ISTIO_META_NETWORK: network1values:global:proxy:# 资源配置resources:requests:memory: 128Mi
Envoy本身不会改变MTU,但需要确保Sidecar注入不影响MTU设置:
# 检查注入Sidecar后的MTUkubectl 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
多集群场景下MTU更需要注意:
# Submariner 跨集群网络配置apiVersion: submariner.io/v1alpha1kind: Brokermetadata:name: submariner-brokerspec:globalCIDR: 242.0.0.0/8---apiVersion: submariner.io/v1alpha1kind: ClusterGlobalEgressIPmetadata:name: cluster-egressspec:# MTU需要考虑跨集群隧道开销# IPsec: ~50字节# WireGuard: ~60字节
Cilium Cluster Mesh配置:
# Cilium跨集群配置apiVersion: v1kind: ConfigMapmetadata:name: cilium-confignamespace: kube-systemdata:cluster-name: cluster1cluster-id: "1"# 跨集群MTU设置mtu: "1400"enable-endpoint-routes: "true"tunnel: "vxlan"
高性能计算场景需要特殊的MTU配置:
# NVIDIA GPU Direct RDMA需要Jumbo Frame# Mellanox网卡配置mlxconfig -d /dev/mst/mt4123_pciconf0 set LINK_TYPE_P1=2 LINK_TYPE_P2=2# 设置IB网络MTUip 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: v1kind: ConfigMapmetadata:name: rdma-devicesdata:config.json: |{"mode": "shared","maxPods": 100}
NetworkPolicy通常不影响MTU,但某些实现可能会引入额外的处理开销:
# 确保NetworkPolicy不影响PMTUDapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: allow-icmpspec:podSelector: {}policyTypes:- Ingressingress:- ports:# 允许ICMP,确保PMTUD正常工作- protocol: ICMP
# 检查是否有丢弃ICMP的规则iptables -L -n | grep -i icmp# Cilium的ICMP策略kubectl get cnp -A -o yaml | grep -i icmp
八、高级场景和边界情况
IPv6的最小MTU是1280字节,比IPv4的576字节大:
# 检查IPv6 MTUip -6 link show eth0 | grep mtu# IPv6 Path MTU Discoveryping6 -M do -s 1452 2001:db8::1# IPv6的PMTUD使用ICMPv6# Packet Too Big消息代替ICMP Fragmentation Neededtcpdump -i eth0 'icmp6 and ip6[40] = 2'
IPv6隧道的MTU计算:
IPv6-in-IPv4 隧道: 1500 - 20(IPv4头) = 1480IPv6-in-IPv6 隧道: 1500 - 40(IPv6头) = 1460
不同容器运行时对MTU的处理:# Dockerdocker 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}
各种LB的MTU处理:
# HAProxy - 不直接处理MTU,但可以调整buffer# /etc/haproxy/haproxy.cfgglobaltune.bufsize 16384tune.maxrewrite 1024# Nginx - 可以调整proxy bufferupstream 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
eBPF程序可能影响数据包处理:
// XDP程序需要考虑MTUSEC("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相关的mapbpftool map show name cilium_metrics
九、生产环境MTU配置规范
根据多年的经验,总结一套生产环境的MTU配置规范。
# mtu-config-template.yaml# 生产环境MTU配置清单# 1. 物理网络层physical_network:datacenter:core_switch_mtu: 9000 # 如果支持Jumbo Frameaccess_switch_mtu: 9000server_nic_mtu: 9000wan:mtu: 1500 # 公网固定1500# 2. 虚拟化层virtualization:hypervisor:virtual_switch_mtu: 1500vm_nic_mtu: 1500# 3. 容器网络层container_network:cni_mtu: 1450 # VXLAN环境pod_mtu: 1450service_mesh_overhead: 0 # Istio不增加头部# 4. 隧道和VPNtunnels:vxlan_mtu: 1450 # 1500 - 50ipsec_mtu: 1400 # 保守值wireguard_mtu: 1420 # 1500 - 80# 5. 云环境cloud:aws_vpc_mtu: 9001 # 支持Jumbo Framealiyun_vpc_mtu: 1500gcp_vpc_mtu: 1460
# mtu_change_procedure.sh - MTU变更标准流程# 1. 变更前检查echo "=== Pre-change Checks ==="ip link show | grep mtuip route get 10.0.0.1netstat -s | grep -i frag# 2. 备份当前配置echo "=== Backup Current Config ==="ip link show > /tmp/mtu_backup_$(date +%Y%m%d).txtcp /etc/network/interfaces /tmp/ 2>/dev/nullcp /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:"'
# Prometheus告警规则groups:- name: mtu-alertsrules:# 高丢包率告警- alert: HighPacketDropexpr: rate(node_network_transmit_drop_total[5m]) > 10for: 5mlabels:severity: warningannotations:summary: "High packet drop on {{ $labels.instance }}"description: "Interface {{ $labels.device }} has high TX drops"# TCP重传率告警- alert: HighTCPRetransmitexpr: rate(node_netstat_Tcp_RetransSegs[5m]) / rate(node_netstat_Tcp_OutSegs[5m]) > 0.01for: 5mlabels:severity: warningannotations:summary: "High TCP retransmit rate on {{ $labels.instance }}"# MTU不一致告警(自定义exporter)- alert: MTUMismatchexpr: mtu_config_current != mtu_config_expectedfor: 1mlabels:severity: criticalannotations: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 }}"}]}]}
# mtu_health_check.sh - MTU健康检查自动化脚本set -eLOG_FILE="/var/log/mtu_health_check.log"EXPECTED_MTU=1450ALERT_THRESHOLD=100log() {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); docurrent_mtu=$(cat /sys/class/net/$iface/mtu)if [ "$current_mtu" != "$EXPECTED_MTU" ]; thenlog "WARNING: $iface MTU is $current_mtu, expected $EXPECTED_MTU"return 1fidonelog "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); dotx_dropped=$(cat /sys/class/net/$iface/statistics/tx_dropped)if [ "$tx_dropped" -gt "$ALERT_THRESHOLD" ]; thenlog "ALERT: $iface has $tx_dropped TX drops"return 1fidonelog "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; thenlog "PMTUD is working correctly"return 0elselog "WARNING: PMTUD may not be working"return 1fi}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" ]; thenlog "WARNING: PMTU discovery is disabled"fiif [ "$mtu_probing" == "0" ]; thenlog "INFO: TCP MTU probing is disabled"fireturn 0}# 主函数main() {log "========== MTU Health Check Started =========="errors=0check_interface_mtu || ((errors++))check_packet_drops || ((errors++))check_pmtud || ((errors++))check_kernel_params || ((errors++))if [ $errors -gt 0 ]; thenlog "Health check completed with $errors issues"exit 1elselog "Health check passed"exit 0fi}main
十、总结和经验教训
回顾这次MTU问题的排查过程,有几点教训:
1)升级前要看Release Notes:Cilium 1.15的变更导致了这个问题,如果升级前仔细看了Release Notes,可能就不会踩坑。
2)监控要覆盖网络层指标:之前我们的监控主要关注应用层,网络层的丢包、重传这些指标覆盖不够。
3)变更要有对照组:如果当时有个没升级的集群做对照,可能更快定位问题。
4)文档要及时更新:环境的MTU配置没有统一的文档记录,排查时浪费了不少时间。
MTU问题排查流程图:1. 现象确认├── 大包丢失,小包正常? → 很可能是MTU问题├── 特定路径丢包? → 检查路径上的MTU└── 随机丢包? → 可能不是MTU问题2. 快速诊断├── ping -M do -s 1472 目标IP├── tracepath 目标IP└── tcpdump抓ICMP错误3. 定位问题点├── 检查所有接口MTU├── 检查隧道/overlay的MTU└── 检查云环境的MTU限制4. 验证修复├── 调整MTU配置├── 测试不同大小的包└── 观察监控指标
为了避免类似问题再次发生,我们做了以下改进:
1)标准化MTU配置:所有环境使用统一的MTU值(1450),并写入Ansible Playbook自动化配置。
2)增加监控告警:在Prometheus中添加了丢包率、重传率的告警规则。
3)变更CheckList:升级CNI插件前必须检查MTU相关配置变化。
4)定期健康检查:每天自动运行MTU健康检查脚本。
5)文档化:把所有网络配置(包括MTU)都记录在CMDB中。
写这篇文章的时候参考了一些资料,列在这里供大家参考:
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网络文档
MTU这个东西,说简单也简单,就是个数字;说复杂也复杂,涉及到整个网络栈的方方面面。特别是在现在这种云原生、多层封装的环境下,MTU问题比以前更容易出现,也更难排查。
希望这篇文章能帮到遇到类似问题的兄弟们。如果有什么问题或者更好的方法,欢迎交流讨论。
最后说一句,做运维这行,遇到问题不可怕,可怕的是遇到问题不记录、不总结。每次故障都是一次学习的机会,把经验积累下来,下次遇到类似问题就能更快解决。
更多运维领域不容错过的热点探讨:
OpenClaw浪潮下的智能体应用可观测体系构建
从AIOps到Agentic AIOps的战略转型
多Agent协作及统一管理实践
金融级全栈信创化与云原生实践路径
……
都能在XCOPS智能运维管理人年会-广州站上看到生产级实战案例、找到可参考可落地的方式方法。扫描下方二维码可了解大会详情及报名↓

如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721