把「连不上」这条报障拆成四层
一句话总结
网络故障诊断中,90% 的问题都可以通过**先确定“故障停在哪一层”**来解决。不先判断层次就打开各种工具,只会让时间白白流逝。
为什么需要分层诊断
接到“API 连不上”的报障后,人们通常会这样做:先 ping,再打开防火墙规则,然后检查 DNS,接着翻找应用程序日志。整个过程没有顺序,因此,同一个地方会检查两遍,而漏掉的地方始终没有人看。
经验丰富的人采用不同的方法:先确定故障所在的层次,然后只使用与该层对应的一种工具。
它是如何工作的
实际工作中无需套用完整的 OSI 七层模型,下面四层就足够了。
| 层次 | 问题 | 检查工具 | 失败时的表现 |
|---|---|---|---|
| 名称 | 这个名称会解析成哪个 IP | getent hosts、dig、resolvectl query |
Name or service not known |
| 可达性 | 数据包能否到达该 IP | ping、traceroute -U、ip route |
Network is unreachable、无响应 |
| 端口 | 该 IP 上的指定端口是否开放 | nc -zv、ss -ltn、curl --connect-timeout |
Connection refused / timed out |
| 响应 | 端口开放后,响应是否异常 | curl -v、curl -w、应用程序日志 |
5xx、缓慢、响应体错误 |
关键在于,每一层的故障都有不同的表现。 只要准确读懂这些表现,就能确定问题所在的层次。
记住下面这张 errno 判断表,就能仅凭报障描述区分层次。
| 消息 | errno | 实际发生的事情 | 首先检查哪里 |
|---|---|---|---|
Name or service not known |
EAI_NONAME | 名称解析失败,甚至还没有打开套接字 | nsswitch.conf、resolv.conf、/etc/hosts |
Network is unreachable |
ENETUNREACH | 路由表中不存在可用路径 | ip route,尤其是 IPv6 路由 |
No route to host |
EHOSTUNREACH | 收到 ICMP unreachable,或 ARP 没有响应 | 目标设备电源、ARP、REJECT 规则 |
Connection refused |
ECONNREFUSED | 目标返回了 RST | 目标主机上的进程与绑定地址 |
Connection timed out |
ETIMEDOUT | SYN 全部重传后仍然没有响应 | 防火墙 DROP、安全组、accept 队列 |
这里最常见的错误,是把 refused 归咎于防火墙。refused 表示收到了 RST;既然能收到 RST,就证明流量已经通过了路由、安全组、NAT 与网络策略。生产防火墙几乎总是采用 DROP 策略,因此,被阻止的端口通常表现为 timeout。看到 refused 后继续排查防火墙,等于重新检查一道已经通过的关卡。
时间本身也是证据。在 Linux 默认设置(net.ipv4.tcp_syn_retries=6)下,无响应的连接会在 1+2+4+8+16+32+64 = 127 秒后放弃。如果应用程序配置中明明写着 30 秒超时,实际却在 127 秒后才失败,就说明该超时配置并未生效。
实际工作中会遇到的情况
“本机可以连接,只有远端不行。” 十有八九是绑定地址的问题。如果 ss -ltnp 显示进程监听在 127.0.0.1:8080,来自 Pod IP 的连接就会被内核用 RST 拒绝——这就是 refused。相反,如果连接 Pod IP 也出现 timeout,此时才应该开始检查网络策略。仅这一处分支判断,就能把调查范围缩小一半。
接下来要做什么
从最底层的名称解析开始。亲手验证为什么 /etc/hosts 与 nsswitch.conf 的检查顺序先于 dig,以及为什么 dig 完全不会读取这两个文件。