前言
干運(yùn)維這么多年,見(jiàn)過(guò)各種各樣的故障,但有些問(wèn)題真的是讓人抓狂。前段時(shí)間遇到的一個(gè)MTU問(wèn)題,差點(diǎn)讓我懷疑人生。表面上看是簡(jiǎn)單的丟包,實(shí)際上折騰了整整兩天才定位到根因。今天就把這個(gè)案例完整地記錄下來(lái),順便把MTU相關(guān)的知識(shí)點(diǎn)系統(tǒng)地梳理一遍,希望能幫到遇到類(lèi)似問(wèn)題的兄弟們。
說(shuō)實(shí)話(huà),MTU這個(gè)東西,很多人覺(jué)得不就是個(gè)數(shù)字嘛,有什么難的。但真正遇到問(wèn)題的時(shí)候,你會(huì)發(fā)現(xiàn)水很深。特別是現(xiàn)在云原生環(huán)境下,各種overlay網(wǎng)絡(luò)、隧道封裝,MTU問(wèn)題比以前復(fù)雜多了。
一、故障背景
1.1 環(huán)境介紹
先說(shuō)下我們的環(huán)境背景。公司用的是混合云架構(gòu),自建機(jī)房跑著Kubernetes集群,同時(shí)也用了阿里云和AWS。業(yè)務(wù)是做在線(xiàn)教育的,有直播、點(diǎn)播、互動(dòng)白板等模塊。
基礎(chǔ)設(shè)施情況:
自建機(jī)房:100多臺(tái)物理服務(wù)器,跑著3個(gè)K8s集群
網(wǎng)絡(luò)架構(gòu):核心交換機(jī)是華為CE12800,接入層是H3C S6800
Kubernetes版本:1.29.3
CNI插件:Cilium 1.15
服務(wù)網(wǎng)格:Istio 1.21
1.2 問(wèn)題現(xiàn)象
那天下午3點(diǎn)多,突然接到告警,業(yè)務(wù)方反饋直播推流出現(xiàn)卡頓,而且是間歇性的??戳讼卤O(jiān)控,發(fā)現(xiàn)一個(gè)奇怪的現(xiàn)象:
# 某個(gè)服務(wù)的網(wǎng)絡(luò)監(jiān)控?cái)?shù)據(jù) TCP重傳率: 2.3% (正常應(yīng)該在0.1%以下) 丟包率: 1.8% 延遲: P99從5ms飆到了200ms
第一反應(yīng)是網(wǎng)絡(luò)抖動(dòng),讓網(wǎng)絡(luò)組的兄弟查了下核心交換機(jī),沒(méi)發(fā)現(xiàn)異常。接著排查了服務(wù)本身,CPU、內(nèi)存、磁盤(pán)IO都正常。
最詭異的是,這個(gè)問(wèn)題只在某些特定場(chǎng)景下才會(huì)出現(xiàn)。小文件傳輸沒(méi)問(wèn)題,一旦傳大文件或者大數(shù)據(jù)包就開(kāi)始丟包。ping是通的,telnet端口也是通的,但就是業(yè)務(wù)數(shù)據(jù)傳不過(guò)去。
1.3 初步排查
按照常規(guī)套路,先看網(wǎng)絡(luò)基礎(chǔ)指標(biāo):
# 查看網(wǎng)卡統(tǒng)計(jì)信息 ip -s link show eth0 # 輸出結(jié)果 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個(gè)包被丟棄了,這個(gè)數(shù)字在增長(zhǎng)。繼續(xù)深入:
# 查看更詳細(xì)的網(wǎng)卡統(tǒng)計(jì) ethtool -S eth0 | grep -i drop # 輸出 tx_dropped: 1823 rx_dropped: 0 tx_window_errors: 0
丟包發(fā)生在發(fā)送端。再看看系統(tǒng)日志:
dmesg | grep -i"too long" # 發(fā)現(xiàn)了這個(gè) [89234.123456] eth0: dropped packet, size 1514 > 1500
看到這個(gè)日志,心里大概有數(shù)了,八成是MTU問(wèn)題。
二、MTU基礎(chǔ)知識(shí)回顧
在深入排查之前,先把MTU相關(guān)的基礎(chǔ)知識(shí)捋一遍。這些東西可能有些老生常談,但確實(shí)很重要。
2.1 什么是MTU
MTU(Maximum Transmission Unit)就是網(wǎng)絡(luò)設(shè)備能夠傳輸?shù)淖畲髷?shù)據(jù)包大小。這個(gè)概念是在數(shù)據(jù)鏈路層定義的,不同的網(wǎng)絡(luò)技術(shù)有不同的MTU值:
| 網(wǎng)絡(luò)類(lèi)型 | MTU值 |
|---|---|
| Ethernet II | 1500字節(jié) |
| PPPoE | 1492字節(jié) |
| GRE隧道 | 1476字節(jié) |
| VXLAN | 1450字節(jié) |
| IPsec (ESP+AH) | 約1400字節(jié) |
| Jumbo Frame | 9000字節(jié) |
以太網(wǎng)的1500字節(jié)MTU是最常見(jiàn)的,這個(gè)數(shù)字從1980年代沿用至今。當(dāng)時(shí)的考量是在傳輸效率和錯(cuò)誤概率之間取得平衡。
2.2 MTU vs MSS
很多人分不清MTU和MSS的區(qū)別,這里說(shuō)明一下:
+------------------+------------------+------------------+------------------+
| 以太網(wǎng)幀頭 | IP頭部 | TCP頭部 | 數(shù)據(jù) |
| 14字節(jié) | 20字節(jié) | 20字節(jié) | 最大1460字節(jié) |
+------------------+------------------+------------------+------------------+
|<-------------- MTU 1500字節(jié) ---------------->|
|<- MSS ->|
MTU:包含IP頭和TCP頭,以太網(wǎng)默認(rèn)1500字節(jié)
MSS(Maximum Segment Size):只計(jì)算TCP數(shù)據(jù)部分,默認(rèn)1460字節(jié)(1500-20-20)
TCP三次握手的時(shí)候,雙方會(huì)協(xié)商MSS值。如果MSS設(shè)置不當(dāng),會(huì)導(dǎo)致分片或者丟包。
2.3 Path MTU Discovery
PMTUD(Path MTU Discovery)是用來(lái)自動(dòng)發(fā)現(xiàn)整條鏈路上最小MTU的機(jī)制。工作原理:
發(fā)送端發(fā)送DF(Don't Fragment)標(biāo)志位設(shè)置為1的數(shù)據(jù)包
如果中間路由器的MTU小于數(shù)據(jù)包大小,會(huì)返回ICMP "Fragmentation Needed"消息
發(fā)送端根據(jù)ICMP消息調(diào)整數(shù)據(jù)包大小
重復(fù)上述過(guò)程,直到數(shù)據(jù)包能夠成功傳輸
問(wèn)題是,很多防火墻會(huì)把ICMP包干掉,導(dǎo)致PMTUD失效。這就是著名的"PMTUD黑洞"問(wèn)題。
# 測(cè)試PMTUD是否正常工作 ping -Mdo-s 1472 192.168.1.1 # -M do: 設(shè)置DF標(biāo)志,不允許分片 # -s 1472: 1472 + 8(ICMP頭) + 20(IP頭) = 1500
如果1472能ping通但1473 ping不通,說(shuō)明MTU是1500且PMTUD工作正常。
2.4 分片與重組
當(dāng)數(shù)據(jù)包大于MTU時(shí),如果DF標(biāo)志沒(méi)有設(shè)置,IP層會(huì)進(jìn)行分片:
# 查看分片統(tǒng)計(jì) 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
分片會(huì)帶來(lái)幾個(gè)問(wèn)題:
增加CPU開(kāi)銷(xiāo)
任何一個(gè)分片丟失,整個(gè)包都要重傳
分片攻擊的安全風(fēng)險(xiǎn)
狀態(tài)防火墻可能無(wú)法正確處理分片包
所以現(xiàn)代網(wǎng)絡(luò)一般都不建議分片,而是在應(yīng)用層控制數(shù)據(jù)包大小。
三、深入排查過(guò)程
回到我們的故障。既然懷疑是MTU問(wèn)題,就開(kāi)始針對(duì)性排查。
3.1 確認(rèn)MTU配置
首先檢查各個(gè)節(jié)點(diǎn)的MTU配置:
# 物理機(jī)網(wǎng)卡MTU ip link show eth0 | grep mtu # 輸出: mtu 1500 # K8s節(jié)點(diǎn)的CNI網(wǎng)卡 ip link show cilium_host | grep mtu # 輸出: mtu 1500 # Pod內(nèi)部網(wǎng)卡 kubectlexec-ittest-pod -- ip link show eth0 # 輸出: mtu 1500 # 檢查VXLAN隧道接口 ip link show cilium_vxlan | grep mtu # 輸出: mtu 1500 <-- 問(wèn)題出在這里!
發(fā)現(xiàn)問(wèn)題了!VXLAN隧道接口的MTU也是1500,但VXLAN封裝會(huì)額外增加50字節(jié)的開(kāi)銷(xiāo):
VXLAN封裝開(kāi)銷(xiāo): - 外層以太網(wǎng)頭: 14字節(jié) - 外層IP頭: 20字節(jié) - 外層UDP頭: 8字節(jié) - VXLAN頭: 8字節(jié) 總共: 50字節(jié)
也就是說(shuō),原始數(shù)據(jù)包如果是1500字節(jié),加上VXLAN封裝后會(huì)變成1550字節(jié),超過(guò)了物理網(wǎng)卡的MTU限制,導(dǎo)致丟包。
3.2 抓包確認(rèn)
用tcpdump抓包確認(rèn)問(wèn)題:
# 在物理機(jī)上抓包 tcpdump -i eth0 -nn'icmp[0]=3 and icmp[1]=4' # 在Pod內(nèi)發(fā)送大包 kubectlexec-ittest-pod -- ping -Mdo-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
交換機(jī)告訴我們MTU應(yīng)該是1450,但我們的VXLAN接口設(shè)置的是1500,所以大包就被丟了。
3.3 驗(yàn)證問(wèn)題
寫(xiě)個(gè)簡(jiǎn)單的腳本來(lái)驗(yàn)證不同包大小的通信情況:
#!/bin/bash # mtu_test.sh - 測(cè)試不同包大小的連通性 TARGET_IP=$1 START_SIZE=1400 END_SIZE=1500 echo"Testing MTU to$TARGET_IP" echo"=========================" forsizein$(seq$START_SIZE$END_SIZE);do ifping -Mdo-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
運(yùn)行結(jié)果:
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,確認(rèn)了實(shí)際的Path MTU就是1450。
3.4 為什么之前沒(méi)問(wèn)題
這個(gè)問(wèn)題困擾了我很久:配置一直是這樣的,為什么之前沒(méi)事,現(xiàn)在才出問(wèn)題?
后來(lái)查了Cilium的更新日志才發(fā)現(xiàn),1.15版本修改了默認(rèn)的PMTUD行為。之前版本會(huì)自動(dòng)處理MTU mismatch,新版本默認(rèn)關(guān)閉了這個(gè)功能,需要手動(dòng)開(kāi)啟。
# 查看Cilium版本 cilium version # 查看相關(guān)配置 kubectl -n kube-system get configmap cilium-config -o yaml | grep -i mtu # 輸出 enable-pmtu-discovery:"false"# 這個(gè)是關(guān)閉的 mtu:"0"# 0表示自動(dòng)檢測(cè),但檢測(cè)的是本地MTU
另外,我們的業(yè)務(wù)最近上線(xiàn)了一個(gè)新功能,傳輸?shù)臄?shù)據(jù)包變大了。以前的小包剛好不超過(guò)1450,所以沒(méi)問(wèn)題。這就解釋了為什么問(wèn)題是突然出現(xiàn)的。
四、解決方案
找到根因后,解決方案就比較清晰了。
4.1 方案一:調(diào)整Pod網(wǎng)絡(luò)MTU
最直接的方法是把Pod網(wǎng)絡(luò)的MTU調(diào)小,留出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
驗(yàn)證配置生效:
# 檢查cilium_host接口MTU kubectl -n kube-systemexec-it cilium-xxxxx -- ip link show cilium_host # 檢查Pod內(nèi)的MTU kubectlexec-ittest-pod -- ip link show eth0
4.2 方案二:開(kāi)啟Jumbo Frame
如果你的網(wǎng)絡(luò)環(huán)境支持,可以考慮開(kāi)啟巨型幀(Jumbo Frame),把MTU設(shè)置為9000:
# 在所有物理節(jié)點(diǎn)上設(shè)置 ip linkseteth0 mtu 9000 # 永久生效,修改網(wǎng)卡配置 # 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 # 應(yīng)用配置 netplan apply
注意,Jumbo Frame需要整條鏈路上的所有設(shè)備都支持,包括:
服務(wù)器網(wǎng)卡
交換機(jī)所有端口
路由器
負(fù)載均衡器
如果中間有任何設(shè)備不支持9000 MTU,就會(huì)出問(wèn)題。
# 檢查交換機(jī)端口是否支持Jumbo Frame # 華為設(shè)備 display interface GigabitEthernet0/0/1 | include MTU # H3C設(shè)備 display interface GigabitEthernet1/0/1 | include MTU
4.3 方案三:TCP MSS Clamping
如果沒(méi)法改MTU,可以通過(guò)iptables調(diào)整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 # 持久化規(guī)則 iptables-save > /etc/iptables/rules.v4
在Kubernetes環(huán)境中,可以通過(guò)Cilium的BPF程序?qū)崿F(xiàn)類(lèi)似功能:
# 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設(shè)置為1450
同時(shí)開(kāi)啟PMTUD
添加MSS clamping作為兜底
# 完整的解決腳本
#!/bin/bash
# fix_mtu.sh
# 1. 更新Cilium配置
kubectl -n kube-system patch configmap cilium-config --typemerge -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(在所有節(jié)點(diǎn)執(zhí)行)
fornodein$(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. 驗(yàn)證修復(fù)
kubectlexec-ittest-pod -- ping -Mdo-s 1422 10.244.1.100
echo"MTU fix completed!"
五、不同場(chǎng)景下的MTU配置
MTU問(wèn)題在不同的網(wǎng)絡(luò)環(huán)境下表現(xiàn)不同,這里總結(jié)一下各種場(chǎng)景的最佳配置。
5.1 物理機(jī)環(huán)境
最簡(jiǎn)單的場(chǎng)景,直接設(shè)置網(wǎng)卡MTU即可:
# 臨時(shí)設(shè)置 ip linkseteth0 mtu 1500 # 永久設(shè)置 - systemd-networkd cat > /etc/systemd/network/10-eth0.network << EOF [Match] Name=eth0 [Network] DHCP=yes [Link] MTUBytes=1500 EOF systemctl restart systemd-networkd # 驗(yàn)證 ip link show eth0 | grep mtu
5.2 虛擬機(jī)環(huán)境
虛擬化環(huán)境要考慮虛擬交換機(jī)的MTU:
# KVM/libvirt環(huán)境 # 修改虛擬網(wǎng)絡(luò)配置 virsh net-edit default # 添加MTU配置# VMware環(huán)境 # 在vSphere中設(shè)置分布式交換機(jī)的MTU # vSphere Client -> Networking -> DVS -> Edit Settings -> MTU default ...
OpenStack環(huán)境需要同時(shí)設(shè)置多個(gè)組件:
# /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 容器環(huán)境
Docker單機(jī)環(huán)境:
# 修改Docker daemon配置
cat > /etc/docker/daemon.json << EOF
{
??"mtu": 1450
}
EOF
systemctl restart docker
# 驗(yàn)證
docker network inspect bridge | grep -i mtu
Kubernetes環(huán)境要根據(jù)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 云環(huán)境
各大云廠商的MTU限制不同:
| 云廠商 | 默認(rèn)MTU | 最大MTU | 備注 |
|---|---|---|---|
| AWS | 1500 | 9001 | VPC內(nèi)部支持Jumbo Frame |
| 阿里云 | 1500 | 1500 | 跨可用區(qū)有限制 |
| 騰訊云 | 1500 | 1500 | VPC內(nèi)部統(tǒng)一 |
| Azure | 1500 | 1500 | ExpressRoute支持更大 |
| GCP | 1460 | 1460 | 因?yàn)槭褂昧朔庋b |
AWS VPC配置Jumbo Frame:
# 檢查實(shí)例是否支持Jumbo Frame aws ec2 describe-instances --instance-ids i-1234567890abcdef0 --query'Reservations[].Instances[].NetworkInterfaces[].Groups' # 在實(shí)例內(nèi)設(shè)置MTU sudo ip linkseteth0 mtu 9001 # 持久化 - Amazon Linux 2 echo'MTU=9001'| sudo tee -a /etc/sysconfig/network-scripts/ifcfg-eth0
阿里云環(huán)境注意事項(xiàng):
# 阿里云的ENI彈性網(wǎng)卡有MTU限制 # 主網(wǎng)卡固定1500,不可修改 # 如果使用Terway CNI(阿里云官方K8s網(wǎng)絡(luò)方案) # 需要考慮ENI的MTU限制 # 檢查當(dāng)前MTU ip link show eth0 # 阿里云VPC內(nèi)Pod網(wǎng)絡(luò)推薦MTU # - 普通VPC網(wǎng)絡(luò): 1500 # - ENI多IP模式: 1500 # - IPVLAN模式: 1500
5.5 VPN和隧道環(huán)境
各種隧道封裝的MTU開(kāi)銷(xiāo):
# 常見(jiàn)隧道協(xié)議的開(kāi)銷(xiāo)
IPsec (ESP, tunnel mode): 52-73字節(jié)
IPsec (ESP, transport mode): 38-59字節(jié)
GRE: 24字節(jié)
VXLAN: 50字節(jié)
Geneve: 50字節(jié) + 可變長(zhǎng)度選項(xiàng)
WireGuard: 60字節(jié)
# 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網(wǎng)絡(luò)
企業(yè)SD-WAN環(huán)境的MTU配置:
# 以Cisco SD-WAN為例 # 邊緣設(shè)備配置 interface GigabitEthernet0/0 ip mtu 1400 tcp adjust-mss 1360 # 控制平面配置 system overlay-mtu 1450 control-mtu 1500
六、MTU問(wèn)題排查工具箱
這里整理一套完整的MTU排查工具和方法。
6.1 基礎(chǔ)診斷命令
# 查看所有接口的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 # 查看系統(tǒng)的分片統(tǒng)計(jì) cat /proc/net/snmp | grep -E"^Ip:"| head -2 # 查看網(wǎng)卡的詳細(xì)統(tǒng)計(jì) ethtool -S eth0 | grep -E"drop|error|frag"
6.2 PMTUD測(cè)試腳本
#!/bin/bash
# pmtud_test.sh - 完整的PMTUD測(cè)試腳本
TARGET=$1
START_MTU=${2:-1500}
if[ -z"$TARGET"];then
echo"Usage:$0 [start_mtu]"
exit1
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)
ifping -Mdo-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_successbytes"
echo"Recommended TCP MSS:$(( last_success - 40 )) bytes"
else
echo"Error: Cannot determine Path MTU"
fi
6.3 tracepath檢測(cè)
tracepath比ping更適合檢測(cè)MTU問(wèn)題:
# 使用tracepath檢測(cè)路徑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會(huì)自動(dòng)發(fā)現(xiàn)整條路徑的MTU,并顯示在哪一跳發(fā)生了MTU變化。
6.4 抓包分析
# 抓取ICMP Fragmentation Needed消息 tcpdump -i eth0 -nn'icmp[0]=3 and icmp[1]=4'-w /tmp/frag_needed.pcap # 抓取所有ICMP錯(cuò)誤消息 tcpdump -i eth0 -nn'icmp[0]=3'-v # 抓取帶DF標(biāo)志的大包 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過(guò)濾器 # icmp.type == 3 && icmp.code == 4
6.5 內(nèi)核參數(shù)調(diào)優(yōu)
# 查看當(dāng)前PMTUD相關(guān)參數(shù) sysctl -a | grep -E"pmtu|mtu" # 關(guān)鍵參數(shù)說(shuō)明 net.ipv4.ip_no_pmtu_disc = 0 # 0=啟用PMTUD, 1=禁用 net.ipv4.tcp_mtu_probing = 1 # 0=禁用, 1=黑洞檢測(cè)時(shí)啟用, 2=始終啟用 net.ipv4.tcp_base_mss = 1024 # TCP MTU探測(cè)的初始MSS net.ipv4.route.min_pmtu = 552 # 最小PMTU值 # 優(yōu)化配置 cat >> /etc/sysctl.d/99-mtu.conf << EOF # MTU優(yōu)化 net.ipv4.ip_no_pmtu_disc = 0 net.ipv4.tcp_mtu_probing = 1 net.ipv4.tcp_base_mss = 1024 # 路由緩存優(yōu)化 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問(wèn)題一鍵診斷
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. 檢查內(nèi)核參數(shù)
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. 檢查丟包統(tǒng)計(jì)
echo-e"
${YELLOW}[4/6] Drop Statistics${NC}"
forifacein$(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錯(cuò)誤
echo-e"
${YELLOW}[5/6] Recent ICMP Errors${NC}"
netstat -s | grep -i -E"frag|mtu|icmp"| head -10
# 6. 檢查iptables MSS規(guī)則
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環(huán)境MTU最佳實(shí)踐
Kubernetes環(huán)境下MTU問(wèn)題更復(fù)雜,這里專(zhuān)門(mén)講講K8s的MTU配置。
7.1 CNI插件MTU配置對(duì)比
不同CNI插件的MTU處理方式不同:
# Calico - 自動(dòng)檢測(cè)或手動(dòng)配置 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 - 支持自動(dòng)MTU檢測(cè) 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 服務(wù)網(wǎng)格的MTU考慮
Istio Envoy Sidecar會(huì)增加網(wǎng)絡(luò)復(fù)雜度:
# Istio MTU配置
apiVersion:install.istio.io/v1alpha1
kind:IstioOperator
spec:
meshConfig:
defaultConfig:
proxyMetadata:
# 調(diào)整Envoy的buffer大小
ISTIO_META_NETWORK:network1
values:
global:
proxy:
# 資源配置
resources:
requests:
memory:128Mi
Envoy本身不會(huì)改變MTU,但需要確保Sidecar注入不影響MTU設(shè)置:
# 檢查注入Sidecar后的MTU kubectlexec-it my-pod -c istio-proxy -- ip link show eth0 # 檢查Envoy的連接狀態(tài) kubectlexec-it my-pod -c istio-proxy -- curl localhost:15000/stats | grep -i mss
7.3 跨集群網(wǎng)絡(luò)MTU
多集群場(chǎng)景下MTU更需要注意:
# Submariner 跨集群網(wǎng)絡(luò)配置 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需要考慮跨集群隧道開(kāi)銷(xiāo) # IPsec: ~50字節(jié) # WireGuard: ~60字節(jié)
Cilium Cluster Mesh配置:
# Cilium跨集群配置 apiVersion:v1 kind:ConfigMap metadata: name:cilium-config namespace:kube-system data: cluster-name:cluster1 cluster-id:"1" # 跨集群MTU設(shè)置 mtu:"1400" enable-endpoint-routes:"true" tunnel:"vxlan"
高性能計(jì)算場(chǎng)景需要特殊的MTU配置:
# NVIDIA GPU Direct RDMA需要Jumbo Frame
# Mellanox網(wǎng)卡配置
mlxconfig -d /dev/mst/mt4123_pciconf0setLINK_TYPE_P1=2 LINK_TYPE_P2=2
# 設(shè)置IB網(wǎng)絡(luò)MTU
ip linksetib0 mtu 4092
# RoCE v2配置
echo4096 > /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,但某些實(shí)現(xiàn)可能會(huì)引入額外的處理開(kāi)銷(xiāo):
# 確保NetworkPolicy不影響PMTUD
apiVersion:networking.k8s.io/v1
kind:NetworkPolicy
metadata:
name:allow-icmp
spec:
podSelector:{}
policyTypes:
-Ingress
ingress:
-ports:
# 允許ICMP,確保PMTUD正常工作
-protocol:ICMP
# 檢查是否有丟棄ICMP的規(guī)則 iptables -L -n | grep -i icmp # Cilium的ICMP策略 kubectl get cnp -A -o yaml | grep -i icmp
八、高級(jí)場(chǎng)景和邊界情況
8.1 IPv6環(huán)境的MTU
IPv6的最小MTU是1280字節(jié),比IPv4的576字節(jié)大:
# 檢查IPv6 MTU ip -6 link show eth0 | grep mtu # IPv6 Path MTU Discovery ping6 -Mdo-s 1452 2001:1 # IPv6的PMTUD使用ICMPv6 # Packet Too Big消息代替ICMP Fragmentation Needed tcpdump -i eth0'icmp6 and ip6[40] = 2'
IPv6隧道的MTU計(jì)算:
IPv6-in-IPv4 隧道: 1500 - 20(IPv4頭) = 1480 IPv6-in-IPv6 隧道: 1500 - 40(IPv6頭) = 1460
8.2 容器運(yùn)行時(shí)差異
不同容器運(yùn)行時(shí)對(duì)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 負(fù)載均衡器MTU
各種LB的MTU處理:
# HAProxy - 不直接處理MTU,但可以調(diào)整buffer
# /etc/haproxy/haproxy.cfg
global
tune.bufsize 16384
tune.maxrewrite 1024
# Nginx - 可以調(diào)整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
云負(fù)載均衡器通常有自己的MTU限制:
# AWS ALB/NLB # MTU自動(dòng)處理,無(wú)需配置 # 阿里云SLB # 經(jīng)典網(wǎng)絡(luò): 1500 # VPC網(wǎng)絡(luò): 1500 # 跨可用區(qū): 注意MTU差異 # GCP Load Balancer # 默認(rèn)1460,因?yàn)镚CP網(wǎng)絡(luò)MTU是1460
8.4 eBPF和XDP對(duì)MTU的影響
eBPF程序可能影響數(shù)據(jù)包處理:
// XDP程序需要考慮MTU
SEC("xdp")
intxdp_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)
returnXDP_PASS;
structethhdr*eth=data;
structiphdr*iph=data+sizeof(structethhdr);
// 可以在這里檢查和處理MTU相關(guān)邏輯
__u16 tot_len = ntohs(iph->tot_len);
if(tot_len >1500-sizeof(struct ethhdr)) {
// 包太大,記錄或處理
}
returnXDP_PASS;
}
Cilium的eBPF MTU處理:
# 查看Cilium的BPF程序 bpftool prog show # 查看MTU相關(guān)的map bpftool map show name cilium_metrics
九、生產(chǎn)環(huán)境MTU配置規(guī)范
根據(jù)多年的經(jīng)驗(yàn),總結(jié)一套生產(chǎn)環(huán)境的MTU配置規(guī)范。
9.1 配置清單模板
# mtu-config-template.yaml # 生產(chǎn)環(huán)境MTU配置清單 # 1. 物理網(wǎng)絡(luò)層 physical_network: datacenter: core_switch_mtu:9000 # 如果支持Jumbo Frame access_switch_mtu:9000 server_nic_mtu:9000 wan: mtu:1500 # 公網(wǎng)固定1500 # 2. 虛擬化層 virtualization: hypervisor: virtual_switch_mtu:1500 vm_nic_mtu:1500 # 3. 容器網(wǎng)絡(luò)層 container_network: cni_mtu:1450 # VXLAN環(huán)境 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. 云環(huán)境 cloud: aws_vpc_mtu:9001 # 支持Jumbo Frame aliyun_vpc_mtu:1500 gcp_vpc_mtu:1460
9.2 變更流程
#!/bin/bash
# mtu_change_procedure.sh - MTU變更標(biāo)準(zhǔn)流程
# 1. 變更前檢查
echo"=== Pre-change Checks ==="
ip link show | grep mtu
ip route get 10.0.0.1
netstat -s | grep -i frag
# 2. 備份當(dāng)前配置
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. 灰度變更(先在一臺(tái)機(jī)器測(cè)試)
echo"=== Gradual Change ==="
# 這里執(zhí)行實(shí)際變更
# 4. 驗(yàn)證變更
echo"=== Post-change Verification ==="
# 測(cè)試連通性
ping -Mdo-s 1422 10.0.0.1
# 測(cè)試業(yè)務(wù)
curl -o /dev/null -w"%{time_total}"http://service:8080/health
# 5. 監(jiān)控觀察
echo"=== Monitoring ==="
# 觀察丟包率和重傳率變化
watch -n 1'cat /proc/net/snmp | grep -E "^Tcp:"'
9.3 監(jiān)控告警配置
# Prometheus告警規(guī)則
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 自動(dòng)化檢查腳本
#!/bin/bash
# mtu_health_check.sh - MTU健康檢查自動(dòng)化腳本
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..."
forifacein$(ls /sys/class/net/ | grep -v lo);do
current_mtu=$(cat /sys/class/net/$iface/mtu)
if["$current_mtu"!="$EXPECTED_MTU"];then
log"WARNING:$ifaceMTU is$current_mtu, expected$EXPECTED_MTU"
return1
fi
done
log"All interface MTU values are correct"
return0
}
check_packet_drops() {
log"Checking packet drops..."
forifacein$(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:$ifacehas$tx_droppedTX drops"
return1
fi
done
log"Packet drop counts are within threshold"
return0
}
check_pmtud() {
log"Checking PMTUD functionality..."
# 測(cè)試目標(biāo)IP(需要替換為實(shí)際的測(cè)試目標(biāo))
TEST_TARGET="10.0.0.1"
ifping -Mdo-c 1 -s 1422 -W 2$TEST_TARGET> /dev/null 2>&1;then
log"PMTUD is working correctly"
return0
else
log"WARNING: PMTUD may not be working"
return1
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
return0
}
# 主函數(shù)
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$errorsissues"
exit1
else
log"Health check passed"
exit0
fi
}
main
十、總結(jié)和經(jīng)驗(yàn)教訓(xùn)
10.1 這次故障的教訓(xùn)
回顧這次MTU問(wèn)題的排查過(guò)程,有幾點(diǎn)教訓(xùn):
升級(jí)前要看Release Notes:Cilium 1.15的變更導(dǎo)致了這個(gè)問(wèn)題,如果升級(jí)前仔細(xì)看了Release Notes,可能就不會(huì)踩坑。
監(jiān)控要覆蓋網(wǎng)絡(luò)層指標(biāo):之前我們的監(jiān)控主要關(guān)注應(yīng)用層,網(wǎng)絡(luò)層的丟包、重傳這些指標(biāo)覆蓋不夠。
變更要有對(duì)照組:如果當(dāng)時(shí)有個(gè)沒(méi)升級(jí)的集群做對(duì)照,可能更快定位問(wèn)題。
文檔要及時(shí)更新:環(huán)境的MTU配置沒(méi)有統(tǒng)一的文檔記錄,排查時(shí)浪費(fèi)了不少時(shí)間。
10.2 MTU問(wèn)題排查思路總結(jié)
MTU問(wèn)題排查流程圖: 1. 現(xiàn)象確認(rèn) ├── 大包丟失,小包正常? → 很可能是MTU問(wèn)題 ├── 特定路徑丟包? → 檢查路徑上的MTU └── 隨機(jī)丟包? → 可能不是MTU問(wèn)題 2. 快速診斷 ├── ping -Mdo-s 1472 目標(biāo)IP ├── tracepath 目標(biāo)IP └── tcpdump抓ICMP錯(cuò)誤 3. 定位問(wèn)題點(diǎn) ├── 檢查所有接口MTU ├── 檢查隧道/overlay的MTU └── 檢查云環(huán)境的MTU限制 4. 驗(yàn)證修復(fù) ├── 調(diào)整MTU配置 ├── 測(cè)試不同大小的包 └── 觀察監(jiān)控指標(biāo)
10.3 預(yù)防措施
為了避免類(lèi)似問(wèn)題再次發(fā)生,我們做了以下改進(jìn):
標(biāo)準(zhǔn)化MTU配置:所有環(huán)境使用統(tǒng)一的MTU值(1450),并寫(xiě)入Ansible Playbook自動(dòng)化配置。
增加監(jiān)控告警:在Prometheus中添加了丟包率、重傳率的告警規(guī)則。
變更CheckList:升級(jí)CNI插件前必須檢查MTU相關(guān)配置變化。
定期健康檢查:每天自動(dòng)運(yùn)行MTU健康檢查腳本。
文檔化:把所有網(wǎng)絡(luò)配置(包括MTU)都記錄在CMDB中。
10.4 參考資料
寫(xiě)這篇文章的時(shí)候參考了一些資料,列在這里供大家參考:
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網(wǎng)絡(luò)文檔
10.5 寫(xiě)在最后
MTU這個(gè)東西,說(shuō)簡(jiǎn)單也簡(jiǎn)單,就是個(gè)數(shù)字;說(shuō)復(fù)雜也復(fù)雜,涉及到整個(gè)網(wǎng)絡(luò)棧的方方面面。特別是在現(xiàn)在這種云原生、多層封裝的環(huán)境下,MTU問(wèn)題比以前更容易出現(xiàn),也更難排查。
希望這篇文章能幫到遇到類(lèi)似問(wèn)題的兄弟們。如果有什么問(wèn)題或者更好的方法,歡迎交流討論。
最后說(shuō)一句,做運(yùn)維這行,遇到問(wèn)題不可怕,可怕的是遇到問(wèn)題不記錄、不總結(jié)。每次故障都是一次學(xué)習(xí)的機(jī)會(huì),把經(jīng)驗(yàn)積累下來(lái),下次遇到類(lèi)似問(wèn)題就能更快解決。
-
服務(wù)器
+關(guān)注
關(guān)注
14文章
10270瀏覽量
91538 -
網(wǎng)絡(luò)
+關(guān)注
關(guān)注
14文章
8280瀏覽量
94952 -
阿里云
+關(guān)注
關(guān)注
3文章
1038瀏覽量
45739
原文標(biāo)題:MTU配置不當(dāng)引發(fā)的血案:一次詭異的網(wǎng)絡(luò)丟包排查
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
波特率漂移導(dǎo)致通信異常的故障排查過(guò)程
常見(jiàn)的網(wǎng)絡(luò)丟包故障定位?法
網(wǎng)絡(luò)丟包常見(jiàn)故障分析及處理方式
網(wǎng)絡(luò)數(shù)據(jù)及攝像機(jī)丟包的原因
常見(jiàn)的云網(wǎng)絡(luò)丟包故障定位?法
網(wǎng)絡(luò)丟包怎么辦,常見(jiàn)故障分析及處理方式
DC-DC電源故障排查過(guò)程和總結(jié),珍貴的經(jīng)驗(yàn)!資料下載
網(wǎng)絡(luò)丟包時(shí)常用的排錯(cuò)思路
深入分析Linux網(wǎng)絡(luò)丟包問(wèn)題!
深入分析Linux網(wǎng)絡(luò)丟包問(wèn)題
網(wǎng)絡(luò)丟包問(wèn)題分析
網(wǎng)絡(luò)丟包率正常范圍及其影響因素
Java應(yīng)用OOM問(wèn)題的排查過(guò)程
詳解網(wǎng)絡(luò)丟包故障排查過(guò)程
評(píng)論