cmdb CMDB运维实战:MTU故障排查两天,云原生环境避坑指南

网安智编 厦门萤点网络科技 2026-06-13 00:08 7 0
前言 干运维这么多年,见过各种各样的故障,但有些问题真的是让人抓狂。前段时间遇到的一个MTU问题,差点让我怀疑人生。表面上看是简单的丢包,实际上折腾了整整两天才定位到根因。今天就把这个案例完整地记录下来,顺便把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问题排查_cmdb_Kubernetes环境MTU配置

各大云厂商的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问题比以前更容易出现,也更难排查。

希望这篇文章能帮到遇到类似问题的兄弟们。如果有什么问题或者更好的方法,欢迎交流讨论。

最后说一句,做运维这行,遇到问题不可怕,可怕的是遇到问题不记录、不总结。每次故障都是一次学习的机会,把经验积累下来,下次遇到类似问题就能更快解决。