暗色模式

Linux 网络排查实战:从 ping 到 tcpdump 的故障定位手册

技术教程
2026-08-05
11
0

网络排查的思路:逐层定位,从底层往上

网络故障最怕「瞎试」。运维老手的做法是按 OSI 分层逐层排查:先确认网络层(IP 通不通、丢不丢包),再确认传输层(端口在不在监听、TCP 状态健不健康),最后看应用层(DNS 解析对不对、HTTP 有没有正常返回)。每一层都有对应的工具,哪一层出问题就停在哪一层,不会在错误的层面浪费时间。

排查层工具要回答的问题
网络层ping、traceroute、mtrIP 通不通?丢包发生在哪一跳?
传输层ss端口在监听吗?TCP 状态健康吗?
应用层dig、curlDNS 解析正确吗?HTTP 响应正常吗?
数据包层tcpdump请求真的发出去了吗?对端到底回了什么?

下面按这个思路,从最常用的 ping 一路走到抓包工具 tcpdump。文中所有命令都在 Ubuntu 24.04 演示服务器上真实执行过(dig 9.18.39、curl 8.5.0、tcpdump 4.99.4),照着敲即可。

第一层探测:ping 与连通性检查

ping 是最快的「探路石」,通过 ICMP Echo 请求确认目标 IP 是否可达。常用参数:

ping -c 4 8.8.8.8        # 只发 4 个包,否则会一直 ping 下去
ping -c 4 -i 0.5 <ip>    # 调整发包间隔(秒),默认 1 秒
ping -c 4 -s 1400 <ip>   # 加大包体,配合测 MTU 分片问题

输出的重点在两块:丢包率rtt(往返时延)0% packet loss 说明网络层通畅;rtt 的 avg 是这条链路的基准延迟,以后出问题时的对比基线。

阿里云等云主机的第一个坑:ping 不通 ≠ 服务不可用

云厂商的安全组默认规则经常屏蔽 ICMP。这种情况下 ping 会一直超时,但 SSH、HTTP 全都正常。所以 ping 不通先别慌,用 nc -vz <ip> 22 或直接 ssh 试一下,如果 TCP 端口能通,说明只是 ICMP 被拦,不是网络断了。

路径追踪:traceroute 与 mtr

ping 告诉你「通不通」,traceroute 告诉你「走哪条路、断在哪一站」。原理:逐跳递增 IP 包的 TTL,每经过一台路由器 TTL 减 1,减到 0 时路由器会返回 ICMP Time Exceeded 包,于是就能收集到每一跳的地址和时延。

traceroute -n -m 10 8.8.8.8     # -n 不解析主机名,更快更干净; -m 限制最大跳数
traceroute -I -m 10 <域名>      # 用 ICMP 探测(默认 UDP,部分路由会拦 UDP)

看下面的真实输出:从这台阿里云香港机器到 8.8.8.8,一共 8 跳。

ping 与 traceroute 实测:4 个包零丢包,路径 8 跳到达 8.8.8.8

注意第 4、6、7 跳显示 * * *这不代表链路断了——很多路由器出于安全或限速策略,不回应 TTL 超时探测包。判断标准是「最后的可达跳」:只要最后一跳(第 8 跳)能正常到达目的地,中间跳的 * 基本都是探测包被忽略,不是故障。

mtr:把 traceroute 变成统计

traceroute 每一跳只探 3 次,「掷三次骰子」看不出偶然丢包。mtr 持续发包并统计每跳的丢包率和时延分布,是判断「到底哪一跳在丢包」的标准工具:

mtr -rwc 100 blog.astarry.top    # -r 报表模式 -w 宽行 -c 发 100 轮

输出每一跳一行的 Loss%/Snt/Last/Avg/Best/Wrst/StDev 列。判读有一条铁律:只有「从某跳起到最后一跳持续丢包」才算真丢包;某一跳显示丢包但后面一跳 0% 丢包,那只是这一跳设备对探测包限速,属于正常现象。

传输层排查:ss 看端口与 TCP 状态

ss(socket statistics)是 iproute2 自带工具,也是被弃用的 netstat 的现代替代,输出更快、信息更全、直接读内核。最常用的组合:

ss -tlnp        # 查看所有 TCP 监听端口及所属进程
ss -tunp        # TCP/UDP 全部连接及进程
ss -s           # 连接状态汇总
ss -tan state time-wait | wc -l   # 统计某种状态的连接数

-t 只看 TCP,-l 只看监听(listening),-n 不解析名字,-p 显示进程(需要 root 才能看到别人的进程)。

下面的实测输出,把这台服务器上「谁在监听什么端口」看得清清楚楚:

dig 与 ss 实测:域名解析到 38.76.201.7,监听端口与进程一目了然

解读这张表,有三点经验:

  1. 看绑定地址判断能不能被外部访问:0.0.0.0:22 表示 sshd 监听所有网卡,外部可以连;127.0.0.1:46043 的 containerd 只监听回环,外部永远连不上——这是设计,不是故障;而如果服务本应对外却绑了 127.0.0.1,外部连它时会表现为一直超时(SYN 被静默丢弃),查半天防火墙不如先看一眼这列。
  2. systemd-resolved 只在 127.0.0.53 上监听 53 端口,这是 DNS 缓存解析器,后面 dig 一节会再遇到它。
  3. Recv-Q/Send-Q 在 LISTEN 行里不是队列长度,而是「已建立但还没被应用 accept() 的连接数 / 队列上限」,两者相等(如 128/128)说明 accept 队列满了、内核开始丢新连接,要查应用而不是调参数。

三种必会的 TCP 状态解读

ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn   # 一键统计各状态数量
  • TIME_WAIT 多:正常。主动关闭的一方要保留四元组约 60 秒防止旧包混淆新连接,量大只占一点内存、不占文件描述符,几千个都不算故障。千万不要去调 tcp_tw_recycle——它因会破坏 NAT 环境,早在内核 4.12 就被移除了。
  • CLOSE_WAIT 堆积(上百上千):这才是真故障信号。对端已经发了 FIN,但你的应用一直没调用 close(),内核不会超时回收,会一直占着文件描述符。这是应用层的 bug(连接没用完就丢了、池子没回收),不是网络问题,排查 ss -tnp state close-wait 拿到 PID 去查应用。
  • SYN-SENT 长时间卡住:TCP 三次握手的 SYN 发出去后没人应答。注意:如果端口被拒绝,会立刻收到 RST,状态不会停留;卡在 SYN-SENT 说明中间有防火墙在静默丢弃,去查安全组/防火墙规则,而不是应用。

DNS 排查:dig 看懂解析链路

域名解析慢、解析出错是「看起来像网络问题」的高发区。dig 是排 DNS 的瑞士军刀:

dig blog.astarry.top               # 完整输出,含各段详情
dig blog.astarry.top +short        # 只要解析结果(IP),脚本里常用
dig @8.8.8.8 example.com A +short  # 指定 DNS 服务器查询,绕过本机缓存

上面的实测截图里,dig blog.astarry.top 的关键信息:

  • ANSWER SECTION:blog.astarry.top. 538 IN A 38.76.201.7——解析到的 A 记录是 38.76.201.7;中间的 538 是 TTL(缓存存活秒数)的剩余值,站点的 TTL 配置是 600 秒,显示 538 说明这条答案是从缓存里来的
  • Query time: 1 msecSERVER: 127.0.0.53:请求是由本机 systemd-resolved(127.0.0.53 缓存解析器)直接应答的,根本没出网,所以快到 1ms。如果答案不在缓存里,dig 会显示真正的上游解析器地址,Query time 也会变成几十毫秒。

两个实用技巧:

  1. 对比「本机解析 vs 公共 DNS」:怀疑解析有问题时,dig 域名dig @8.8.8.8 域名 各查一次,结果不一致就说明问题出在本机解析链路(systemd-resolved 配置、/etc/resolv.conf、上游 DNS),而不是域名本身。
  2. Ubuntu 24.04 里 dig 由 bind9-dnsutils 包提供(旧包名 dnsutils 现在只是指向它的过渡包),装过一次就有 dig、nslookup、host 全套。

应用层验证:curl 计时定位瓶颈

连得上、解析也对,但用户说「打开很慢」,这时候用 curl 的 -w 计时功能把请求拆成几段,一看便知慢在哪:

curl -o /dev/null -s -w 'DNS解析: %{time_namelookup}s
连接建立: %{time_connect}s
首字节TTFB: %{time_starttransfer}s
总耗时: %{time_total}s
' https://blog.astarry.top/

四个时间变量都是从请求开始累计的,不能直接相减,但差值就是分段耗时:

差值含义数值大说明
time_namelookupDNS 解析耗时本机 DNS 配置或上游解析器慢
time_connect - time_namelookupTCP 握手耗时网络往返长、有丢包重传
time_starttransfer - time_connect服务器处理时间(TTFB)后端应用慢,与网络无关
time_total - time_starttransfer内容下载耗时带宽小或响应体大

再配合 curl -v 看握手细节(是否走了代理、TLS 版本、每个阶段的耗时),绝大多数「慢」的根因都能定位到具体阶段。curl 是每台服务器默认自带的,不用装任何东西。

终极武器:tcpdump 抓包分析

前面所有工具都是「看结果」,tcpdump 是直接「看过程」——把网卡上流过的包原样抓下来,前端说「我请求了但没反应」时,只有它能让双方对质:

tcpdump -nn -l -c 2 host 8.8.8.8 and port 53   # 抓指定主机的 DNS 流量,抓到 2 个包自动停
tcpdump -nn tcp port 80                        # 抓 80 端口全部 TCP 流量
tcpdump -i any -w /tmp/dns.pcap port 53        # 存成 pcap 文件,之后用 Wireshark 分析
tcpdump -nn -r /tmp/dns.pcap | head            # 读回 pcap 文件

过滤表达式是核心: host <ip> 按主机、port 53 按端口、tcp/udp 按协议,可用 and/or/not 组合;-nn 不做任何反解(快且不产生额外 DNS 流量)、-c N 抓到 N 个包自动退出(不用 Ctrl+C)、-l 行缓冲输出方便管道、-w 存文件。

下面的实测:让 tcpdump 监听 DNS 流量,同时用 dig @8.8.8.8 example.com A +short 触发一次真实查询,正好抓到一进一出两个包:

tcpdump 实测:抓到 dig 发出的 DNS 查询与应答两个包,2 captured 0 dropped

第一行 IP 172.28.141.96.49823 > 8.8.8.8.53: 30247+ [1au] A? example.com. 是本机发出的 DNS 查询(源端口 49823 → 目标 8.8.8.8:53);第二行是 8.8.8.8 的应答,2/0/1 表示返回了 2 条 A 记录:172.66.147.243104.20.23.154。结尾 2 packets captured / 0 packets dropped 说明抓取完整无丢包。

判断「请求到底有没有发出、发给了谁、对方回了什么」,一张抓包图胜过十次争论。这也是和云厂商/IDC 工单时最有说服力的证据。

实战:端口不通的五步定位法

把上面的工具串成一个标准流程,处理「服务连不上」类故障时按顺序走,五步之内必见分晓:

# 1. 网络层:目标 IP 通不通
ping -c 4 <目标IP>

# 2. 路径:哪一跳在丢包(不通时)
mtr -rwc 100 <目标IP>

# 3. 传输层:目标端口在监听吗?(本机) / 端口可达吗?(对端)
ss -tlnp | grep <端口>
nc -vz <目标IP> <端口>

# 4. 应用层:DNS 解析对不对、HTTP 有没有响应
dig <域名> +short
curl -v https://<域名>/

# 5. 数据包层:请求真的发出去了吗、对端回了什么
tcpdump -nn port <端口>

一条经验:第 3 步的 nc -vz(或 nc -zv)是连接测试神器,端口通会立刻返回 succeeded!,不通会明确提示 Connection refused(对端主动拒绝,说明服务没在听)或一直卡住(被防火墙静默丢弃)。

排查顺序反过来也成立——如果第 1 步就失败,后面 4 步全是浪费时间。逐层定位,是网络排查里唯一不会走弯路的方法。

参考资料

发表评论

暂无评论,快来抢沙发吧!