BGP多线主机rsync备份间歇性中断之谜:多元运营商出口下的MTU黑洞与策略路由终极修复

故障现象

凌晨3点,Prometheus告警:某BGP多线主机(跑核心MySQL从库)的备份脚本连续第3天失败。日志触目惊心:

rsync: writefd_unbuffered failed to write 32768 bytes [sender]: Broken pipe (32)
rsync error: unexplained IO error (code 10) at io.c(829) [sender=3.2.7]
...
rsync connection unexpectedly closed (0 bytes received so far) [receiver]

备份指向本地私有云的对象存储网关,需跨运营商网络传输。手动重试偶成功,但凌晨准时扑街。

初步盲区:能ping就代表链路正常?

第一步:ping -M do -s 1472 对象存储网关IP —— 通过!
第二步:ping加大包 -s 4472 —— 部分运营商方向丢包100%。

爆了:路径MTU不一致。BGP多线主机默认出口路由走“主线路”,但主线路的中间设备(某运营商城域设备)MTU仅1450(典型PPPoE或VXLAN叠加场景)。备份数据流默认TCP MSS=1460,加上IP头40字节=1500,超过1450,分片被中间设备静默丢弃。

为什么不是全挂?

因为对象存储DNS返回多个A记录(不同运营商VIP),每次rsync解析随机命中一个IP。若命中与主流运营商同路径,则MTU小得卡死;若命中与其他供应商路径(MTU正常1500),则顺利。凌晨之所以必挂,系因DNS缓存过期后重新解析,恰好连续三次命中“坏路径”。

硬核修复:TCP MSS Clamp + 策略路由绑定

第一板斧:给备份进程直接钳住MSS

# 只针对备份进程(UID=1000)的出站流量,强制TCP MSS不超过1400
iptables -t mangle -A OUTPUT -m owner --uid-owner 1000 -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --clamp-mss-to-pmtu
# 同时降低路由器级PMTU发现超时,避免缓存旧值
echo 600 > /proc/sys/net/ipv4/route/gc_timeout

但这治标不治本:MSS钳到1400后,每个包有效载荷减少约4%,在千万级文件增量时累计时间浪费。

第二板斧(推荐):策略路由强制备份走“宽路”

多线主机轻云互联拥有联通、移动、电信三条BGP线路,其中一条MTU稳定1500且延迟最低。手动指定备份流量仅从该线路的出口IP发出:

# 1. 创建新路由表(假设给备份专用)
echo "200 backup" >> /etc/iproute2/rt_tables

# 2. 为备份进程UID打标记(1000)
iptables -t mangle -A OUTPUT -m owner --uid-owner 1000 -j MARK --set-mark 200

# 3. 根据标记分流:从指定网卡eth1(优选线路)出,并指定原IP
ip rule add fwmark 200 table backup
ip route add default via 优选线路网关 dev eth1 table backup
ip route add 对象存储子网/24 via 优选线路网关 dev eth1 table backup

# 4. 防止回包不对称:给优选线路加反向路径过滤宽松
sysctl -w net.ipv4.conf.eth1.rp_filter=2

备份进程以专用系统用户(uid=1000)运行,sudo -u backup rsync ...,流量自动走eth1,MTU通路清爽。

第三板斧:备份任务的tc限速(免拖业务)

BGP多线主机上跑着Nginx业务,备份不能占满出口带宽。用htb + iptables mark做精细化限速:

# 创建根队列(假设eth1带宽100Mbps)
tc qdisc add dev eth1 root handle 1: htb default 30
tc class add dev eth1 parent 1: classid 1:1 htb rate 100mbit ceil 100mbit

# 备份流class(limit to 30Mbps)
tc class add dev eth1 parent 1:1 classid 1:10 htb rate 20mbit ceil 30mbit

# 根据iptables的mark(200)匹配到class
tc filter add dev eth1 parent 1:0 protocol ip prio 1 handle 200 fw flowid 1:10

配合cgroup或cgroups v2更佳,但这里不展开。

验证:一夜稳如泰山

次日凌晨,备份全程跑完,日志干净:

2025-11-13 03:12:45 sent 143.68 GB  received 2.35 MB  117.34 MB/sec
total size is 142.51 GB  speedup is 0.99

速度反而比之前平均提升22%(没了重传和超时)。后续持续监控一周,0次断流。

经验沉淀

  • BGP多线主机不是“自动优化”:默认路由往往只选一条,其他线路闲置。需针对不同业务(备份、API、静态文件)写策略路由,做到流量级线路优选
  • MTU陷阱的隐藏性:常见于使用了VXLAN、PPPoE、IPsec的云网关。务必对备份端点做PMTU探测:tracepath -n 目标IP观察MSS变化。
  • 轻云互联的BGP多线主机提供了独立网卡和网关配置,正是这次策略路由的基础。若只有虚拟网卡,用ip addr add添加虚拟IP并配合src参数也能实现类似效果。
  • 自动化脚本:最终我将路由规则封装进/usr/local/bin/backup_traffic_steer.sh,开机自启,并在备份前后ping端点的MTU大小做自检。

附:最终排障用到的核武器命令合集

# 实时监控备份流进出接口
tcpdump -i any -nn 'port 873' -e -v | grep -E '(flags [S.]|mss)'

# 查看当前PMTU缓存(针对特定IP)
ip route get 目标IP lookup backup

# 强制重新探测PMTU
echo 1 > /proc/sys/net/ipv4/route/flush

# 检查BGP多线主机的路由表完整性
ip rule show; ip route show table 200

排障结束,又多了半小时摸鱼时间。