网络排查的思路:逐层定位,从底层往上
网络故障最怕「瞎试」。运维老手的做法是按 OSI 分层逐层排查:先确认网络层(IP 通不通、丢不丢包),再确认传输层(端口在不在监听、TCP 状态健不健康),最后看应用层(DNS 解析对不对、HTTP 有没有正常返回)。每一层都有对应的工具,哪一层出问题就停在哪一层,不会在错误的层面浪费时间。
| 排查层 | 工具 | 要回答的问题 |
|---|---|---|
| 网络层 | ping、traceroute、mtr | IP 通不通?丢包发生在哪一跳? |
| 传输层 | ss | 端口在监听吗?TCP 状态健康吗? |
| 应用层 | dig、curl | DNS 解析正确吗?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 跳。

注意第 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 才能看到别人的进程)。
下面的实测输出,把这台服务器上「谁在监听什么端口」看得清清楚楚:

解读这张表,有三点经验:
- 看绑定地址判断能不能被外部访问:
0.0.0.0:22表示 sshd 监听所有网卡,外部可以连;127.0.0.1:46043的 containerd 只监听回环,外部永远连不上——这是设计,不是故障;而如果服务本应对外却绑了 127.0.0.1,外部连它时会表现为一直超时(SYN 被静默丢弃),查半天防火墙不如先看一眼这列。 - systemd-resolved 只在 127.0.0.53 上监听 53 端口,这是 DNS 缓存解析器,后面 dig 一节会再遇到它。
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 msec 和 SERVER: 127.0.0.53:请求是由本机 systemd-resolved(127.0.0.53 缓存解析器)直接应答的,根本没出网,所以快到 1ms。如果答案不在缓存里,dig 会显示真正的上游解析器地址,Query time 也会变成几十毫秒。
两个实用技巧:
- 对比「本机解析 vs 公共 DNS」:怀疑解析有问题时,
dig 域名和dig @8.8.8.8 域名各查一次,结果不一致就说明问题出在本机解析链路(systemd-resolved 配置、/etc/resolv.conf、上游 DNS),而不是域名本身。 - 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_namelookup | DNS 解析耗时 | 本机 DNS 配置或上游解析器慢 |
| time_connect - time_namelookup | TCP 握手耗时 | 网络往返长、有丢包重传 |
| 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 触发一次真实查询,正好抓到一进一出两个包:

第一行 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.243 和 104.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 步全是浪费时间。逐层定位,是网络排查里唯一不会走弯路的方法。
参考资料
- ss(8) — Linux manual page(Ubuntu)
- How to properly interpret a traceroute or MTR(APNIC Blog)
- bind9-dnsutils 软件包(Ubuntu Packages)
- curl man page(计时变量 time_namelookup 等)
- tcpdump(8) man page
- How to Use the ss Command as a Modern Netstat Replacement
- How do I measure request and response times at once using cURL?(Stack Overflow)
评论 (0)
暂无评论,快来抢沙发吧!