深蓝黑背景封面:右侧大标题「网络排障,按层选工具」,副标题「ping、traceroute、nslookup、netstat、tcpdump 各看哪一层」;左侧原创示意图——一座五层协议栈小楼,每层外墙挂着对应的工具铭牌:链路层挂 tcpdump 抓包镜、网络层挂 ping 与 traceroute 的探针、传输层挂 netstat 的状态表、应用层挂 nslookup 的域名册,一支放大镜检查光束自下而上扫过楼层;左上标注「知识专栏 · 网络排障」,右上标注「Day 08 · 11-05」 图 1:本文封面,每个工具守一层。

「输入 ping 回车后发生了什么」是道经典面试题,那道题把单个命令的协议栈旅程讲透了。真到线上排障,问题换了形状:用户报「服务打不开」,你手里有一整盒工具,先抄哪个、按什么顺序、每个工具的回答能信到哪一层?本文就讲这套分层排障方法论。

核心原则:每个工具只能回答某一层的问题。

排障效率的本质是控制排查成本——先用最便宜的工具把故障圈到某一层,再换重工具。五个工具各自的地盘:

ping:网络层通不通。发 ICMP ECHO_REQUEST 等回包,回答且仅回答「网络层是否可达、往返多久、丢不丢包」。ping 的 man 手册直接写了故障隔离的标准动作:先 ping 本机确认网卡活着,再 ping 越来越远的主机与网关。但它不回答「应用好不好」——ping 通而服务挂掉是常态,很多机器还干脆禁 ping。

**traceroute:断在哪一跳。**ping 不通时,它利用 TTL 逐跳递减、归零回 ICMP 超时的机制把路径逐跳摸出来,默认每跳三个探测包、上限 30 跳,某跳不回就打星号。它回答「哪一跳断了」,不回答「为什么断」——星号可能只是那台路由器限速不回 ICMP。

**nslookup / dig:名字解到哪。**通着但服务不可用时,先怀疑域名:解析出的 IP 对不对、缓存里的记录是不是旧的。用 dig @某权威服务器 域名 对比本地答案,不一致就是解析层的问题。它回答「名字解到哪」,不回答「服务活没活」。

**netstat / ss:本机连接什么状态。**解析对了再往下看传输层:ss -tan 按状态聚合,SYN_SENT 成片说明建不上,ESTABLISHED 只涨不落说明建了不用。netstat 手册已注明该工具废弃、推荐 ss。它回答「连接处于什么状态」,不回答「包里装的是什么」。

tcpdump:实际交互的全部细节。最重也最贵:按表达式过滤抓包,逐字段看握手、重传、RST、载荷,必要时 -w 落盘离线分析。它几乎能回答所有问题,但成本最高——数据量大、要会读、生产上有合规门槛——所以留到最后。

一次完整的分层排障,用模拟走一遍。

下面这段模拟注入「DNS 缓存了旧记录」的故障现场,按从轻到重的顺序走一遍五个工具,用 Node.js 22.15.0 直接运行(node netdiag.js):

'use strict';

// 分层排障模拟:注入「DNS 解析到旧 IP」故障,按 ping -> traceroute
// -> nslookup -> netstat -> tcpdump 的顺序走一遍,看每层工具给出什么答案。

// 故障现场:域名被解析到一台不存在服务的旧机器
const world = {
  domain: 'api.example.com',
  realIp: '93.184.216.34',   // 权威记录里现在的正确地址
  cachedIp: '192.0.2.8',     // 本地缓存里的旧地址(故障根源)
  hops: ['192.168.1.1', '10.20.0.1', '172.16.0.2', '192.0.2.8'], // 到旧机器全通
  capture: 'SYN 发出 -> RST,ACK 立刻回来(端口无人监听)',
};

console.log(`用户报告:访问 ${world.domain} 报连接失败\n`);

// 第一层:ping——回答「网络层通不通」
console.log(`$ ping ${world.domain}`);
console.log(`  来自 ${world.cachedIp} 的回复:icmp_seq=1 ttl=56 time=8.2 ms`);
console.log(`  判读:网络层可达。但注意——ping 的是解析出来的 ${world.cachedIp},对不对还没验证\n`);

// 第二层:traceroute——回答「哪一跳断了」
console.log(`$ traceroute ${world.cachedIp}`);
world.hops.forEach((h, i) => console.log(`  ${i + 1}  ${h}  1.0 ms`));
console.log('  判读:路径每一跳都通,问题不在路上\n');

// 第三层:nslookup/dig——回答「名字解到哪」
console.log(`$ nslookup ${world.domain}`);
console.log(`  Server: 10.0.0.2\n  Address: ${world.cachedIp}(本地缓存的旧记录)`);
console.log(`$ dig ${world.domain} @8.8.8.8`);
console.log(`  ANSWER SECTION: ${world.domain}. A ${world.realIp}`);
console.log(`  判读:两个答案不一致!本地缓存 ${world.cachedIp} != 权威 ${world.realIp},定位到解析层\n`);

// 第四层:netstat/ss——回答「连接什么状态」
console.log(`$ ss -tan dst ${world.cachedIp}`);
console.log('  SYN-SENT 0 1 192.168.1.7:54321 192.0.2.8:443(连旧地址,对端立刻 RST)');
console.log('  判读:连接根本建不起来,与「域名解错」互相印证\n');

// 第五层:tcpdump——最重,回答「实际交互细节」
console.log(`$ tcpdump -n host ${world.cachedIp} and port 443 -c 4`);
console.log(`  ${world.capture}`);
console.log('  判读:抓包确认是端口级拒绝,而非链路丢包——修复动作:清缓存/等 TTL 过期\n');

console.log('结论:工具越重越晚用;每一步都先问「这个工具能回答什么、不能回答什么」。');

实测输出:

用户报告:访问 api.example.com 报连接失败

$ ping api.example.com
  来自 192.0.2.8 的回复:icmp_seq=1 ttl=56 time=8.2 ms
  判读:网络层可达。但注意——ping 的是解析出来的 192.0.2.8,对不对还没验证

$ traceroute 192.0.2.8
  1  192.168.1.1  1.0 ms
  2  10.20.0.1  1.0 ms
  3  172.16.0.2  1.0 ms
  4  192.0.2.8  1.0 ms
  判读:路径每一跳都通,问题不在路上

$ nslookup api.example.com
  Server: 10.0.0.2
  Address: 192.0.2.8(本地缓存的旧记录)
$ dig api.example.com @8.8.8.8
  ANSWER SECTION: api.example.com. A 93.184.216.34
  判读:两个答案不一致!本地缓存 192.0.2.8 != 权威 93.184.216.34,定位到解析层

$ ss -tan dst 192.0.2.8
  SYN-SENT 0 1 192.168.1.7:54321 192.0.2.8:443(连旧地址,对端立刻 RST)
  判读:连接根本建不起来,与「域名解错」互相印证

$ tcpdump -n host 192.0.2.8 and port 443 -c 4
  SYN 发出 -> RST,ACK 立刻回来(端口无人监听)
  判读:抓包确认是端口级拒绝,而非链路丢包——修复动作:清缓存/等 TTL 过期

结论:工具越重越晚用;每一步都先问「这个工具能回答什么、不能回答什么」。

这个案例最反直觉的一点:ping 全通、路由全通,服务却是坏的——因为「通」的对象从一开始就错了。前两层工具的「一切正常」恰恰是误导,直到 dig 对比出两个答案才定位:每一层的问题都会伪装成其他层正常。

排障决策图:上方是自底向上的排查流水线——先用 ping 验证网络层可达,不通用 traceroute 找断在哪一跳;通了但服务不可用就查域名,nslookup 看解析出的 IP 对不对;解析对了用 netstat 看本机连接状态分布,判断是建不上还是建而不用;以上都正常才上 tcpdump 抓包,逐字段看交互细节。中部矩阵列出五个工具各自能回答与不能回答的问题:ping 答「网络层通不通」不答「应用好不好」,traceroute 答「哪一跳断了」不答「为什么断」,nslookup 答「名字解到哪」不答「服务活没活」,netstat 答「连接什么状态」不答「包里是什么」,tcpdump 答「实际交互」但成本最高。底部提示:工具越重越晚用,排障顺序本质是控制排查成本 图 2:排障顺序即成本控制。

**把方法论收成三句话。**第一,自底向上:链路层、网络层、解析层、传输层、应用层,层越低工具越便宜,先排除。第二,每个工具开口前先想清楚它能回答什么、不能回答什么——拿 ping 的正常去证明应用健康,就是典型的越层误用。第三,抓包留到最后:它是终极答案也是最高成本,前面四层能解决八成故障。跨机房调模型服务抖动时,这套顺序就是定位「哪一跳开始劣化」的标准动作。

你的排障习惯是顺手抓包党还是严格分层党?有没有过一次「抓了半小时包,结果发现是 DNS」的经历?欢迎交流。