1. 先搞清楚一件事:连通性测试到底在测什么
做服务器运维或者日常写代码的人,应该都遇到过这种场景:手里的 Linux 机器突然连不上某个服务了,可能是要拉 GitHub 上的代码仓库,也可能是一个外部 API 突然超时,或者是想确认 HuggingFace 那边的模型下载地址通不通。这时候直接在终端里敲 curl,结果光标一闪,然后就是漫长的等待,最后冒出来一个Could not resolve host或者Connection timed out,你就知道事情没那么简单了。
我第一次碰到这种问题的时候,习惯性先 ping 一下对方域名,能通就觉得网络没问题,不能通就觉得是对方挂了。后来踩了几次坑才明白,这种简单的二分法远远不够。网络连通性其实是一个分层概念,一次完整的访问请求要从你的网卡出发,经过路由、运营商网络、DNS 解析、TCP 握手,最后才是 HTTP 数据交互,任何一个环节出问题,表现都可能不一样。有些时候 ping 不通但网页能开,有些时候 ping 通了但接口一直超时,有些时候域名解析出来的 IP 压根不对。所以,命令行里做连通性测试,本质上是把这条链路拆开来,一层一层验证,看问题到底卡在哪。
这一套方法不只对那几个目标站点有效,放到任何域名、任何服务器上都适用。我后面写到的所有命令和思路,都是在 Ubuntu / CentOS 这类主流 Linux 发行版上跑的,用到的也都是系统自带的工具或者安装起来非常容易的软件包。这篇文章更像是我自己的排查手记,把常用的几板斧整理出来,像 ping、dig、nc、curl、traceroute 这些命令分别解决什么问题,什么场景下用什么参数,都给你捋一遍。
先说结论性的经验:单靠一个命令判断"能不能访问"是不靠谱的。我的习惯是至少做三层验证。第一层,看网络通不通,也就是 ICMP 能不能到;第二层,看域名解析对不对,能不能拿到正确的 IP;第三层,看目标端口上有没有服务在监听,能不能完成 TCP 和 HTTP 握手。三关都过了,才敢说这个地址从我这个 Linux 机器上访问是正常的。这套顺序看起来简单,但真能帮你省掉大量瞎猜的时间。
1.1 为什么要分层看问题
打个比方,你要去一个朋友家拜访,整个过程可以拆成"有没有一条路能走到那片小区"、"那个小区里到底有没有这栋楼"、"这栋楼里朋友在不在家"三个问题。对应的就是路由可达性、DNS 解析、端口服务三个层面。
平时我们说的"打不开网站",可能是路不通,也可能是楼号找错了,还可能是朋友当天不在家。如果不分层排查,你会一直在原地打转。比如有一次我遇到的情况是 curl 报错超时,第一反应是防火墙拦截,结果排查半天,最后发现是 DNS 解析到了一个已经废弃的旧 IP,数据包全发到不知道哪里去了。这种问题用 ping 是看不出来的,因为 ping 走得是域名解析后的地址,你把域名的解析记录换掉之后,ping 工具本身不会告诉你这件事对不对。
分层排查让问题的定位边界变得非常清晰:网络层的问题用 ping/traceroute 看,DNS 层的问题用 dig/nslookup 看,传输层的问题用 nc/telnet 看,应用层的问题再看 curl 的返回码。每一层排查完,能排除一部分嫌疑,没准问题范围就从一个面缩小到一个点。
1.2 一次完整请求经过哪些环节
我简化一下这个过程。你在终端里输入curl https://example.com,大概经历这么几步:先是检查本地缓存有没有这个域名的记录,没有就去问系统配置的 DNS 服务器;得到 IP 之后,系统查路由表找到出口网卡,然后发出 TCP SYN 包跟目标服务器的 443 端口建立连接;连接建立后,再通过 TLS 握手协商加密参数,最后才发送 HTTP GET 请求。每一层都有对应的超时时间和错误信息。
这就是为什么同样的"访问不了",不同环节错误信息完全不同。ping: unknown host说明域名解析就失败了,Destination Host Unreachable说明路由层有问题,No route to host说明对方网络或防火墙直接把你拒了,Connection refused则说明网络层通、但目标端口没有服务在监听。这些信息不用猜,命令行工具全都帮你打出来了,关键是你得知道它们分别代表什么。接下来几节我按照从下往上的顺序,把每个环节的命令和典型输出过一遍。
2. 第一板斧:ping 和 ICMP 能告诉你什么
2.1 ping 不只是"通不通"
ping 是大家最熟悉的网络命令,它发送 ICMP Echo 请求,然后等目标回一个 Echo Reply,通过往返时间来估算网络的响应速度。动手起来很简单:
ping -c 4 example.com这里-c 4表示只发 4 个包就停下来,避免一直刷屏。输出里最重要的几个信息:icmp_seq是包序号,如果有丢包会看到序号跳跃;ttl是数据包经过的跳数上限,常见初始值是 64 或 128,看它也能大概判断目标系统类型;time是往返延迟,稳定在几十毫秒内说明链路状态不错,如果波动很大,那可能中间线路质量有问题。
我自己的习惯是先 ping 10 个包,而不是 4 个。因为 4 个包的样本太少,偶尔丢一个包说明不了什么,10 个包能看出丢包率的趋势。命令是这样的:
ping -c 10 -i 0.2 example.com-i 0.2把发包间隔改成 0.2 秒,10 个包两秒左右就测完了。如果丢包率是 100%,那就是完全不通;丢包率在 20% 以下可能是线路抖动,丢包 50% 以上基本就是中间链路拥堵或者被限速了,业务访问的体感会非常差。
2.2 当 ping 不通的时候,别急着下结论
有个非常常见的误区是:ping 不通,就觉得目标站点一定挂了。实际上很多服务器出于安全考虑,直接在防火墙层丢弃了所有 ICMP 请求,你 ping 不通它,但它的 HTTP 服务完全正常。我见过有人拿着ping 不通的结论报故障,结果人家服务端所有端口都是监听的,业务跑得好好的,纯粹是 ICMP 被禁了。
反过来也一样,即使 ping 通了,也不代表应用层没问题。ICMP 和 TCP 是完全不同的协议,路径上的防火墙、负载均衡设备完全可能放行 ICMP 但限制特定端口。所以说 ping 的结果只能证明网络层到目标主机这一跳是通的,真正的业务能不能访问,还得继续往下测。
另外,ping 的时候如果出现Destination Host Unreachable,通常说明本地路由表里没有到达目标网段的路由,或者下一跳设备通知你说这个主机不在线。这时候就该用ip route看一下默认路由是不是正确:
ip route show我遇到过一台新装的机器,网络配置文件里忘填网关,结果内网 IP 能通、外网一律 unreachable,查了半天才发现是默认路由缺失。这种问题纯靠 ping 目标网站是看不出来的,先看路由表反而一分钟就定位了。
2.3 用 traceroute 看看到底卡在哪一跳
单看 ping 只能知道通或不通,想知道数据包走到哪里断了,得上 traceroute。它利用 ICMP 包的 TTL 逐跳递增机制,让沿途每一台路由器都先给你回一个超时消息,以此画出完整的路径。
traceroute -n -T -p 443 example.com-n表示不做反向域名解析,否则每一跳都要去解析服务器名字,速度会慢很多;-T表示使用 TCP SYN 包探测而不是默认的 UDP 包;-p 443指定目标端口,因为很多中间设备对非标准端口的 UDP 包很不友好,用 TCP 443 探测能更真实地反映实际业务流量走的路径。
输出里每一行就是一个跳点,如果中间某个跳点一直显示* * *,不代表一定断了,因为很多路由器的 ICMP 响应策略就是静默丢弃,只有连续多跳都超时,同时最后一跳也到不了,才能下结论说路径中断。我本地做测试时有个经验:前面几跳延迟正常、到某一跳突然变成几百毫秒或者不再响应,后面全都超时,那问题大概率出在这一跳附近的链路或机房上。
3. 第二板斧:DNS 解析排查,域名到底解析成了什么
3.1 dig 和 nslookup 的基本玩法
连通性测试里最容易忽略、但最容易出问题的就是 DNS 环节。很多时候你访问不了某个域名,不是网络断了,而是压根没解析出对的 IP。排查 DNS 我首选dig,它输出详细、信息完整,比 nslookup 直观得多。
dig example.com输出的重点看ANSWER SECTION里的 A 记录和 CNAME 记录。A 记录就是域名对应的 IPv4 地址,如果你发现它指向一个你没见过的 IP,那基本就能确定解析结果有问题。另外看Query time和SERVER字段,SERVER会告诉你是哪个 DNS 服务器返回的答案,这对判断本地配置有没有生效很有帮助。
有些发行版默认没装 dig,比如精简版 CentOS 容器里经常找不到。可以用 nslookup 替代,或者直接装一下,Ubuntu/Debian 上安装 bind9-dnsutils,CentOS/RHEL 上安装 bind-utils:
# Ubuntu / Debian sudo apt install dnsutils # CentOS / Rocky / Alma sudo yum install bind-utilsnslookup 也能应付基本的查询需求:
nslookup example.com但如果你要追查 CNAME 链、查指定记录类型,dig 会更顺手。比如我想看一个域名是不是走了 CDN,用dig example.com CNAME一眼就能看出来。常见记录的查询方式都差不多:dig example.com A、dig example.com AAAA、dig example.com MX,把类型换成你想查的就行。
3.2 解析结果异常怎么处理
DNS 解析失败经常有几种表现。第一种是SERVFAIL,说明上游权威服务器暂时异常;第二种是NXDOMAIN,域名本身不存在,检查是不是拼错了;第三种是查询结果正常,但 IP 不是你预期的那个,这种情况多半是公共 DNS 的缓存和权威服务器不一致,或者本机配置了 hosts 覆盖。
本机 hosts 覆盖是个隐蔽的坑。我装修过一台机器,怎么查解析都觉得不对,后来想起来/etc/hosts里写了一条指向内网地址的记录,把所有请求都带到测试环境去了。排查的时候记得看一眼这个文件:
cat /etc/hosts另外也要确认系统用的 DNS 服务器是谁,配置文件是/etc/resolv.conf:
cat /etc/resolv.conf里面nameserver字段就是系统实际查询的 DNS 服务器地址。网络管理工具(比如 NetworkManager)可能在网络重连后重写这个文件,所以如果发现配置被改,别直接硬改,得去 NetworkManager 或 netplan 的配置源里改。
3.3 顺便验证一下 IPv4 和 IPv6 的差别
现在很多域名同时有 A 和 AAAA 记录,有些环境里 IPv6 路由是残缺的,导致 curl 默认先尝试 IPv6,卡很久超时才回退到 IPv4。这种问题光看 A 记录查不出来,你得确认本地有没有可用 IPv6 地址:
ip -6 addr show如果这台机器根本没有 IPv6 地址,但某些工具解析到了 AAAA 记录并且在尝试连接,那就是浪费时间。遇到这种情况,可以在 curl 里加-4参数强制走 IPv4,或者检查系统是否开启了 IPv6 解析偏好。DNS 这个环节不深入,我就提一句:排查连通性时,别把 AAAA 给忘在脑后了。
4. 第三板斧:TCP 端口探测,服务真在听吗
4.1 nc 与 /dev/tcp 两种姿势
网络层通了、DNS 也没问题,下一步就是验证目标服务器的指定端口是否开放。DNS 让你找到了楼栋,TCP 探测才能确认你要找的那一层有没有人。这里我推荐用nc,也就是 netcat。
nc -zv -w 5 example.com 443各参数含义:-z表示只探测端口、不发送实际数据,-v输出详细信息,-w 5是超时时间,5 秒后还没连上就放弃。命令执行完后,如果端口开放,会看到类似Connection to example.com 443 port [tcp/https] succeeded!的输出;如果拒绝,会看到Connection refused;如果超时,会卡 5 秒然后报Connection timed out。
有些精简系统连 nc 都没装,但没关系,Linux 的 bash 自带一个黑科技/dev/tcp,可以临时顶上:
timeout 5 bash -c 'echo > /dev/tcp/example.com/443' && echo "port open" || echo "port closed"原理很简单:bash 把/dev/tcp/主机名/端口这个伪设备当作一个 TCP 连接,能写进去就表示连接建立成功。配合timeout命令防止无限期阻塞,这也是我在最小化容器镜像里最常用的探测方式,不用额外装任何东西。
4.2 端口通但业务不通,问题出在哪
测试端口时有个让我印象很深的案例:一台服务器在海外数据中心,业务方说访问他的接口偶尔超时。我 ping 延迟很低,TCP 443 端口也是通的,但 curl 一拉就卡。后来再用 nc 反复多测几次,发现连接有时候 2 秒就成功、有时候 20 秒才成功,说明链路存在严重的网络抖动,或者中间设备的会话处理不稳定。
这就引出一个经验:端口探测不能只测一次。至少要连续测 5 到 10 次,观察成功率和耗时波动。用一条小循环就能完成:
for i in $(seq 1 10); do nc -zv -w 3 example.com 443 2>&1 | tail -n 1; done如果失败次数高,你的业务在网络层面就有隐患,这时候再怎么调应用层参数都白搭。另外,telnet也可以用来探测端口,telnet example.com 443连上之后会停留在空界面,看到Connected to开头就说明通了,Ctrl + ] 再输入 quit 退出交互模式。不过 telnet 客户端不是默认必装的,nc 足够用了。
4.3 本地先确认:是不是服务没监听
排查远程之前,先默认怀疑本地。很多"访问不了"其实是本地服务根本没起来,或者端口监听在 127.0.0.1 而不是 0.0.0.0。用ss能快速看监听状态:
ss -lntp | grep 443-l只看监听中的 socket,-n不做服务名解析,-t只看 TCP,-p显示对应的进程名。输出里如果看到Local Address:Port是127.0.0.1:443,说明服务只在回环地址上监听,外部机器必然连不上,得让服务监听在0.0.0.0:443才能对外提供服务。这类问题我在自测脚本的时候经常遇到,写完服务以为自己监听了,一查发现绑错网卡。
5. 第四板斧:HTTP 层面的真实握手体验
5.1 curl -I 和 -w 输出
前面几层都验证过,最后回到用户真正关心的 HTTP 服务上。curl 是这一层的首选工具,它支持 HTTP/HTTPS、跟随重定向、自定义超时、输出详细的耗时统计,几乎能满足所有应用层连通性诊断需求。
最基础的命令:
curl -I --connect-timeout 5 --max-time 10 https://example.com-I表示只发送 HEAD 请求,服务器返回响应头,不会下载整个页面。这个选项对你的目标是"测试是否访问正常"来说非常合适,不需要把主页正文都拉回来。如果服务正常,你会看到HTTP/2 200或者HTTP/1.1 200 OK这样的状态行。
如果想把耗时数据打出来,配合-w写模板:
curl -o /dev/null -s -w "DNS解析: %{time_namelookup}s\nTCP建连: %{time_connect}s\nTLS握手: %{time_appconnect}s\n总耗时: %{time_total}s\nHTTP状态: %{http_code}\n" https://example.com-o /dev/null把响应体丢弃,-s静默模式不显示进度条,-w输出的各项指标能精确到秒。这是我最依赖的连通性测试命令,一条命令下来,DNS 解析耗时、TCP 连接耗时、TLS 握手耗时、总耗时和状态码全都有了,哪个环节慢一眼就能看出来。比如time_namelookup非常长,说明 DNS 那边有问题;time_connect长但time_namelookup短,说明网络传输路径或者目标端口层面有延迟;time_appconnect长,说明目标机器的 HTTPS 服务和证书校验拖了后腿。
5.2 超时和重试参数建议
命令行测试里最容易犯的错就是不给请求设置超时。默认情况下 curl 可以一直等下去,如果目标主机路由黑洞化,这个命令会在终端里挂半天。我建议所有测试命令都带上--connect-timeout和--max-time,前者控制 TCP 连接阶段的超时,后者控制整个请求的硬性超时,一般分别设成 5 秒和 10 秒就够用。
重试也是连通性测试里很实用的能力。比如你怀疑网络偶发抖动,想验证是不是稳定可达,可以这样写:
curl -s -o /dev/null --connect-timeout 5 --max-time 10 --retry 3 --retry-delay 1 --retry-all-errors -w "%{http_code}\n" https://example.com--retry指定重试次数,--retry-delay指定两次重试的间隔,--retry-all-errors表示只要是错误状态都允许重试,不加这个参数的话,默认只有极少部分错误会触发重试逻辑。多跑几次能看到一个稳定结果,就不会被一次偶发故障误导判断。
5.3 返回码和证书告警的坑
curl 的退出码和 HTTP 状态码是两回事,这个很多人一开始容易混淆。HTTP 状态码是服务器返回的,比如 200、301、404;curl 的退出码是命令本身的执行结果,比如 0 表示成功,6 表示无法解析主机,7 表示连接失败,28 表示超时。写脚本判断连通性时,两个都得看:
curl -s -o /dev/null -w "%{http_code}" https://example.com echo ""如果追求严谨,用&&和||组合判断退出码:
curl -s -o /dev/null --connect-timeout 5 https://example.com && echo "请求完成" || echo "curl执行出错,退出码 $?"HTTPS 测试时还会遇到证书告警问题。测试环境或者自签名证书的站点,curl 默认会直接报SSL certificate problem并拒绝连接。这种情况我通常分两步走:先验证证书链是否正常,再访问。如果只是临时验证连通性,可以加-k跳过证书校验,但千万别在正式脚本里这么干,那等于把 HTTPS 的全部意义都扔了。先说明这只是本次测试的行为,永远不要因为测试方便而放松发送真实业务请求的安全标准。
6. 常见问题与排查技巧实录
6.1 一张速查表解决大部分问题
把前面几层排查方法汇总成一张表,我在工单里排查"某某站点访问不了"这种问题的时候,基本就按这个顺序走,十分钟内能定位到具体层级。
| 现象 | 优先排查命令 | 可能原因 | 下一步处理 |
|---|---|---|---|
| ping 报 unknown host | dig/nslookup 查域名 | DNS 解析失败 | 检查 resolv.conf、hosts 文件、上游 DNS 状态 |
| ping 报 Destination Host Unreachable | ip route show | 本地缺少路由或下一跳不可达 | 检查默认网关和网卡配置 |
| ping 长时间 100% 丢包 | traceroute 分段定位 | 中间链路中断或被丢弃 | 联系网络管理员检查中间路径 |
| DNS 能解析但 curl 卡住 | nc -zv -w 5 目标IP 端口 | 网络路径不通或目标端口被限 | 检查防火墙规则,确认服务端监听地址 |
| nc 端口通但 curl 超时 | curl -w 输出耗时详情 | TLS 握手慢或应用层异常 | 看 time_appconnect 定位 TLS 段,检查服务端负载 |
| curl 报 Connection refused | ss -lntp 检查服务端 | 目标端口没有服务监听 | 启动服务或检查服务绑定地址 |
| curl 报 SSL certificate problem | openssl s_client 查证书 | 证书过期或不匹配 | 更新证书或临时用 -k 验证连通性 |
这张表不是万能的,但它能把绝大多数"访问不了"的问题框在一个范围内。我自己写自动化检查脚本时,就是按这个思路写成一系列函数的,先在命令行手动跑通,再固化到脚本里做定时拨测。
6.2 我踩过的几个坑
第一个坑是用 ping 的丢包率直接推断业务质量。有一次我测一个海外节点的延迟,ping 只有 10% 的丢包,觉得还行,但实际跑业务时发现请求成功率只有一半。后来才明白,ICMP 和 TCP 在中间网络设备上的待遇完全不同,运营商可能对 ICMP 限速但对 TCP 流量走另一条路径。所以只要涉及业务访问,最终结论必须用 curl 或 nc 这类真实协议去验证。
第二个坑是 DNS 缓存造成的假故障。在排查某次域名切换问题时,我改完 DNS 记录,测试还是解析到旧 IP。折腾半天才发现本地 systemd-resolved 缓存了旧的解析结果。用下面这个命令清一下就好了:
sudo resolvectl flush-caches另外一些老系统用的是 nscd 缓存,重启 nscd 服务也能达到同样效果。以后再看到"解析不对",先想缓存,别急着赖 DNS 服务器。
第三个坑是 IPv6 优先策略导致 curl 超时。一个看起来特别奇怪的现象:浏览器打开正常,curl 却卡了很久才报错,后来发现是本机通过 IPv6 去连一个实际上 IPv6 路由不通的站点,系统在等待 IPv6 超时后才回退到 IPv4。curl 的-4参数能快速验证这个判断,确认后可以把系统的 IPv6 优先级调低,或者在测试命令里固定使用 IPv4。
6.3 把测试方法固化成脚本
手动测试了一轮之后,建议把常用的检查项整理成一个 shell 脚本,下次遇到类似问题直接一把梭。我自己的脚本写得比较简单灵活,核心就是把 DNS 解析、TCP 端口、HTTP 状态码三层检查串起来:
#!/bin/bash TARGET="${1:-example.com}" PORT="${2:-443}" echo "== DNS 解析检查 ==" dig +short "$TARGET" A echo "== TCP 端口检查 ==" nc -zv -w 5 "$TARGET" "$PORT" 2>&1 echo "== HTTP 请求检查 ==" curl -s -o /dev/null --connect-timeout 5 --max-time 10 \ -w "HTTP状态码: %{http_code}\nDNS耗时: %{time_namelookup}s\n连接耗时: %{time_connect}s\n总耗时: %{time_total}s\n" \ "https://$TARGET"脚本接受一个域名作为参数,默认测 443 端口。日常巡检的时候,我可以同时跑好几个目标,把输出重定向到文件里对比不同时间点的数据,判断是持续故障还是间歇性抖动。这里有个细节,脚本里的nc在不同发行版上参数有细微差异,比如 OpenBSD 版 netcat 和老式 GNU netcat 略有不同,如果报参数错误,用nc -h看一眼帮助就行,不需要死记。
我个人在实际操作中的体会是,排查连通性问题最怕的不是故障本身,而是没有章法地乱试。你一会儿换个工具、一会儿换个目标,最后试了一堆操作,还是说不清问题出在哪一层。按照 DNS、网络、端口、HTTP 这个从下往上的顺序走一遍,每一步的输出都是下一步的依据,哪怕最后没找到根因,给其他人交接的时候也能明确说出已经排查过哪些环节、剩下可疑的是哪一层,协作效率会高很多。
最后再分享一个小技巧:命令行测试时,尽量保留原始输出,不要光看"成功""失败"这两个结论。像 ping 的延迟分布、curl 的各阶段耗时、dig 的查询服务器地址,这些细节才是定位问题的真正线索。以后再有人问你"这个地址能不能访问",你的回答就不只是"能"或"不能",而是"能访问,但 DNS 解析花了 2 秒,TCP 连接很快,TLS 握手偏慢,可能是目标服务器处理器忙"——这种信息量,才是一次靠谱的连通性测试应该给出的交代。