找回密码
立即注册
搜索
发回帖 发新帖

5081

积分

0

好友

659

主题
发表于 1 小时前 | 查看: 5| 回复: 0

"连不上"是生产网络故障里最常见的开头:连不上数据库、连不上上游接口、连不上另一台服务器,又或者外网访问偶尔超时。听起来是网络问题,但根因分布其实很散——可能是 DNS 解析失败,可能是路由或 ACL 变更,可能是防火墙规则,也可能是应用层连接池耗尽、DNS 服务器被过载,或者一张被打满的 TCP 连接表。把这四类原因混在一起排查,最常见的结局是"看着都没错"——每个工具单独跑一遍都正常,故障依然稳稳地待在那里。

本文给出一条按"由近及远、由应用向链路"顺序推进的排查路径,覆盖四段:DNS 解析、三层路由与连通性、防火墙与访问控制、TCP 连接状态与参数。环境假设为 RHEL 8 兼容系统的生产服务器(ss、ip、mtr、tcpdump 可用),服务可能是裸机也可能是容器化部署;容器内排查时额外注意命名空间与 cgroup 的差异(下文标注)。文中所有命令输出均为示例,用于说明判断方法,不代表真实线上记录。排查顺序不是死板流程,它回答的是"在什么证据下跳过哪一步";遇到明显线索(比如只有某个域名解析失败)可以直接跳到对应段落。

排查前:先收敛症状

动手跑命令之前,先把症状收敛成四条信息。它们决定后面哪些步骤可以跳过,也决定排查从哪一段切入:

  1. 谁访问谁:源主机、目标主机(IP 和域名都要,域名类故障 90% 出在解析或缓存)。
  2. 全还是部分:所有目标都失败,还是只有特定 IP/域名/特定源?"只有从 A 到 B 不通"和"全局不通"是两类问题。
  3. 什么时候开始:精确到分钟,对照变更时间线(发布、网络工单、云厂商维护公告)。
  4. 失败形态:立即失败(Connection refused、No route to host、解析失败)还是挂起超时?立即失败是"对端明确说不",挂起通常是"包在路上被丢了",方向完全不同。

这四项缺任何一条,后面的判断都容易跑偏。比如不知道"什么时候开始",就没法把故障和某次发布或网络变更关联起来。

第一段:DNS 解析

DNS 段要回答的核心问题是:域名到 IP 这一步到底成不成立?按下面的顺序排查。

第一步,本机解析验证。

# 记录完整耗时与结果(示例输出)
# server:
# 10.20.1.36
#
# Query time: 1 msec
# SERVER: 10.20.1.36#53(10.20.1.36)
dig +time=3 +tries=1 app.internal.example.com

+time=3 +tries=1 限制单次等待时间,并避免 dig 默认重试放大耗时造成的错觉。判断标准:能出 IP 且耗时正常(内网 DNS 通常毫秒级),DNS 段可以跳过;解析失败或耗时数秒以上,继续往下查。

第二步,区分"解析失败"和"解析被缓存/过期"。 应用报"找不到域名"但 dig 正常,最常见的原因是应用层自带缓存。Java 的 InetAddress 有自身 DNS 缓存,默认负缓存 10 秒、正缓存行为随 JDK 版本不同;Varnish 做正向代理、Nginx 的 resolver 指令也带 TTL 缓存。缓存层拿到的可能还是旧 IP——尤其是目标服务刚做过 IP 漂移(Keepalived VIP 切换、云 SLB 更换)。这时用 strace 挂到应用进程上,看它实际发出的 DNS 查询与拿到的结果:

# 示例:观察该进程发出的 DNS 包与结果
strace -e trace=network -p <pid> 2>&1 | grep -iE 'getaddrinfo|resolv|connect'

第三步,确认 /etc/resolv.conf 的配置。 容器环境里 resolv.conf 由 K8s 的 kube-dns/CoreDNS 或 Docker 网络注入,宿主机配置不生效;容器内要单独进容器查看:

cat /etc/resolv.conf
# 记录 nameserver、search 域、options 三项

search 域过长会放大"短名"解析失败的概率——短名先拼各 search 域逐个查询,全部超时才走 FQDN。options ndots 设得高(如 ndots:5)时,短域名也要先被当作 FQDN 解析再退回 search 域,内网环境常见的"解析慢"就出自这里。

第四步,验证上游 DNS 服务器本身。 本机解析正常但其他机器不正常,或者怀疑 DNS 服务器过载:

# 直接指定可疑上游(示例:云内网 DNS)
dig @10.20.1.36 app.internal.example.com +time=3 +tries=1

# 连续 20 次看是否有失败或偶发超时(示例统计)
# 20 queries, 0 failed
for i in $(seq 20); do dig +time=1 +tries=1 app.internal.example.com >/dev/null 2>&1 || echo "fail $i"; done

偶发失败(20 次里 1–2 次失败)指向 DNS 服务器过载或某条链路丢包,而不是配置错误——全局配置错误是 100% 失败。

DNS 段落的常见根因清单:resolv.conf 的 nameserver 指向已下线的 DNS、search 域与 ndots 配置放大超时、DNS 服务器被应用高频查询打满、负缓存过期时间过长。处置时,改 resolv.conf 前先备份原文件;DNS 服务器过载优先考虑在应用侧加本地缓存(dnsmasq、nscd 或应用框架自带缓存层),而不是无脑加机器。

第二段:三层路由与连通性

DNS 段排除后(或者故障一开始报的就是 IP 而非域名),确认三层可达性。

第一步,本机路由表与出接口。

# 看目标 IP 会走哪条路由(示例输出)
# 10.30.2.50 via 10.20.1.254 dev eth0
ip route get 10.30.2.50

# 全表(只看有目标网段相关的条目即可)
ip route

ip route get 是判断"本机决策"的核心命令:它给出源 IP、目的 IP 最终走的下一跳和出接口。如果下一跳指向已下线的交换机端口,或者出接口不是你以为的那个(双网卡环境常见),根因就在本机路由。

第二步,逐跳探测,定位断点在哪一跳。

# mtr 是 traceroute 与 ping 的合体(示例输出,30 秒)
# HOST: web-01        Loss%   Snt   Avg  Last
# 10.20.1.254         0.0%    30   0.8   0.9
# 10.30.1.254         0.0%    30   1.2   1.1
# 10.30.2.50         36.7%    30   8.4   9.1
mtr -n -c 30 10.30.2.50

判断方法:逐跳看 Loss% 从哪一跳开始出现持续丢包。前两跳全通、某跳之后(含目标)持续高丢包,断点就在"该跳与下一跳之间"。丢包只发生在最后一跳且中间跳全通,优先怀疑目标主机本身。注意,中间跳丢 ICMP 不一定是真实故障——mtr 的 loss 要结合目标自身 ping 来验证。

第三步,接口与网线层。 ip link 看接口状态,ethtool <iface> 看速率与双工是否异常。自协商失败降级到 100M 时带宽骤降但"能通",表现为间歇性超时而非完全不通:

ip -s link show eth0   # 看 TX/RX 错误、丢包计数
ethtool eth0           # 看 Speed、Duplex

接口 rx_dropped/tx_dropped 持续增长(用 ip -s link 两次采样对比)指向网卡队列溢出或驱动丢包。这类"能通但慢/间歇超时"的故障,容易被误判为"对端问题"。

第四步,变更时间线对齐。 若三层是"突然全断",先查最近 24 小时内是否有网络工单(交换机端口 ACL 变更、VLAN 调整、云安全组修改、BGP 路由撤销)。网络侧变更造成的"全断",本机逐跳探测可能一切正常直到某跳。此时 mtr 的 loss 分布是定位"变更影响面"的强证据:对比受影响子网与未受影响子网的 loss 差异。

第三段:防火墙与访问控制

三层通但端口不通,下一站是访问控制。按以下顺序区分"哪一层在丢包"。

本机 iptables/nftables 或 firewalld。

# firewalld 环境(RHEL 系常见)
# 列出当前生效的链与规则(示例)
# target  prot  source  destination
# ACCEPT  tcp   10.20.1.0/24 0.0.0.0/0 multiport dports 3306
firewall-cmd --list-all
iptables -S INPUT | grep 3306   # 裸机场景直接看 iptables

判断"规则是否在丢"最直接的方法是看链的 pkts/bytes 计数(iptables -L -n -v):某条 target=DROP 规则的 pkts 持续增长,就是根因;pkts=0 说明流量根本没到这条规则,问题在上游。

云安全组 / 网络 ACL / 物理交换机 ACL。 这三层本机看不到,处置路径不同:云安全组在控制台查看,变更后需等待生效(秒级到分钟级);交换机 ACL 需要网络侧配合。抓包对比(tcpdump 在源、目的两端各抓一段)能区分"源端没发出"还是"目的端没收到"——两端同时抓包、用时间戳对齐,是定界"谁在丢包"的标准手法。

Nginx/应用自身监听与 accept。 端口"通"但连接立即被拒,用 ss -lntp 看目标端口是否在监听、由哪个进程持有:

# 示例:查看 3306 监听
ss -lntp | grep 3306
# LISTEN 0 151 *:3306 *:* users:(("mysqld",pid=1024,fd=34))

端口没监听(应用没起来,或者监听地址绑定到 127.0.0.1 而非 0.0.0.0/具体网卡 IP),防火墙全通也没用。

第四段:TCP 连接状态与参数

前三段都正常、但应用报连接偶发超时或"连接池耗尽",问题就在 TCP 层。

连接状态分布。

# 看与某目标的连接状态(示例输出)
# State   Recv-Q  Send-Q     Local-Address:Port  Peer-Address:Port
# ESTAB   0      0      10.20.1.10:43210      10.30.2.50:3306
# TIME_WAIT 0  0  10.20.1.10:43100   10.30.2.50:3306
ss -tn state established '( dport = :3306 or sport = :3306 )' | head
ss -tn state time-wait | wc -l

TIME_WAIT 数量异常多(数万起步)指向短连接风暴——应用没做连接复用、每次请求新建连接。此时新连接因端口耗尽或本地端口范围受限开始失败。ESTAB 数量远超应用配置的连接池上限,则指向连接池泄漏:连接建立后未归还,池内全被占,业务报"获取连接超时",而实际底层连接还挂在对端。

SYN 队列与 accept 队列溢出。 ss -lnt 看 Recv-Q/Send-Q 两列。对 LISTEN 状态的 socket,Send-Q 是 somaxconn 上限,Recv-Q 是当前 accept 队列已入队连接数。用 nstat 看丢包计数:

# 示例:accept 队列溢出计数
nstat -az TcpExtListenDrops TcpExtOverflow

TcpExtListenDrops 持续增长 = accept 队列满,新 SYN 被丢,客户端侧表现为"连接有时快有时挂很久"(重传 SYN)。根因是应用处理连接的速度跟不上,或者 somaxconn 设得比后端实际能消化的还大导致积压——调大 somaxconn 不是治本,要查应用为何处理慢。

ss -i 看个别慢连接。 应用报"某个请求特别慢",用 ss -ti 看该连接的 rtt、unacked、cwnd:

# 示例:与某目标连接的信息
ss -ti dst 10.30.2.50
# cwnd:10 rtt:12.34/2.10 unacked:2 reordering:3

rtt 远大于网络基线(同子网应亚毫秒,跨机房几毫秒到几十毫秒)且持续增长,配合 reordering 增长,指向链路层丢包/重排——与第二段 mtr、接口错包计数交叉验证。cwnd 很小而 unacked 很高,指向拥塞窗口被压得很低,可能是带宽拥塞,也可能是中间设备 MTU 不匹配触发 Path MTU 问题。

MTU 与分片。 经典症状:HTTP 200 的页面偶尔打不开、特定大报文失败、小报文正常。用 ping -M do -s <size> <目标> 二分查找最大可通 size(-M do 禁止分片)。若某 size 开始不通而网络基线 MTU 应为 1500,指向路径上某跳 MTU 小于 1500 且未正确处理 Path MTU 发现(ICMP "需要分片"被防火墙丢弃)。容器环境里 Docker overlay 网络默认 MTU 会比宿主小,宿主 1500 时容器内常是 1450 左右,跨容器通信要按这个基线判断。

容器与 K8s 环境的特殊注意点

容器内排查时,以上命令要在容器内执行(kubectl exec),并且理解以下几点差异:

  • resolv.conf、/etc/hosts、路由表均由运行时注入。改动要改在 Deployment 配置或 NetworkPolicy 层,不要在容器内直接改文件,重启即丢。
  • Service 的 ClusterIP 是虚拟 IP,后端 Pod 间还隔着一层 kube-proxy 的 iptables/IPVS 规则。"Pod 直连 IP 通、走 Service 不通"时,kube-proxy 规则或 IPVS 会话表是嫌疑对象,用 ipvsadm -Ln(IPVS 模式)或 iptables -t filter -L KUBE-*(iptables 模式)逐条对照。
  • NetworkPolicy 只限制 Pod 间东西向流量,且需要 CNI 支持才生效。"配了 NetworkPolicy 但没效果",先确认 CNI 是否实现该能力,而不是怪配置写错。
  • ss/netstat 在容器内看到的是该网络命名空间内的连接,宿主机侧看不到;反之亦然。两端对齐抓包(tcpdump 在 Pod 网络与宿主机 eth 口同时抓)是定界"K8s 层丢"还是"物理层丢"的标准手法。

变更、回滚与验证

排查本身是只读的,真正产生风险的是处置阶段的变更。按风险分层:

  • 改 resolv.conf 的 options:备份原文件,改前执行 cp /etc/resolv.conf /etc/resolv.conf.bak.$(date +%s);改完立即 dig 验证,异常即恢复备份。
  • 改防火墙规则(firewall-cmd/iptables):规则追加而非整表重写,避免一次改坏所有连接。执行前记录当前规则集(iptables-save > /tmp/iptables.bak),回滚即 iptables-restore < /tmp/iptables.bak。firewalld 的 --permanent 与即时生效是两层,确认改的是哪层、重启是否触发。
  • 调 somaxconn 与 ulimit:内核参数属系统级变更,sysctl -w 即时生效但不持久化,sysctl -p 持久化需编辑 sysctl.d 文件——两者行为不同,别混用。回滚即恢复旧值。
  • Nginx 调 resolver 与连接池参数:改 nginx.conf 后 nginx -t 校验语法,再 nginx -s reload(graceful,不断连),不要直接 restart 造成连接中断。reload 失败(语法错误)时 Nginx 继续跑旧配置,不会"半新半旧"。
  • 云安全组 / 交换机 ACL 变更:无直接"回滚命令",变更前截图当前规则集作为恢复依据,变更后按"最小放行验证 → 逐步放行"灰度,出现异常立即恢复截图里的规则。
  • K8s NetworkPolicy / Service 配置:YAML 变更前 kubectl get <资源> -o yaml > backup.yaml,应用新配置用 kubectl apply,异常即 kubectl apply -f backup.yaml。

验证的统一标准:处置后"原来失败的操作"现在成功,且持续一个观察窗口(至少覆盖业务高峰或至少 15 分钟无复现);nstat 相关计数(TcpExtListenDrops、TcpRetransSegs)在处置后的采样中不再持续增长。

应当记住的判断原则

  • 收敛症状(谁到谁、全还是部分、何时开始、失败形态)比跑命令重要;缺这些信息,工具输出无法解读。
  • 排查顺序是"由近及远、由应用向链路":DNS → 本机路由 → 逐跳 mtr → 接口/错包 → 防火墙(本机 iptables -L -v 的 pkts 计数判断规则是否在丢)→ 云安全组/交换机(两端对齐抓包定界)→ TCP 状态分布与参数。
  • 关键定界命令:ip route get(本机路由决策)、mtr -n(断点在哪跳)、iptables -L -n -v 的 pkts(规则是否在丢)、ss -lnt 的 Recv-Q/Send-Q 与 nstat -az TcpExtListenDrops(accept 队列溢出)、ss -ti 的 rtt/cwnd/unacked(链路质量与拥塞)。
  • 中间跳丢 ICMP 不等于真实故障,必须用真实协议流量(dig、curl、nc)验证目标可达性。
  • 任何处置都是变更:改前备份、改后验证"原失败操作现在成功"、保留恢复依据(规则截图、YAML 备份)。云侧与交换机侧的"无回滚命令"场景,靠截图恢复。
  • 容器/K8s 环境里 resolv.conf、路由、网络命名空间均由运行时注入,排查命令要在 Pod 内执行。Service 背后隔着一层 kube-proxy,"Pod 直连 IP 通、走 Service 不通"优先查 kube-proxy 规则。



上一篇:深度解析 CPO:硅光子与外部激光器如何驱动光互连
下一篇:链上侦探卧底朝鲜黑客洗钱网络:追踪 Bybit 15 亿美元赃款
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-19 10:51 , Processed in 0.087187 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表