服务器网络不通问题的排查方法

描述

 

“网络不通”不是一个根因,而是一个未经分层的现象:可能是网卡没有地址、路由选错、ARP/邻居解析失败、DNS 返回了错误地址、防火墙丢包、目标端口未监听、TLS 握手失败、HTTP 代理异常,也可能只是对端禁止 ICMP。pingtelnetcurltraceroute 的价值不在于四条命令都跑一遍,而在于它们分别验证不同层次,并把故障边界逐步缩小。

本文以使用 systemd、iproute2 和常见 GNU/Linux 用户空间工具的服务器为例。所有尖括号内容都是占位符:<目标域名><目标IP><目标端口><网卡名><网关IP><服务名><命名空间> 分别替换为真实域名、解析后的地址、业务端口、出口接口、默认网关、systemd 单元和 Kubernetes namespace;<源IP><本机业务IP><客户端IP> 替换为对应角色的真实地址;<容器名><已批准的调试镜像> 替换为集群中的真实对象;<目标网段CIDR> 和 <新网关IP> 替换为变更审批中的网段与下一跳。命令语法说明中的 <主机><域名><端口> 也应替换为对应目标。IPv6 地址作为 URL 主机时必须用方括号包围,例如 https://[2001:10]:8443/

任何根因结论都要有至少一条证据链。例如:“域名解析到预期 IP;ip route get 选择预期网卡;到网关的邻居解析正常;TCP SYN 重传且本机 nftables 计数器不增;同网段另一主机也失败”,这些证据才能支持把问题继续交给网络路径或对端。只凭 ping 失败就说“防火墙拦了”,不够。

先定义故障:从哪里、到哪里、用什么协议

排查前固定五个要素:源主机或 Pod、目标域名/IP、目标端口、传输协议、故障时间。再确认是所有来源都失败,还是单机失败;是所有端口失败,还是某个服务失败;是持续故障,还是偶发超时。没有这些边界,后续输出无法对照。

先记录时间、主机身份、内核和网络命名空间。容器里的 localhost、路由表和防火墙可能与宿主机完全不同。


			   bashdate --iso-8601=seconds hostnamectl uname -r systemd-detect-virt --container || true readlink /proc/self/ns/net 

如果问题来自容器,应在发生故障的同一个网络命名空间执行测试。宿主机 curl 成功,只能说明宿主机路径正常,不能证明 Pod 的 DNS、NetworkPolicy、服务网格或源地址转换正常。

四件套分别证明什么

工具 主要验证对象 成功能证明 失败不能直接证明
ping ICMP Echo、往返时延、基本可达性 对端或中间设备回复 ICMP,双向路径在测试时可用 TCP 端口一定关闭;对端宕机;DNS 一定异常
telnet TCP 建连,及可选的明文交互 到指定 IP:端口的三次握手成功 TLS/HTTP 业务一定正常;UDP 正常
curl DNS、TCP、TLS、HTTP/应用响应 从 URL 到应用层的链路在指定条件下完成 ping 必须成功;所有客户端都正常
traceroute 逐跳 TTL/Hop Limit 反馈 部分路径节点愿意返回超时报文 * 所在路由器一定丢业务包;显示路径必然对称

推荐顺序不是机械的。如果现象是“HTTPS 返回 502”,直接从 curl -v、服务日志和反向代理开始;如果目标 IP 都不知道,先查 DNS;如果本机没有默认路由,没必要先 traceroute 公网。

第零步:排除本机基础配置

接口、地址与链路状态

ip -br 适合快速浏览,ip -s link 给出错误和丢弃计数。虚拟机显示链路 UP 不代表云安全组正确,但链路 DOWN、没有地址或错误计数持续增长已经足以缩小范围。


			   baship -br link ip -br address ip -s link show dev <网卡名> ethtool <网卡名> 2>/dev/null | grep -E 'Speed:|Duplex:|Link detected:' || true 

物理机 Link detected: no 要检查网线、交换机端口、bond 成员和驱动;虚拟网卡可能不支持 ethtool 的全部字段。接口 RX dropped 增长可能发生在驱动、队列或协议栈,不应直接等同于外部链路丢包。

让内核告诉你实际走哪条路

不要只看默认路由。策略路由、多个路由表、源地址选择和 VRF 都可能改变实际路径。ip route get 会按当前规则模拟一次路由查找。


			   baship rule show ip route show table main ip route get <目标IP> ip route get <目标IP> from <源IP> 

最后一条中的 <源IP> 替换为业务进程实际绑定或 SNAT 前的源地址。如果输出接口、下一跳或 src 与预期不同,先修路由/策略,不要靠 ping 的结果猜测。修改路由属于高风险操作,错误会中断远程连接;必须先保留现有配置、确认带外通道,并优先用临时路由灰度验证。

同网段先看邻居解析

IPv4 的 ARP 和 IPv6 的 NDP 都通过邻居表呈现。到同网段目标或下一跳时,FAILED、长时间 INCOMPLETE 表明二层解析没有完成。


			   baship neigh show dev <网卡名> ping -c 3 -W 1 <网关IP> ip neigh show to <网关IP> dev <网卡名> 

网关可达而远端不可达,说明本机二层和至少到网关的路径正常;网关也不响应时仍需注意它可能禁 ICMP,可结合邻居状态和抓包判断。不要随意执行 ip neigh flush all,这会影响整机现有连接的邻居解析。若确需清理单条异常缓存,先确认目标和影响,只处理明确条目并在操作后验证。

DNS 必须拆成“解析是否成功”和“解析结果是否正确”

ping <域名> 失败可能发生在 DNS 之前。先查看系统解析配置,再用系统解析器和指定 DNS 服务器对照。


			   bashcat /etc/resolv.conf getent ahosts <目标域名> resolvectl query <目标域名> 2>/dev/null || true dig +time=2 +tries=1 <目标域名> A dig +time=2 +tries=1 <目标域名> AAAA 

getent 走 Name Service Switch,更接近应用使用的系统解析行为;dig 默认直接查询 /etc/resolv.conf 指向的 DNS,不一定经过 nsswitch 中的 mDNS、LDAP 或本地缓存。使用 systemd-resolved 时,/etc/resolv.conf 可能指向本地 stub,resolvectl 可显示按接口分配的 DNS 和搜索域。

若怀疑某台 DNS 服务器,显式指定它:


			   bashdig @ <目标域名> A +noall +answer +comments dig @ <目标域名> AAAA +noall +answer +comments dig +trace <目标域名> 

dig +trace 从根开始迭代查询,可能被企业出口策略拦截,也不代表内部 split-horizon 解析路径;内网域名不要用它作唯一证据。DNS 成功的判断包括返回码、答案、TTL 和地址是否属于当前环境,而不是“有 IP 就算正常”。

ping:验证 ICMP 可达性与质量

域名和 IP 分开测试

先 ping IP,排除 DNS;再 ping 域名,观察它实际选择的地址族和地址。


			   bashping -c 4 -W 2 <目标IP> ping -4 -c 4 -W 2 <目标域名> ping -6 -c 4 -W 2 <目标域名> 

在 iputils 版本中,-c 是发送次数,-W 是每个应答等待时间,通常以秒计;BusyBox 的选项语义可能不同,先运行 ping --help。分别强制 IPv4/IPv6 能发现“AAAA 可解析但 IPv6 出口不存在”这类双栈问题。

以下是示例输出,只展示判断方式,并非真实执行结果:


			   text64 bytes from 192.0.2.20: icmp_seq=1 ttl=56 time=12.4 ms 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 11.9/12.3/12.8/0.3 ms 

成功说明目标对 ICMP Echo 作出响应,并且回程可达。TTL 可辅助发现路径变化,但不同操作系统初始 TTL 不同,不能仅凭接收 TTL 精确推断跳数或系统类型。短样本的 0% 丢包也不能证明高峰无抖动。

固定源接口或源地址

多网卡主机可能从错误出口发包。使用 -I 明确接口或源地址,与应用实际绑定方式对照。


			   bashping -I <网卡名> -c 4 -W 2 <目标IP> ping -I <源IP> -c 4 -W 2 <目标IP> ip route get <目标IP> from <源IP> 

接口测试失败、默认测试成功,常见于该接口无可用路由、策略路由缺失、回程路由不对或源地址被上游过滤。结论需要路由查询和抓包支持。

排查 MTU/PMTU 黑洞

小包通、大包超时,尤其是 TLS 握手或传输大响应卡住时,应检查路径 MTU。iputils 的 -M do 设置 IPv4 DF 并禁止本地分片,-s 是 ICMP 数据负载长度。以太网 MTU 1500 的 IPv4 ICMP 常用 1472 字节负载作为起点,因为还需 20 字节 IPv4 头和 8 字节 ICMP 头;IPv6 头长度不同。


			   bashping -4 -M do -s 1472 -c 3 -W 2 <目标IP> ping -4 -M do -s 1400 -c 3 -W 2 <目标IP> tracepath <目标IP> ip link show dev <网卡名> 

若 1400 成功而 1472 失败,说明需要继续二分负载大小并调查隧道、VPN、云封装或 ICMP Fragmentation Needed 是否被过滤。不要立即把所有主机 MTU 改成 1400;修改 MTU 会影响整个接口上的连接。执行前必须确认路径范围、冗余和回滚值,先在灰度接口验证,操作后复测大包与业务 TLS。

ping 失败的正确解释

常见结果应这样处理:

  • Name or service not known:先查 DNS/NSS,不是 ICMP 故障。
  • Network is unreachable:本机没有匹配路由,先查地址、路由和策略。
  • Destination Host Unreachable:看消息由本机、网关还是中间路由器发出;本机发出时常伴随邻居失败。
  • 全部超时:可能被静默丢弃、对端禁 ICMP、回程缺失或目标不可用,不能单凭超时定责。
  • 高丢包/时延:用更长但受控的样本,并结合 mtr、接口计数和业务时段;ICMP 可能被限速,结果不一定代表 TCP 同等丢包。

telnet:只验证 TCP 建连,不等于业务健康

虽然 nc 更适合自动化,telnet <主机> <端口> 仍是常见现场工具。它发起 TCP 连接,适合快速区分 timeout、refused 和 connected。不要用它测试 UDP,也不要在明文会话里输入密码、Token 或生产敏感数据。


			   bashtelnet <目标IP> <目标端口> 

常见现象:

  • 出现 Connected to ...:TCP 三次握手已完成。按 Ctrl+] 进入 telnet 命令模式,再输入 quit 退出。
  • 立即 Connection refused:通常收到 TCP RST,说明路径基本可达,但该地址端口未监听或防火墙主动拒绝。
  • 长时间超时:SYN 或 SYN-ACK 被丢、回程错误、对端无响应,需抓包定位。
  • No route to host:可能是本机路由错误,也可能是防火墙返回了 ICMP prohibited,需结合 ip route get 与抓包。

自动化检查更推荐 netcat。不同实现(OpenBSD netcat、Nmap ncat、BusyBox nc)的选项存在差异,先执行 nc -h


			   bashnc -vz -w 3 <目标IP> <目标端口> timeout 5 bash -c '/<目标端口>' 

第二条依赖 Bash 的 /dev/tcp 特性,不适用于 POSIX sh,也不给出完整诊断。两者都只验证 TCP,不验证 TLS 证书、HTTP 状态或响应内容。

在服务端确认是否监听

如果有目标服务器权限,先看端口、绑定地址和进程。服务只绑定 127.0.0.1 时,本机访问成功、远端必然失败;只绑定 IPv6 是否同时接收 IPv4,取决于 net.ipv6.bindv6only 和应用 socket 选项。


			   bashss -lntp 'sport = :<目标端口>' systemctl status <服务名> --no-pager systemctl show <服务名> -p MainPID -p ActiveState -p SubState journalctl -u <服务名> --since '-30 minutes' -p warning --no-pager 

ss -p 查看其他用户进程通常需要 root。没有监听时,应先查服务启动失败、配置端口和进程日志;不要通过开放防火墙解决“进程根本没监听”。

本地回环、主机地址分别测试,可以区分应用绑定与外部路径:


			   bashcurl --noproxy '*' --connect-timeout 3 http://127.0.0.1:<目标端口>/ curl --noproxy '*' --connect-timeout 3 http://<本机业务IP>:<目标端口>/ nc -vz -w 3 <本机业务IP> <目标端口> 

如果服务不是 HTTP,不应用 curl 强测;使用协议对应的健康检查。--noproxy '*' 防止环境代理把“本机测试”送到代理服务器。

用抓包区分 timeout 的位置

抓包涉及业务元数据,可能包含源/目的地址、域名和未加密内容;应遵守权限与留存要求,限制接口、主机、端口、包数和时长。不要无过滤地长期抓取生产流量。


			   bashsudo timeout 20 tcpdump -ni <网卡名>    -c 100 'host <目标IP> and tcp port <目标端口>'    -w /var/tmp/connectivity-check.pcap sudo tcpdump -nn -tttt -r /var/tmp/connectivity-check.pcap 

判断依据:反复看到客户端 SYN、没有 SYN-ACK,说明请求已离开抓包点但响应未返回;立即收到 RST 表示端口拒绝;握手完成后客户端发出请求但服务端不回应用数据,重点转向服务进程、TLS 或上游。只在客户端抓包不能精确区分中间哪一跳丢包,必要时在服务端同时抓取并对齐时间。

curl:把 DNS、TCP、TLS、HTTP 分段计时

curl -v 会显示解析、连接、TLS 和请求响应过程,但可能把请求头中的 Authorization、Cookie 等敏感信息写入终端或日志。生产排查优先访问无敏感凭据的健康端点,保存输出前脱敏。


			   bashcurl --noproxy '*' -v    --connect-timeout 3    --max-time 10    https://<目标域名>:<目标端口>/health 

--connect-timeout 限制连接阶段,HTTPS 下通常涵盖 DNS 后的 TCP 与 TLS 建立所用时间;--max-time 限制整个传输。--noproxy '*' 强制直连,适合排除 HTTP_PROXYHTTPS_PROXY 和 NO_PROXY 影响。若业务本来必须经过代理,则应分别测试直连与代理,不要把直连结果当作最终路径。

输出可比较的阶段耗时

curl -w 能把排查从“感觉很慢”变成各阶段时长。remote_ip 还能确认实际连接了哪个解析地址。


			   bashcurl --noproxy '*' -sS -o /dev/null    --connect-timeout 3 --max-time 15    -w 'remote_ip=%{remote_ip} http_code=%{http_code} namelookup=%{time_namelookup} connect=%{time_connect} appconnect=%{time_appconnect} starttransfer=%{time_starttransfer} total=%{time_total} '    https://<目标域名>:<目标端口>/health 

判断时看差值:time_connect - time_namelookup 近似 TCP 建连阶段;time_appconnect - time_connect 近似 TLS 阶段;time_starttransfer - time_appconnect 主要包含请求发送、服务处理和首字节等待。对 HTTP 明文请求,time_appconnect 通常为 0。一次采样不代表趋势,应在受控频率下多次测试并保留时间戳。

下面脚本连续采样并输出 CSV。它不会忽略错误;失败行记录 curl 退出码,避免只统计成功请求产生幸存者偏差。


			   bash#!/usr/bin/env bash set -uo pipefail URL="https://<目标域名>:<目标端口>/health" COUNT=10 INTERVAL=2 printf 'timestamp,exit_code,remote_ip,http_code,dns,tcp,tls,ttfb,total ' for ((i=1; i<=COUNT; i++)); do   stamp="$(date --iso-8601=seconds)"   metrics="$(curl --noproxy '*' -sS -o /dev/null      --connect-timeout 3 --max-time 10      -w '%{remote_ip},%{http_code},%{time_namelookup},%{time_connect},%{time_appconnect},%{time_starttransfer},%{time_total}'      "${URL}")"   rc=$?   printf '%s,%d,%s ' "${stamp}" "${rc}" "${metrics}"   sleep "${INTERVAL}" done 

脚本没有 set -e,是为了在单次失败后继续采样;保留 set -u 和 pipefail。curl 失败时仍可能输出部分指标,因此必须结合 exit_code 解读。

绕过 DNS但保留 Host 和 SNI

直接请求 https://<目标IP>/ 会改变 TLS SNI 和 HTTP Host,虚拟主机可能返回错误证书或默认站点。--resolve 才能固定连接 IP,同时保留域名语义。


			   bashcurl --noproxy '*' -v    --resolve '<目标域名>:<目标端口>:<目标IP>'    --connect-timeout 3 --max-time 10    https://<目标域名>:<目标端口>/health 

若普通请求失败而 --resolve 成功,证据指向 DNS 结果、DNS 缓存或地址族选择;若两者都在 TLS 后返回相同错误,继续查服务端。--resolve 只影响本次 curl,不修改系统 hosts,适合安全验证。

分离 IPv4 与 IPv6

双栈域名可能只坏一个地址族。分别强制测试,并记录实际远端地址:


			   bashcurl -4 --noproxy '*' -sS -o /dev/null    -w 'ip=%{remote_ip} code=%{http_code} total=%{time_total} '    --connect-timeout 3 --max-time 10 https://<目标域名>/ curl -6 --noproxy '*' -sS -o /dev/null    -w 'ip=%{remote_ip} code=%{http_code} total=%{time_total} '    --connect-timeout 3 --max-time 10 https://<目标域名>/ 

只有 IPv6 失败时,检查 IPv6 地址、默认路由、邻居发现、防火墙和上游回程;不要轻率删除 AAAA 记录或全局关闭 IPv6,那会影响其他业务。临时以 -4 验证可以止于客户端层,不是基础设施修复。

TLS 失败要看证书链、名称与时间

curl 报 certificate verify failed 不等于 TCP 不通。可能是证书过期、主机时间错误、中间证书缺失、SNI 不匹配或本机 CA 包异常。不要用 curl -k 作为修复;它会跳过证书验证,只能在明确风险的短时诊断中对照,且不得携带敏感凭据。


			   bashdate -u timedatectl status openssl s_client    -connect <目标域名>:<目标端口>    -servername <目标域名>    -verify_return_error  

openssl s_client 输出可能很长。检查证书主题备用名称、有效期、issuer、链验证返回码和服务端是否发全中间证书。根因必须由证书内容与验证错误支持,而不是把所有 TLS 错误归为“证书过期”。

只提取关键证书字段可用:


			   bashopenssl s_client -connect <目标域名>:<目标端口>    -servername <目标域名> -showcerts /dev/null |  openssl x509 -noout -subject -issuer -dates -ext subjectAltName 

管道只解析服务端返回的第一张证书;完整链仍应结合 s_client 验证输出。若命令因 OpenSSL 版本不支持 -ext,先查 openssl x509 -help,再分别用 -text 查看。

HTTP 状态不是网络层失败

200 通常说明应用端到端成功,但 301/302 可能跳到错误域名,401/403 表示已到应用或网关鉴权层,404 可能是 Host/path 不匹配,502/503/504 多与反向代理上游或容量相关。用响应头和有限正文定位,避免下载大文件。


			   bashcurl --noproxy '*' -sS -D /var/tmp/response.headers    -o /var/tmp/response.body    --range 0-4095    --connect-timeout 3 --max-time 10    https://<目标域名>:<目标端口>/health sed -n '1,40p' /var/tmp/response.headers wc -c /var/tmp/response.body 

--range 只有服务端支持 Range 时才限制响应;诊断端点最好本身返回小响应。响应文件可能含敏感信息,检查权限并按组织要求清理。不要把 503 误报为“端口不通”,此时 TCP/TLS 很可能都已成功。

代理变量经常造成“只有这个用户不通”

systemd 服务、交互 shell、容器可能使用不同代理设置。只读检查变量和值的来源:


			   bashenv | grep -iE '^(http|https|all|no)_proxy=' systemctl show <服务名> -p Environment curl -v --connect-timeout 3 --max-time 10 https://<目标域名>/ 2>&1 |    grep -E 'Uses proxy env variable|Connected to|Trying ' 

代理 URL 可能包含凭据,展示和提交工单前必须脱敏。比较 curl --noproxy '*' 与正常 curl:只有代理路径失败时,查代理 DNS、ACL、认证和 NO_PROXY 匹配;不要全局删除代理变量影响包管理器和其他服务。

traceroute:定位路径边界,不把星号当作断点

经典 traceroute 发送逐渐增大 TTL 的探测包,中间路由器若返回 ICMP Time Exceeded,就能显示该跳。默认探测方式因实现不同,Linux 常见版本默认 UDP;网络可能只允许 ICMP 或 TCP,因此应按业务协议对照。


			   bashtraceroute -n -w 2 -q 1 <目标IP> traceroute -I -n -w 2 -q 1 <目标IP> traceroute -T -p <目标端口> -n -w 2 -q 1 <目标IP> 

-n 禁止反向 DNS,避免把 DNS 慢混入路径探测;-w 是等待,-q 是每跳探测次数;-I 使用 ICMP,-T -p 使用指定目的端口的 TCP SYN。不同 traceroute 实现选项有差异,先运行 traceroute --help。TCP 模式通常需要足够权限。

看到某一跳 * 但后续跳继续出现,说明该路由器不回复或限速,不是业务包在那里断了。最后一跳不显示也可能因为目标不回应探测,而 curl 仍成功。路径还可能是非对称、ECMP 或 MPLS,单次 traceroute 只是一条观测。

连续观测更适合 mtr,报告模式便于留证:


			   bashmtr --report --report-cycles 20 --no-dns <目标IP> mtr --tcp --port <目标端口> --report --report-cycles 20 --no-dns <目标IP> 

中间一跳显示高丢包而后续跳正常,通常是该节点对探测报文限速,不代表转发同样丢包。只有从某跳开始后续所有跳都出现相似丢包,并且端到端业务也受影响,才构成较强证据;还应从反向或其他源点测试。

tracepath 无需特殊权限,且能提示路径 MTU,适合补充:


			   bashtracepath -n <目标IP> tracepath6 -n  2>/dev/null || tracepath -6 -n  

工具名称随发行版不同;有的系统只有支持 -6 的 tracepath。以本机 --help 为准。

防火墙:先看规则和计数,再决定是否改

现代发行版可能使用 nftables,也可能通过 iptables 前端管理 nftables。先确定实际规则后端,不要同时修改两套规则造成漂移。


			   bashnft list ruleset iptables --version iptables-save firewall-cmd --state 2>/dev/null || true ufw status verbose 2>/dev/null || true 

规则输出可能包含内部地址、端口和安全策略,提交外部工单前脱敏。排查重点是输入/输出方向、接口、源/目的地址、状态匹配、默认策略和规则计数器。

用 nftables monitor trace 可以观察包命中路径,但开启 trace 标记本身要修改规则;生产上应限制到单个源/目标/端口,短时执行,并在结束后删除所加诊断规则。若组织已有防火墙控制面,应通过其变更流程,不要手工改。

以下是诊断流程示例,属于高风险配置修改:执行前保存 nft list ruleset,确认远程管理通道不会被匹配,并在控制台准备回滚。meta nftrace set 1 会让匹配包进入 trace。


			   bash#!/usr/bin/env bash set -euo pipefail TARGET_IP="<目标IP>" TARGET_PORT="<目标端口>" BACKUP="/var/tmp/nft-before-$(date +%Y%m%d-%H%M%S).nft" nft list ruleset >"${BACKUP}" nft add table inet debug_trace nft 'add chain inet debug_trace output { type filter hook output priority -301; policy accept; }' nft add rule inet debug_trace output ip daddr "${TARGET_IP}"    tcp dport "${TARGET_PORT}" meta nftrace set 1 echo "Run in another terminal: nft monitor trace" echo "Cleanup after capture: nft delete table inet debug_trace" echo "Backup: ${BACKUP}" 

这段脚本仅覆盖 IPv4 TCP。诊断结束必须执行明确的清理并验证业务;删除的是临时 debug_trace 表,不是生产规则表:


			   bashnft list table inet debug_trace nft delete table inet debug_trace nft list ruleset >/var/tmp/nft-after.txt 

若脚本在加表后中途失败,仍需手工清理。不要直接用备份文件 nft -f 覆盖当前规则集,除非已确认语义和原子替换方式;错误恢复可能锁死远程连接。优先只删除本次明确创建的临时表。

服务端回程、策略路由与 rp_filter

TCP 请求能到服务端但 SYN-ACK 没回客户端,常见原因是回程路由走错、策略路由缺失、SNAT 不一致或反向路径过滤。服务端同时确认入包和路由决策:


			   baship route get <客户端IP> ip rule show sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.default.rp_filter for f in /proc/sys/net/ipv4/conf/*/rp_filter; do   printf '%s=%s ' "${f}" "$(cat "${f}")" done 

rp_filter=1 是严格模式,非对称路由和多宿主场景可能丢弃合法包;2 为松散模式,0 关闭。实际有效行为还与接口级值和内核规则相关。修改它会改变安全边界,不得为了“试试看”直接关闭。应先用路由、抓包和内核日志证明反向路径检查相关,再经过安全评审在单接口灰度,保留旧值并验证回程。

conntrack 与 NAT

经过状态防火墙或 NAT 的连接可能受 conntrack 表限制。表满时常见内核日志证据,NAT 还可能耗尽源端口。


			   bashsysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max 2>/dev/null || true journalctl -k -g 'nf_conntrack.*table full' --since '-2 hours' --no-pager conntrack -S 2>/dev/null || true ss -s sysctl net.ipv4.ip_local_port_range 

没有相关 sysctl 可能表示模块未加载或当前命名空间不可见,不要为了查看而在生产随意加载模块。提高表上限会消耗内存,扩展临时端口也必须避开保留端口;优先识别异常短连接、连接池失效、超时过长、攻击流量或 NAT 网关容量。

Kubernetes 场景:一定在正确的 namespace 和网络位置测试

以下命令中的所有 kubectl 都显式使用 -n <命名空间>。先看 Pod IP、节点、重启和就绪状态,再看 Service、EndpointSlice 与 NetworkPolicy。


			   bashkubectl -n <命名空间> get pod -o wide kubectl -n <命名空间> get service kubectl -n <命名空间> get endpointslice kubectl -n <命名空间> get networkpolicy kubectl -n <命名空间> describe pod  

Service 有 ClusterIP 但 EndpointSlice 没有 ready endpoint,问题在选择器、就绪探针或 Pod 状态,不是外部链路。NetworkPolicy 是否生效取决于 CNI 实现,不能仅凭对象存在下结论。

在故障 Pod 内执行,最贴近应用路径;前提是镜像包含工具且组织允许 exec:


			   bashkubectl -n <命名空间> exec  -- getent ahosts <目标域名> kubectl -n <命名空间> exec  --    curl --noproxy '*' -sS -o /dev/null    -w 'ip=%{remote_ip} code=%{http_code} total=%{time_total} '    --connect-timeout 3 --max-time 10 https://<目标域名>/health 

若业务镜像没有排障工具,不要临时修改镜像或在生产安装包。可按集群策略使用临时调试容器;它会创建额外进程和网络流量,执行前确认 RBAC、审计和资源限制。


			   bashkubectl -n <命名空间> debug pod/    -it --image=<已批准的调试镜像> --target=<容器名> 

kubectl debug 的可用参数和临时容器能力取决于 Kubernetes 版本、运行时和安全策略,先运行 kubectl debug --help<已批准的调试镜像> 必须使用组织仓库中的固定版本或摘要,不能现场拉取来源不明镜像。

一条可执行的分层流程

情况一:域名完全访问不了

  1. getent ahosts 失败:检查 NSS、resolv.conf、本地 resolved、DNS 可达性和查询返回码。
  2. 解析成功:对返回的每个 A/AAAA 地址执行 ip route get,确认实际出口与源地址。
  3. ping IP 失败:只说明 ICMP 未得到回应,继续 TCP 端口测试。
  4. telnet/nc 连接成功:网络层和 TCP 建连正常,转向 TLS/HTTP。
  5. curl 使用 --resolve 成功、普通域名失败:重点查 DNS 结果或地址族。

情况二:ping 成功,端口超时

  1. 客户端抓包确认 SYN 是否发出。
  2. 服务端抓包确认 SYN 是否到达。
  3. SYN 未到服务端:查客户端/中间防火墙、安全组、路由和 NAT。
  4. SYN 到达但无 SYN-ACK:查服务监听、防火墙和回程路由。
  5. SYN-ACK 发出但客户端未见:查回程路径、安全设备和非对称路由。

情况三:端口连接成功,curl 失败

  1. curl -w 判断失败在 TLS 还是首字节。
  2. TLS 失败:检查 SNI、证书链、时间、协议和 cipher 兼容性。
  3. HTTP 4xx:检查 Host、路径、鉴权和网关策略。
  4. HTTP 5xx:联查反向代理和应用日志,以同一时间、request ID 或 trace ID 对齐。
  5. 响应慢:用阶段耗时区分 DNS、TCP、TLS 和服务处理。

情况四:只有部分机器失败

对成功与失败机器做配置差异,而不是继续单机猜测:地址/掩码、路由规则、DNS、代理变量、时间、CA 证书、内核参数、防火墙、云安全组绑定、服务网格 sidecar 和出口 NAT。差异本身不是根因,需用复现实验验证。

自动采集:只读、限时、可审计

下面脚本收集四件套及本机网络状态。它不修改配置,所有命令有限时;因某个工具缺失或单项失败不会停止整次采集。以 root 运行会收集更多进程信息,输出可能含内部拓扑,分享前应脱敏。


			   bash#!/usr/bin/env bash set -uo pipefail TARGET_HOST="<目标域名>" TARGET_IP="<目标IP>" TARGET_PORT="<目标端口>" SCHEME="https" OUT_DIR="/var/tmp/netcheck-$(date +%Y%m%d-%H%M%S)" mkdir -p "${OUT_DIR}" exec > >(tee -a "${OUT_DIR}/report.txt") 2>&1 run() {   local name="$1"   shift   printf ' [%s] %s ' "$(date --iso-8601=seconds)" "${name}"   timeout 20 "$@" || printf 'command_failed rc=%d ' "$?" } run identity uname -a run links ip -br link run addresses ip -br address run rules ip rule show run routes ip route show table all run route_get ip route get "${TARGET_IP}" run dns_getent getent ahosts "${TARGET_HOST}" run ping ping -c 4 -W 2 "${TARGET_IP}" run tcp nc -vz -w 3 "${TARGET_IP}" "${TARGET_PORT}" run http curl --noproxy '*' -sS -o /dev/null    --connect-timeout 3 --max-time 10    -w 'ip=%{remote_ip} code=%{http_code} dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total} '    "${SCHEME}://${TARGET_HOST}:${TARGET_PORT}/health" run trace traceroute -T -p "${TARGET_PORT}" -n -w 2 -q 1 "${TARGET_IP}" run sockets ss -s printf ' output_dir=%s ' "${OUT_DIR}" 

进程替换 >(...) 要求 Bash。脚本把目标域名和 IP 都作为显式输入,是为了避免 DNS 变化让不同命令测到不同目标。若 nctraceroute 未安装,脚本会记录失败;不要在故障窗口未经审批安装软件。

用时间线对齐两端证据

偶发网络问题最怕两端时间不准。先检查时间同步,再在客户端与服务端使用同一时间窗查询日志。systemd-journald 的字段可以输出单调或 UTC 时间,便于对齐。


			   bashtimedatectl show -p NTPSynchronized -p TimeUSec -p Timezone journalctl -u <服务名>    --since '2026-08-05 1000'    --until '2026-08-05 1000'    -o short-iso-precise --no-pager 

示例时间必须换成实际故障窗口。客户端 curl 时间、负载均衡访问日志、服务日志和防火墙事件如果无法对齐,就不能可靠判断先后关系。

高风险修复的边界

排障工具是只读为主,修复操作却可能中断连接。以下动作必须按影响范围单独评审:

  • 修改默认路由或策略路由:可能立即断开 SSH。执行前保存 ip route/ip rule,验证带外控制台,在单台灰度;临时规则先测试,持久化配置另行审查。
  • 修改防火墙/安全组:可能扩大暴露面或阻断业务。先核对源、目的、端口和方向,使用最小范围规则与计数器,设置明确撤销步骤。
  • 修改 MTU:会影响接口上全部流量。保留原值,在冗余节点摘流后测试,小包、大包、TLS 和长连接都要验证。
  • 重启网络服务:NetworkManager、systemd-networkd 或传统 network 服务重启可能重建全部接口。远程生产主机不得把它作为第一步。
  • 重启应用:会中断或迁移会话。确认负载均衡摘流、就绪检查、连接排空和实例冗余,逐台进行。
  • 清空 conntrack:会破坏现有状态连接,通常不应作为常规修复。优先处理表满根因和容量设计。

路由变更若确需执行,先以自动回滚保护现场。下面是思路明确的连续流程,但命令仍需按发行版网络管理方式适配;ip 的运行时变更不会自动写入 NetworkManager 或 systemd-networkd 配置。


			   bash#!/usr/bin/env bash set -euo pipefail TARGET_CIDR="<目标网段CIDR>" NEW_VIA="<新网关IP>" IFACE="<网卡名>" ROLLBACK_FILE="/var/tmp/routes-before-$(date +%Y%m%d-%H%M%S).txt" ip route show table all >"${ROLLBACK_FILE}" ip route get "${NEW_VIA}" >/dev/null # 先创建 120 秒后的回滚任务;确认业务后由变更人员取消对应 transient unit。 systemd-run --unit=route-auto-rollback --on-active=120    /usr/sbin/ip route del "${TARGET_CIDR}" via "${NEW_VIA}" dev "${IFACE}" ip route add "${TARGET_CIDR}" via "${NEW_VIA}" dev "${IFACE}" ip route get "${TARGET_CIDR%/*}" ping -c 3 -W 1 "${NEW_VIA}" echo "Verify service now; cancel rollback only after validation:" echo "systemctl stop route-auto-rollback.timer" echo "route_snapshot=${ROLLBACK_FILE}" 

这段脚本假设目标网段原先没有同名路由,因此回滚是删除新路由;若是替换旧路由,不能直接使用。TARGET_CIDR%/* 对一般 CIDR 得到网络地址,但不保证它是可响应主机,只用于展示路由查找。执行前必须根据真实旧路由编写可逆命令,并验证 systemd transient timer 已创建。不能把保存的 ip route show table all 直接当作可执行恢复脚本。

常见误判

“ping 不通就是服务器挂了”

错误。ICMP 可能被目标、防火墙或云策略限制。继续使用 TCP 业务端口、服务端监听和应用健康检查验证。

“telnet Connected 就说明服务正常”

错误。它只证明 TCP 握手成功。TLS 可能失败,HTTP 可能 503,后端依赖也可能不可用。继续用 curl 或协议专用客户端。

“traceroute 从第七跳开始全是星号,所以第七跳坏了”

证据不足。节点可能不返回 TTL exceeded,目的仍可能可达。用 TCP traceroute、端到端 curl、双向测试和运营商/云监控交叉验证。

“curl -k 成功,所以证书没问题”

正相反,-k 成功而正常验证失败,说明连接与应用可能正常,但证书信任或名称验证有问题。修复证书链、SAN、CA 或系统时间,不应永久关闭验证。

“Connection refused 是防火墙丢包”

立即 refused 通常说明收到了 RST 或主动 reject,路径至少部分可达。查目标地址、监听端口、服务状态和防火墙 reject 规则;静默 drop 更常表现为超时。

“关闭防火墙试一下最快”

不可取。全局关闭会扩大攻击面,还会清除你判断具体命中规则的证据。使用规则计数器、有限抓包、临时精确 trace 或单源单端口灰度规则。

交付根因前的证据清单

一次排障结论至少应包含:

  1. 故障窗口、源和目标,明确域名解析到的 IP 与地址族。
  2. 本机接口、地址、ip route get 的输出及实际源地址。
  3. ping 的条件与结果,并明确它只代表 ICMP。
  4. TCP 测试是成功、RST 还是 timeout;必要时有两端抓包佐证。
  5. curl 的远端 IP、退出码、HTTP 状态和分阶段耗时。
  6. traceroute 使用的探测协议,不能把不回应跳直接判为丢包点。
  7. 服务端监听地址、进程状态、同一时间窗日志和应用健康结果。
  8. 防火墙/安全组/NetworkPolicy 的具体规则与计数,而不是“看起来没问题”。
  9. 若涉及 TLS,记录 SNI、证书链、有效期、验证错误和系统时间。
  10. 修复前后使用相同测试条件验证,并记录回滚是否仍可执行。

四件套的正确用法,是让每条命令回答一个有限的问题:ping 看 ICMP 基本可达性,telnet 看 TCP 建连,curl 看 DNS到应用层的完整过程,traceroute 看逐跳反馈。再用 ip route getss、日志、规则计数和受控抓包补齐证据,才能把“网络不通”收敛成可修复、可验证、可回滚的具体故障。

 


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分