详解网络丢包故障排查过程

描述

前言

干运维这么多年,见过各种各样的故障,但有些问题真的是让人抓狂。前段时间遇到的一个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配置

  default
  
  
  ...


# VMware环境
# 在vSphere中设置分布式交换机的MTU
# vSphere Client -> Networking -> DVS -> Edit Settings -> MTU

 

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

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

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

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分