1. 为什么这三个问题让我重新翻出了tcpdump
最近一段时间,我在排查几个线上环境的问题时,发现自己一次又一次地打开了同一个工具——tcpdump。说来有点意思,这个工具我用了快十年了,中间也尝试过各种图形化抓包方案,但每次遇到真正棘手的问题,最后还是得回到命令行里敲tcpdump。不是别的工具不好用,而是在某些场景下,tcpdump那种"直接、透明、可控"的特质,是其他工具替代不了的。
这篇文章不是tcpdump的使用手册,网上那种东西一搜一大把。我想做的是,把最近遇到的三个真实问题拿出来,完整地复盘一遍我是怎么用tcpdump一步步定位到根因的。这三个问题分别涉及连接建立异常、数据传输延迟、以及间歇性丢包,基本覆盖了日常工作中最常见的几类网络故障场景。每个问题的排查过程我都会尽量还原当时的思路,包括哪些弯路是我走过的、哪些参数组合是试了才知道好用的、哪些输出信息看起来像问题其实不是。
如果你平时工作中需要跟网络问题打交道,不管是后端开发、运维工程师还是SRE,这篇文章应该都能给你一些可以直接拿来用的思路。即使你之前没用过tcpdump,也不用担心,我会在排查过程中把用到的每个参数都解释清楚,保证你能跟着操作。当然,如果你已经有一定抓包经验,那我们可以直接聊那些更细的东西——过滤器怎么写更高效、抓包文件怎么分析更快、以及那些只有踩过坑才知道的注意事项。
先说一个我自己的习惯:每次遇到网络问题,我不会一上来就抓包。抓包本身有成本,尤其是在高流量的生产环境里,抓包文件可能几分钟就涨到几个G,分析起来也费劲。我的做法是先通过日志、监控指标、以及简单的连通性测试缩小范围,确定问题大概率出在网络层面之后,再针对性地抓包。这个思路在后面三个案例里都有体现。
2. 案例一:TCP连接建立慢的排查过程
2.1 问题现象与初步判断
第一个问题来自一个内部服务调用超时的告警。具体表现是,服务A调用服务B时,偶尔会出现请求超时,超时时间设置为3秒,但实际业务处理只需要几十毫秒。这个"偶尔"很关键——不是每次都超时,大概每几百次调用会出现一次。这种间歇性的问题最让人头疼,因为它很难稳定复现。
我先看了服务A的日志,发现超时的时候,请求甚至没有到达服务B。服务B那边完全没有收到请求的记录。这就排除了服务B处理慢的可能性,问题大概率出在连接建立阶段或者网络传输阶段。接下来我做了几件事:确认了两台机器的网络配置、检查了中间是否有负载均衡设备、看了下当时的系统负载和网络流量。这些信息看起来都正常,没有明显的异常。
到了这一步,我决定抓包。因为问题出现在连接建立阶段的可能性很大,所以我需要看到TCP三次握手的过程。如果握手本身就很慢,那问题就在网络层面;如果握手很快但后续数据传输慢,那问题可能在应用层或者TCP参数配置上。
2.2 抓包命令的选取与过滤器设计
抓包的位置很关键。因为服务A和服务B之间可能经过多个网络节点,我需要选择一个能同时看到双向流量的位置。最理想的位置是在服务A的宿主机上抓包,这样能看到服务A发出的所有包和收到的所有包。如果只能在服务B上抓,那就只能看到服务B视角的流量,有些信息会缺失。
我用的命令是这样的:
tcpdump -i eth0 -nn -s 0 -w /tmp/case1.pcap host 10.0.1.100 and port 8080解释一下这几个参数。-i eth0指定网卡,这个不用多说。-nn表示不解析主机名和端口名,直接显示IP和端口号,这样输出更干净,也避免DNS解析带来的额外延迟。-s 0表示抓取完整的数据包,不截断。默认情况下tcpdump只抓每个包的前96字节,对于分析TCP头部来说够用,但如果要看应用层数据就不够了。-w把原始包写入文件,方便后续用Wireshark等工具分析。最后的过滤器host 10.0.1.100 and port 8080限定只抓与服务B相关的流量,避免抓到无关的包。
这里有个经验:过滤器的写法直接影响抓包文件的大小和分析效率。我见过有人直接tcpdump -i eth0 -w output.pcap抓所有流量,结果几分钟就抓了几个G,分析的时候光打开文件就卡半天。正确的做法是根据你的目标尽可能缩小过滤范围。比如你知道问题只涉及某个端口,那就加上端口过滤;你知道只涉及某几台机器,那就加上主机过滤。
抓了大概五分钟,问题复现了两次。我把抓包文件拉到本地,用Wireshark打开分析。
2.3 从三次握手时间线定位问题
在Wireshark里,我过滤出有问题的那个TCP流,看它的时间线。正常的TCP三次握手应该是这样的:客户端发送SYN,服务端回复SYN-ACK,客户端再发送ACK。整个过程在局域网内通常在一毫秒以内完成。但我看到的这个流,SYN发出之后,过了将近1秒才收到SYN-ACK。
这个1秒很敏感。在TCP协议里,1秒是一个典型的初始重传超时时间(RTO)。也就是说,客户端发出SYN之后,如果没有收到SYN-ACK,会在1秒后重传SYN。我看到的这个延迟,很可能就是第一次SYN丢了,第二次重传才成功。
为了验证这个判断,我在Wireshark里看了这个流的完整包序列。果然,第一个SYN发出后没有任何响应,1秒后第二个SYN发出,这次很快就收到了SYN-ACK。问题定位到了:SYN包在去往服务B的路上丢了。
但为什么会丢?是网络设备的问题,还是服务B所在的宿主机的问题?我又看了其他正常的流,发现同一个时间段内,其他连接的三次握手都很正常,没有丢包。这就说明不是网络设备的普遍性问题,而是偶发的、针对特定包的丢失。
2.4 这个案例教给我的抓包位置选择经验
这个问题的根因最终定位在服务B所在宿主机的网卡队列上。当瞬时流量较大时,网卡队列溢出,导致部分SYN包被丢弃。解决方案是调整了网卡队列长度和相关的内核参数。但我想说的重点不是这个解决方案,而是抓包位置的选择。
如果当时我只在服务B上抓包,我会看到什么?我会看到第二个SYN到达,然后回复SYN-ACK。我根本看不到第一个SYN,因为它在到达服务B之前就已经丢了。这种情况下,我可能会误以为是服务A没有发送第一个SYN,或者以为是服务A的TCP协议栈有问题。只有在服务A这一侧抓包,才能看到"发了但没收到响应"这个关键信息。
所以我的经验是:抓包位置要尽量靠近问题的发起端,同时要确保能看到双向流量。如果条件允许,在两端同时抓包是最理想的,这样可以对比两端看到的数据,快速判断丢包发生在哪个方向、哪个节点。
另外,这个案例也让我养成了一个习惯:抓包的时候一定会加上-w参数保存原始文件。虽然实时看输出也能发现问题,但保存下来的文件可以在事后反复分析,尤其是当你要对比多个时间点的数据时,原始文件的价值就体现出来了。
3. 案例二:数据传输过程中的延迟抖动分析
3.1 延迟抖动的表现与排查思路
第二个问题是一个文件传输服务,用户反馈说下载速度时快时慢,快的时候能跑满带宽,慢的时候几乎停滞。这个问题比第一个更棘手,因为它不是"通"或"不通"的问题,而是"快"和"慢"的问题。而且"慢"是间歇性的,没有明显的规律。
我先做了一些基础排查。确认了带宽没有跑满,排除了带宽瓶颈。看了服务端的磁盘IO和CPU,都在正常范围内。用ping测试了基本的网络延迟,平均延迟正常,但偶尔会有几个包延迟明显偏高。这个信息很重要——ping的延迟抖动说明网络路径上确实存在某种不稳定因素。
接下来我决定用tcpdump抓包,重点看TCP数据传输过程中的行为。这次我关注的不是连接建立,而是数据传输阶段的ACK时序、窗口大小变化、以及是否有重传。
3.2 用tcpdump观察TCP窗口与重传
抓包命令和上次类似,但过滤器做了调整:
tcpdump -i eth0 -nn -s 0 -w /tmp/case2.pcap host 10.0.2.50 and port 9090抓了几分钟,拿到文件后用Wireshark分析。我重点关注了几个指标:TCP重传率、零窗口出现的次数、以及RTT的变化。
在Wireshark的"Expert Information"里,我看到了不少"TCP Retransmission"和"TCP Fast Retransmission"的提示。这说明确实有丢包发生。但丢包率并不高,大概在0.5%左右。按理说,TCP有重传机制,0.5%的丢包不应该导致传输速度从满带宽降到几乎停滞。
继续深入看,我发现了另一个现象:TCP窗口大小频繁地降到很低的值,甚至出现了零窗口。零窗口意味着接收方告诉发送方"我缓冲区满了,你先别发了"。这通常说明接收方的应用层读取速度跟不上,导致接收缓冲区被填满。
但用户反馈的是"下载速度慢",也就是接收方是用户的客户端。如果客户端处理不过来,那问题可能在客户端侧。可是用户说换了几个客户端都有类似问题,这就排除了单个客户端的问题。
3.3 从抓包文件反推应用层行为
这里有一个分析技巧:当你在传输层看到异常时,不要只盯着传输层,要往上想一想应用层在做什么。TCP窗口的变化反映的是应用层的读取行为。如果窗口频繁降到零,说明应用层读取数据的速度不稳定。
我回头看了服务端的日志,发现这个文件传输服务在处理每个请求时,会先做一些权限校验和元数据查询,这些操作会短暂地阻塞数据发送。虽然每次阻塞只有几十毫秒,但频繁的阻塞会导致TCP发送窗口无法持续打开,从而影响整体吞吐量。
这个问题的根因不在网络,而在应用层的设计。解决方案是优化了服务端的处理逻辑,把权限校验和元数据查询提前做好,数据传输阶段不再做这些阻塞操作。优化之后,传输速度稳定了很多。
但这个案例给我的启发是:tcpdump抓到的包是网络层的真相,但问题的根因可能在应用层。如果你只盯着TCP重传和窗口变化,可能会误以为是网络质量问题。只有结合应用层的日志和行为,才能找到真正的根因。
3.4 分析抓包文件时容易忽略的细节
在这个案例的分析过程中,我还注意到一个细节:Wireshark默认的时间显示格式是"Seconds Since Beginning of Capture",也就是从抓包开始算起的秒数。这个格式在分析长时间抓包文件时不太直观。我习惯把它改成"Time of Day",也就是实际的时间戳,这样更容易和日志里的时间对应上。
另外,Wireshark的"TCP Stream Graphs"功能很好用,尤其是"Time Sequence"图,可以直观地看到数据传输的时序。如果看到某段时间内数据发送停滞,再结合窗口大小的变化,就能快速判断是发送方的问题还是接收方的问题。
还有一个经验:分析抓包文件时,不要一上来就看细节。先看整体,比如用"Conversations"视图看看哪些流的流量最大、哪些流的重传最多。然后针对有问题的流深入分析。这样可以避免在无关的流上浪费时间。
4. 案例三:间歇性丢包的完整排查链路
4.1 丢包问题的复杂性
第三个问题是最难缠的。一个跨机房的服务调用,每隔十几分钟就会出现一次超时。超时时间设置为5秒,但正常情况下响应时间只有100毫秒左右。这个问题持续了将近一周,期间我断断续续地排查了很多次,每次都觉得快找到了,但又被新的证据推翻。
间歇性丢包之所以难排查,是因为它涉及的因素太多:可能是网络设备的问题、可能是主机的问题、可能是应用的问题、也可能是多个因素叠加。而且因为问题是间歇性的,抓包的时候不一定能抓到问题发生的时刻。
我的策略是:长时间抓包,用滚动文件的方式保存,然后等问题复现后回溯分析。具体命令是这样的:
tcpdump -i eth0 -nn -s 0 -w /tmp/case3_%Y%m%d%H%M%S.pcap -G 300 -W 12 host 10.0.3.100 and port 7070这里用了几个新参数。-G 300表示每300秒轮转一个新文件,-W 12表示最多保留12个文件。这样最多占用12个文件的空间,每个文件5分钟,总共可以覆盖最近1小时的抓包数据。当问题复现时,我只需要回溯最近1小时的文件,就能找到问题发生时的包。
这个滚动抓包的方案在生产环境里很实用。你不需要一直盯着,让它自己跑就行。等告警来了,再去分析最近的文件。当然,前提是磁盘空间要留够,每个文件的大小取决于流量大小,需要提前估算一下。
4.2 从ICMP与TCP的差异中找线索
在分析抓包文件时,我发现了一个奇怪的现象:ping测试偶尔会丢包,但TCP连接本身很少断开。也就是说,ICMP层面的丢包比TCP层面的丢包更明显。这个差异让我想到了一种可能性:网络路径上可能存在某种策略,对ICMP包和TCP包的处理方式不同。
为了验证这个想法,我在抓包文件里对比了ICMP和TCP的丢包模式。果然,ICMP丢包的时间点和TCP重传的时间点并不完全重合。ICMP丢包更频繁,但TCP重传相对较少。这说明网络设备可能对ICMP做了限速或者优先级调整,导致ICMP包更容易被丢弃。
但这并不能解释TCP层面的超时。TCP超时说明TCP包也丢了,只是丢包率比ICMP低。我继续深入分析TCP重传的包,发现了一个规律:重传的包大多是较大的数据包,而小的ACK包很少重传。这提示我可能是MTU(最大传输单元)相关的问题。
4.3 MTU与分片问题的验证方法
MTU问题是一个经典但容易被忽略的问题。当网络路径中某一段的MTU小于发送方使用的MTU时,大的数据包会被分片或者丢弃。如果设置了"禁止分片"标志,包就会被丢弃,并返回ICMP需要分片的错误消息。但如果ICMP消息被网络设备过滤了,发送方就不知道需要调整MTU,会一直重传大的包,导致超时。
我用ping命令做了MTU探测:
ping -M do -s 1472 10.0.3.100-M do表示禁止分片,-s 1472表示发送1472字节的数据(加上28字节的ICMP头部和IP头部,总共1500字节,也就是标准以太网的MTU)。如果这个命令成功,说明路径MTU至少是1500。如果失败,说明路径中有更小的MTU。
测试结果是不稳定,有时候成功有时候失败。这进一步印证了MTU问题的可能性。最终定位到是中间某台网络设备的MTU配置不一致,导致部分大的数据包被丢弃。调整MTU配置后,问题解决。
4.4 长时间抓包的文件管理策略
这个案例让我对长时间抓包的文件管理有了更深的认识。滚动抓包虽然好用,但有几个细节需要注意。
第一,文件命名要包含时间戳,这样回溯的时候能快速定位到问题发生的时间段。我用的%Y%m%d%H%M%S格式可以精确到秒,足够用了。
第二,要估算好磁盘空间。每个文件的大小取决于流量大小和抓包时长。如果流量很大,5分钟可能就好几百兆。12个文件就是好几个G。在生产环境里,一定要提前确认磁盘空间是否足够。
第三,分析的时候不要逐个文件打开。先用tcpdump -r命令快速浏览每个文件的时间范围和包数量,找到问题时间段对应的文件,再深入分析。比如:
tcpdump -r /tmp/case3_20240101120000.pcap -nn | head -5这样可以快速看到文件的起始包,判断时间范围。
第四,如果问题涉及多个服务之间的调用,可以考虑在多个节点同时抓包。但这样会产生多个抓包文件,分析的时候需要对齐时间。我的做法是在抓包前先同步各节点的时间,确保时间戳一致。
5. 三个案例背后共通的抓包心法
5.1 抓包之前先想清楚要看什么
回顾这三个案例,我发现一个共同点:每次抓包之前,我都已经对问题有了一个初步的判断,抓包是为了验证或推翻这个判断。而不是盲目地抓一堆包,然后漫无目的地看。
比如第一个案例,我判断问题可能在连接建立阶段,所以重点看三次握手。第二个案例,我判断问题可能在数据传输阶段,所以重点看窗口和重传。第三个案例,我判断问题可能是间歇性的,所以采用了长时间滚动抓包的策略。
这种"带着假设去抓包"的方式,比"先抓了再说"效率高很多。因为你知道自己要找什么,就能设计更有针对性的过滤器,分析的时候也能更快地聚焦到关键信息上。
当然,假设可能是错的。但即使错了,你也能从抓包结果里得到新的线索,然后形成新的假设,再验证。这是一个迭代的过程,比漫无目的地看包要快得多。
5.2 过滤器是tcpdump最值得花时间学的部分
tcpdump的过滤器语法(BPF语法)是它最强大的功能之一,但也是很多人不愿意花时间学的部分。我见过不少人只会用host和port这两个最简单的过滤器,遇到复杂场景就不知道怎么写了。
其实BPF语法并不难,常用的就那么几种组合。比如and、or、not的逻辑组合,src和dst的方向限定,tcp、udp、icmp的协议限定。掌握这些之后,你就能写出很精确的过滤器。
举个例子,如果你想抓所有从10.0.1.100发往10.0.2.50的TCP包,但排除8080端口,可以这样写:
tcpdump -i eth0 -nn 'src host 10.0.1.100 and dst host 10.0.2.50 and tcp and not port 8080'过滤器写得越精确,抓到的包就越少,分析起来就越快。尤其是在高流量的生产环境里,一个精确的过滤器可以帮你节省大量的时间和磁盘空间。
5.3 抓包文件的分析工具选择
虽然tcpdump本身可以读取抓包文件(用-r参数),但它的分析能力有限,只能看包的头部信息和简单的统计。真正深入分析还是得靠Wireshark或者tshark。
Wireshark的图形界面很适合交互式分析,尤其是它的过滤、着色、统计功能,可以帮你快速定位问题。但Wireshark在处理大文件时可能会比较慢,这时候可以用tshark(Wireshark的命令行版本)来做一些预处理。
比如,你可以用tshark快速统计重传包的数量:
tshark -r /tmp/case3.pcap -Y "tcp.analysis.retransmission" -T fields -e frame.number -e ip.src -e ip.dst | wc -l这样可以快速知道抓包文件里有多少重传,如果数量很少,就可以排除重传导致的问题。
另外,如果你需要长期监控网络质量,可以考虑用一些基于tcpdump的自动化工具,定期抓包并分析关键指标。但这属于另一个话题了,这里不展开。
5.4 那些只有踩过坑才知道的注意事项
最后分享几个我在使用tcpdump过程中踩过的坑,希望能帮你少走弯路。
第一个坑:抓包本身会影响网络性能。尤其是在高流量的生产环境里,tcpdump抓包会消耗CPU和内存,可能导致网络延迟增加。所以抓包的时候要尽量缩小过滤范围,减少抓包量。如果可能的话,在非高峰时段抓包。
第二个坑:抓包文件可能包含敏感信息。tcpdump抓的是原始网络包,如果流量没有加密,包里的内容可能包含密码、令牌等敏感数据。所以抓包文件要妥善保管,分析完之后及时删除。如果需要在团队内共享,最好先脱敏。
第三个坑:时间戳的精度问题。tcpdump默认的时间戳精度是微秒级,但在某些系统上可能只有毫秒级。如果你需要分析非常精细的时序问题,可能需要调整时间戳精度。可以用--time-stamp-precision参数来指定。
第四个坑:网卡混杂模式的影响。默认情况下,tcpdump会把网卡设置为混杂模式,这样可以看到所有经过网卡的包,包括不是发给本机的包。在交换式网络里,这通常只能看到本机的流量和广播流量。如果你需要看到其他机器的流量,需要配置端口镜像。但混杂模式在某些环境下可能会被安全策略阻止,导致抓不到包。
第五个坑:抓包文件的权限问题。tcpdump需要root权限才能抓包,但抓包文件的属主是root。如果你用普通用户分析文件,可能会遇到权限问题。可以在抓包后修改文件权限,或者用-Z参数指定抓包后切换到的用户。
这三个案例虽然场景不同,但排查思路是相通的:先缩小范围,再针对性抓包,然后结合应用层信息综合分析。tcpdump只是一个工具,真正重要的是你分析问题的思路。工具用熟了,思路清晰了,大部分网络问题都能在比较短的时间内定位到根因。