晚高峰的网络丢包,是所有运维问题里最让人头疼的一类。它不像硬件故障那样干脆利落——宕机了、硬盘报错了,报警立刻响起来,谁都知道出了什么事。丢包不一样,它往往只在特定时段出现,白天安安静静,一到晚上八点就开始零星掉包,等到十一点又自己好了。你登录服务器想看一眼,什么异常都看不到,日志干干净净,CPU 内存都正常。
这种问题排查起来需要策略,核心逻辑很简单:既然它只在特定时间出现,你就要在特定时间盯着它。MTR 就是干这个的。
MTR 把 traceroute 和 ping 的能力揉在一起,既能看到数据包从源到目标经过的每一跳,又能对每一跳做持续的延迟和丢包统计。单次运行的 MTR 只能抓一个瞬间的快照,但晚高峰丢包是时段性现象,所以真正有效的手段是让 MTR 持续跑下去,覆盖整个故障窗口,然后把数据拉出来做对比分析。
下面说清楚具体怎么操作。
第一步:确认目标与方向
在跑任何测试之前,先搞清楚两件事。
测谁。 晚高峰丢包往往发生在特定链路上。是用户反馈访问你的服务器慢?还是你的服务器调用某个外部接口超时?目标 IP 决定了你关注的是哪一段链路。如果是用户访问服务器丢包,理想情况下应该在用户侧也做一次 MTR,因为 ICMP 的往返路径可能不对称,单向丢包在源侧测不出来。
丢包的“方向”。 MTR 默认测的是从你执行命令的这台机器到目标 IP 的路径。如果你的服务器是“被访问方”,在服务器上跑 MTR 测用户 IP,测的是返程路径。更准确的做法是在至少两个方向都采集数据,但实际操作中,从服务器侧向多个用户侧 IP 做 MTR 已经能提供足够的信息量来定位问题区间。
第二步:构造 MTR 命令
日常交互式使用的 mtr 目标IP 不适合 24 小时采集,因为它会占住终端,而且输出是实时刷新的,没法直接存成可分析的数据。你需要用报告模式加上日志重定向。
核心命令:
mtr -rn -c 100 --report 目标IP >> /var/log/mtr_$(date +%Y%m%d_%H).log
拆解一下:
-r 或 --report:让 MTR 跑完指定的探测轮数后输出一份汇总报告然后退出,而不是持续刷新界面。这是自动化采集的基础。
-n:不做 DNS 反向解析,直接显示 IP。一是避免 DNS 查询本身引入的延迟干扰测试,二是 log 里全部是 IP,后续用脚本处理时不用处理域名解析的耗时问题。
-c 100:每次运行发送 100 个探测包。默认是 10 个,对于 24 小时监测来说太少了。假设你每分钟跑一次,每次 100 个包,那么一分钟的采样窗口内就有 100 个样本,丢包率计算的统计意义足够。如果晚高峰丢包率很低(比如 0.5% 以下),可以适当增加到 200 或 300。
关于协议的选择。 默认 MTR 用 ICMP。但很多网络设备对 ICMP 的处理是“尽力而为”,路由器 CPU 忙的时候优先丢 ICMP,转发真实业务数据时反而不会。这意味着 ICMP 测出来的丢包可能比实际业务丢包更严重,产生假阳性。如果目标服务器开放了 TCP 端口,用 -T -P 端口号 走 TCP 探测更能反映真实业务流量的链路质量。比如测 80 端口:
mtr -rn -T -P 80 -c 100 --report 目标IP >> /var/log/mtr_$(date +%Y%m%d_%H).log
不过 TCP 模式对中间路由器的回显依赖取决于设备的策略,有时中间跳不回显但末跳正常,这不算故障。
第三步:让它在晚高峰持续跑
MTR 本身没有“跑 24 小时”的原生参数。-c 指定的是探测轮数,跑完就退出。你需要用外部调度来制造持续监测。
最直接的方式是配合 watch 或者 shell 循环。但更干净的做法是写一个简短的 shell 脚本,用 cron 调度:
#!/bin/bash
TARGET="10.0.0.1"
LOG_DIR="/var/log/mtr_monitor"
mkdir -p "$LOG_DIR"
mtr -rn -c 100 --report "$TARGET" >> "$LOG_DIR/mtr_$(date +%Y%m%d_%H%M).log"
然后把脚本加到 crontab 里,晚高峰时段每分钟执行一次:
# 每 2 分钟跑一次,18:00 到 23:59
*/2 18-23 * * * /usr/local/bin/mtr_collect.sh
为什么是每 2 分钟而不是连续不断?因为每次 MTR 报告模式本身就要跑 100 个探测包,按默认 1 秒间隔就是 100 秒。每 2 分钟一次意味着探测窗口之间有大约 20 秒的空隙,这个采样密度足够捕捉晚高峰的丢包趋势。如果你想要几乎无间隙的覆盖,可以把 -c 降到 50,间隔改成 1 分钟。
重要提醒:不要在终端会话里直接跑循环。 如果你的 SSH 连接断了,脚本就死了。用 nohup、screen 或 tmux 让它在后台跑。或者直接用 crontab,crontab 的任务不依赖登录会话。
日志管理。 24 小时下来,如果每分钟一个文件,就是 1440 个小文件。更实用的方式是追加到按小时命名的文件里,上面示例命令里的 $(date +%Y%m%d_%H) 就是这个意思。这样一天只有二十几个文件,回看时按小时切分也很直观。
第四步:解读 24 小时数据
拿到 24 小时的 MTR 日志后,真正的活才开始。你需要从一堆文本里找出模式。
先做时间维度的对比。 把晚高峰(比如 20:00-23:00)的日志文件和凌晨(比如 03:00-05:00)的文件并排打开,看同一目标 IP 的同一跳节点。如果某一跳在凌晨的 Loss% 是 0.0%,在晚高峰变成 3.2%,而且后续所有跳的丢包率都至少是这个数,那问题就锁定了:从这一跳开始,链路出现了真实的丢包。
理解“中间跳丢包但末跳不丢”的情况。 这是 MTR 解读中最容易误判的场景。第三跳显示 Loss 15%,第四跳又回到 0%,末跳也正常。这说明什么?大概率是第三跳的路由器对 ICMP 做了速率限制或者低优先级处理,它“不想回”探测包,但并不代表它“转不动”真实数据包。判断方法是看后续跳:如果后续跳的丢包率和第三跳一致或更高,说明丢包是真实的,从第三跳开始就丢了;如果后续跳恢复了,那第三跳的丢包是假象,链路本身没问题。
看 StDev 列。 标准偏差高意味着延迟抖动大。晚高峰时某个节点的 Avg 可能只涨了 5ms,但 StDev 从 2ms 跳到 50ms,说明这个节点的响应时间极不稳定,典型的拥塞信号。丢包往往伴随着高 StDev 一起出现,或者先出现高 StDev,随后丢包率上升。
定位责任边界。 拿到丢包节点 IP 后,查它的归属。如果丢包出现在你的本地网络出口之后的第一个运营商节点,问题在运营商接入侧;如果出现在跨网互联的骨干节点,是运营商之间的问题;如果已经进入目标机房的内网但还在丢,那是目标侧的问题。这个判断决定了你该联系谁——自己的网络团队、云厂商、还是运营商。
最后,别忘了检查服务器自身。 MTR 显示末跳丢包,不一定是网络的问题。服务器网卡队列溢出、CPU 软中断过高处理不过来、或者内核网络参数配置不当,都会在晚高峰流量增大时表现为丢包。用 ip -s link show 看接口的 RX/TX dropped 计数有没有在测试期间增长,用 ethtool -S 看更细的丢包统计。如果服务器侧网卡有 dropped,那链路再干净也没用,瓶颈在服务器自己身上。
一个容易忽略的点:反向路径
MTR 测的是去程,但 IP 网络的路由是不对称的。你去程经过的节点,和返程经过的节点可能完全不同。用户访问你的服务器丢包,可能是用户到服务器的去程丢了,也可能是服务器到用户的返程丢了。只在服务器上跑 MTR,你看到的是返程情况。如果返程一切正常但用户仍然抱怨丢包,问题可能在去程,而这一侧你从服务器上是看不到的。
最彻底的做法是在目标侧也部署同样的 MTR 采集,测你的服务器 IP。但多数情况下做不到,那就至少利用多个监测点的数据来交叉验证。
晚高峰丢包的本质是资源竞争——链路带宽、路由器转发表项、服务器网卡队列,在流量高峰时都不够用了。MTR 的价值在于把“不够用”这件事拆解到具体的节点上。
流程归纳起来就四步:确定目标和协议,用 -rn -c 加循环做自动化采集,覆盖完整晚高峰时段,最后按时间对比加逐跳分析找丢包起点。难点不在命令本身,而在解读数据时区分“真丢包”和“ICMP 限速假象”,以及理解去程返程的不对称性。把这两点搞清楚,大部分晚高峰丢包问题都能在链路层面定位到具体区间,剩下的就是拿着 IP 找对应的人处理。
推荐文章