土法炼钢兴趣小组的算法知识备份

【网络工程】网络故障排查系统化方法:从现象到根因

文章导航

分类入口
network
标签入口
#troubleshooting#diagnostics#network#linux#methodology

目录

“网络不通”是工程师最常遇到也最怕遇到的故障描述。它可能是 DNS 解析失败,可能是防火墙规则阻断,可能是 MTU 黑洞,可能是 BGP 路由震荡,可能是交换机端口的双工不匹配。如果没有系统化的排查方法,你只能一个一个猜,运气好 5 分钟解决,运气差排查 5 小时。

本文建立一套系统化的网络故障排查方法论。核心原则是分层隔离——按照网络模型逐层排查,每一层用对应的工具验证,快速缩小故障范围。

一、故障分类:先判断类型再选方法

1.1 三类网络故障

所有网络故障可以归入三类,每类的排查思路完全不同:

类型 典型表现 排查策略 难度
连通性故障 完全不通,Connection refused,No route to host 分层排查法,从 L1 到 L7 逐层验证 中等
性能故障 延迟高,吞吐低,偶发超时 延迟分解 + 基线对比 + 指标分析 较高
间歇性故障 时好时坏,随机丢包,偶发断连 持续监控 + 日志关联 + 统计分析 最高

间歇性故障最难排查,因为它可能在你开始排查时就”自愈”了。对付间歇性故障的关键是持续采集——在故障复现之前就部署好监控和抓包。

1.2 排查的黄金法则

在深入任何故障之前,记住这几条:

  1. 先确认问题的范围:是所有用户还是部分用户?是所有服务还是单个服务?范围越窄,排查越快。
  2. 先确认最近的变更:90% 的故障都能关联到最近的变更——部署、配置修改、网络设备变更、DNS 记录变更。
  3. 先用最简单的工具ping 能解决的问题不要上 tcpdump
  4. 不要同时改多个东西:一次只改一个变量,验证后再改下一个。

二、OSI 分层排查法

2.1 分层排查的逻辑

分层排查的核心思想:从低层到高层逐层验证。低层故障会影响所有高层,高层故障不会影响低层。

┌─────────────────┐
│ L7 应用层       │  HTTP 返回 502?gRPC 超时?
├─────────────────┤
│ L4 传输层       │  TCP 连接能建立吗?端口开放吗?
├─────────────────┤
│ L3 网络层       │  IP 可达吗?路由正确吗?
├─────────────────┤
│ L2 数据链路层   │  ARP 能解析吗?VLAN 正确吗?
├─────────────────┤
│ L1 物理层       │  网线插好了吗?网卡 UP 吗?
└─────────────────┘

实际排查时,不一定严格从 L1 开始。如果你确定物理层没问题(比如云服务器),可以直接从 L3 开始。但当你卡住时,往下退一层检查是好习惯。

2.2 L1 物理层检查

物理层故障在数据中心比在云环境更常见。典型检查:

# 网卡状态
ip link show eth0
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
#   状态应该是 UP 和 LOWER_UP
#   LOWER_UP 表示物理链路正常(有载波信号)

# 如果是 NO-CARRIER
ip link show eth0
# 2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> ...
# → 网线没插好或对端设备端口 down

# 网卡错误统计
ip -s link show eth0
# 关注 errors、dropped、overruns、carrier 计数
# 如果持续增长,说明物理层或驱动有问题

# ethtool 检查链路参数
ethtool eth0
# Speed: 1000Mb/s
# Duplex: Full         ← 必须是 Full,Half 会导致大量冲突
# Link detected: yes   ← 必须是 yes

常见 L1 问题

现象 可能原因 检查方法
NO-CARRIER 网线松了、对端 down 物理检查、ethtool eth0
Speed: 100Mb/s 自协商失败降速 ethtool -s eth0 speed 1000 duplex full
Duplex: Half 双工不匹配 两端强制设置相同参数
RX errors 持续增长 网线质量差、EMI 干扰 更换网线、检查走线

2.3 L2 数据链路层检查

L2 故障通常表现为同一子网内无法通信:

# ARP 表检查
ip neigh show
# 10.0.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
# 10.0.1.2 dev eth0 FAILED  ← ARP 解析失败

# 手动发 ARP 请求
arping -c 3 -I eth0 10.0.1.2

# VLAN 检查(如果使用 VLAN)
cat /proc/net/vlan/config
# 或
ip -d link show eth0.100

# 检查 bridge/交换表(如果是网桥)
bridge fdb show

常见 L2 问题

现象 可能原因 排查
ARP FAILED 目标不在同一 L2 域、目标 down arping、检查 VLAN 配置
单向通信 ARP 表错误、MAC 地址冲突 ip neigh flush dev eth0、检查 MAC
广播风暴 STP 未启用或环路 检查交换机 STP 状态

2.4 L3 网络层检查

L3 是最常见的故障层,涉及 IP 地址、路由和防火墙:

# 1. IP 地址检查
ip addr show eth0
# 确认 IP 地址、子网掩码正确

# 2. 路由表检查
ip route show
# default via 10.0.1.1 dev eth0
# 10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.50

# 检查到目标的路由
ip route get 10.0.2.100
# 10.0.2.100 via 10.0.1.1 dev eth0 src 10.0.1.50

# 3. ping 测试(ICMP)
ping -c 5 10.0.2.100

# 如果 ping 不通,检查是不是 ICMP 被过滤
# 用 TCP 连接测试替代
nc -zv -w 3 10.0.2.100 443
# 或
hping3 -S -p 443 -c 3 10.0.2.100

# 4. 防火墙规则检查
iptables -L -n -v --line-numbers
# 或
nft list ruleset

# 5. 检查 IP 转发(如果是网关/路由器角色)
sysctl net.ipv4.ip_forward
# 应该是 1

L3 排查决策树

ping 目标 IP
  ├── 成功 → L3 正常,检查 L4
  └── 失败
       ├── ping 网关
       │    ├── 成功 → 问题在网关之后
       │    │    ├── traceroute 看在哪一跳丢失
       │    │    └── 检查中间设备防火墙
       │    └── 失败 → 问题在本地
       │         ├── 检查 IP 地址和子网掩码
       │         ├── 检查网关 ARP 能否解析
       │         └── 检查本地防火墙
       └── 注意:ping 不通不代表不可达
            └── 用 TCP 连接测试确认

2.5 L4 传输层检查

L3 可达但服务不通,通常是 L4 问题(端口、连接状态):

# 1. 端口开放检查(本地)
ss -tlnp | grep :443
# LISTEN  0  4096  *:443  *:*  users:(("nginx",pid=1234,fd=6))

# 如果端口没监听
# → 服务没启动,或者绑定了错误的地址

# 2. 远程端口连通性
nc -zv -w 5 10.0.2.100 443
# Connection to 10.0.2.100 443 port [tcp/https] succeeded!

# 超时 → 防火墙丢弃(DROP)
# Connection refused → 端口未监听(服务端 RST)

# 3. TCP 连接状态分析
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
#  1523 ESTAB
#   342 TIME-WAIT
#    28 CLOSE-WAIT    ← 如果持续增长,说明应用没有正确关闭连接
#     5 SYN-SENT      ← 如果大量堆积,说明对端不可达或防火墙 DROP
#     2 SYN-RECV      ← 正常;如果大量堆积,可能是 SYN Flood

# 4. 检查连接数限制
sysctl net.core.somaxconn          # listen backlog 上限
sysctl net.ipv4.tcp_max_syn_backlog # SYN 队列上限
cat /proc/sys/net/netfilter/nf_conntrack_count  # 当前连接跟踪数
cat /proc/sys/net/netfilter/nf_conntrack_max    # 连接跟踪表上限

# 5. 检查是否有 conntrack 表满
dmesg | grep "nf_conntrack: table full"

L4 常见故障及解决

现象 错误信息 根因 解决
连接超时 connect: Connection timed out 防火墙 DROP 或路由黑洞 检查防火墙规则和路由
连接被拒 connect: Connection refused 端口未监听 检查服务状态
连接重置 read: Connection reset by peer 服务端异常关闭或中间设备 RST 抓包分析 RST 来源
CLOSE_WAIT 堆积 连接数持续增长 应用未调用 close() 修复应用的连接关闭逻辑
conntrack 表满 nf_conntrack: table full 连接数超过内核限制 增大 nf_conntrack_max

2.6 L7 应用层检查

前面各层都正常,但服务返回错误或行为异常:

# 1. HTTP 状态码检查
curl -sI https://api.example.com/health
# HTTP/2 502    ← 502 Bad Gateway 通常是代理后端不可达

# 2. HTTP 响应体检查
curl -s https://api.example.com/health | jq .

# 3. SSL/TLS 证书检查
echo | openssl s_client -connect api.example.com:443 \
  -servername api.example.com 2>/dev/null | openssl x509 -noout -dates
# notBefore=Jan  1 00:00:00 2025 GMT
# notAfter=Apr  1 00:00:00 2025 GMT  ← 证书过期!

# 4. DNS 解析检查
dig api.example.com +short
# 如果返回错误的 IP,问题在 DNS

# 5. 日志检查
journalctl -u nginx --since "10 minutes ago" --no-pager
tail -100 /var/log/nginx/error.log

三、排查工具链速查

3.1 按场景选工具

┌─────────────────────────────────────────────────────────────┐
│                    网络故障排查工具选择                        │
├────────────────┬────────────────────────────────────────────┤
│ 想知道什么      │ 用什么工具                                 │
├────────────────┼────────────────────────────────────────────┤
│ 网卡状态        │ ip link, ethtool                          │
│ IP 和路由       │ ip addr, ip route                         │
│ ARP 解析        │ ip neigh, arping                          │
│ 基础连通性      │ ping, hping3                              │
│ 路径分析        │ traceroute, mtr                           │
│ 端口开放        │ ss, nc (netcat), nmap                     │
│ TCP 连接状态    │ ss -tan, netstat -an                      │
│ 防火墙规则      │ iptables -L, nft list                     │
│ DNS 解析        │ dig, nslookup, resolvectl                 │
│ TLS/证书        │ openssl s_client, curl -v                 │
│ HTTP 请求       │ curl, wget, httpie                        │
│ 抓包分析        │ tcpdump, tshark, wireshark                │
│ 延迟分解        │ curl -w, mtr, hping3                      │
│ 带宽测试        │ iperf3, netperf                           │
│ 连接跟踪        │ conntrack, /proc/net/nf_conntrack         │
│ 内核诊断        │ bpftrace, ss -ti, /proc/net/*             │
└────────────────┴────────────────────────────────────────────┘

3.2 一条命令排查脚本

快速收集系统网络状态的脚本:

#!/bin/bash
# net-diag.sh — 网络状态快速诊断
TARGET="${1:-8.8.8.8}"

echo "========== 网络接口 =========="
ip -br link show
echo ""

echo "========== IP 地址 =========="
ip -br addr show
echo ""

echo "========== 路由表 =========="
ip route show
echo ""

echo "========== DNS 配置 =========="
cat /etc/resolv.conf | grep -v '^#'
echo ""

echo "========== DNS 解析测试 =========="
dig +short "$TARGET" 2>/dev/null || echo "dig 不可用"
echo ""

echo "========== 网关连通性 =========="
GW=$(ip route | grep default | awk '{print $3}')
ping -c 3 -W 2 "$GW" 2>/dev/null | tail -2
echo ""

echo "========== 目标连通性 =========="
ping -c 3 -W 2 "$TARGET" 2>/dev/null | tail -2
echo ""

echo "========== TCP 连接统计 =========="
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
echo ""

echo "========== 监听端口 =========="
ss -tlnp 2>/dev/null | head -20
echo ""

echo "========== 防火墙规则 =========="
iptables -L -n 2>/dev/null | head -20 || echo "无 iptables 权限"
echo ""

echo "========== 最近网络错误 =========="
dmesg | grep -i -E "nf_conntrack|dropped|error|reset|refused" | tail -10
echo ""

echo "========== 网卡错误统计 =========="
ip -s link show | grep -A 2 "RX\|TX" | grep -v "^--$"

四、常见故障模式与排查路径

4.1 Connection Refused

错误: connect: Connection refused (ECONNREFUSED)

含义:目标主机收到了 SYN,但目标端口没有进程监听,内核回复 RST。

排查路径

# 1. 确认服务是否在运行
systemctl status myservice
# 或
ps aux | grep myservice

# 2. 确认监听地址和端口
ss -tlnp | grep :8080
# 常见问题:服务绑定了 127.0.0.1 但你从外部访问
# LISTEN  0  128  127.0.0.1:8080  *:*  ← 只监听了 loopback

# 3. 确认是不是绑定了错误的端口
ss -tlnp | grep myservice
# 发现监听在 8081 而不是 8080

# 4. 检查是否端口冲突
ss -tlnp | grep :8080
# 另一个进程占了端口

4.2 Connection Timed Out

错误: connect: Connection timed out (ETIMEDOUT)

含义:SYN 包发出去了但没收到 SYN-ACK,TCP 重传多次后超时。

排查路径

# 1. 确认 IP 可达(L3)
ping -c 3 -W 2 10.0.2.100
# 如果 ping 也超时 → L3 不可达

# 2. 如果 ping 通但连接超时 → 可能是端口级防火墙
hping3 -S -p 8080 -c 3 10.0.2.100
# 没有响应 → 防火墙在 DROP

# 3. 检查本地防火墙出站规则
iptables -L OUTPUT -n -v

# 4. 检查目标机器防火墙入站规则(如果有权限)
ssh 10.0.2.100 "iptables -L INPUT -n -v"

# 5. 检查安全组(云环境)
# AWS: aws ec2 describe-security-groups
# 阿里云: aliyun ecs DescribeSecurityGroupAttribute

# 6. 抓包确认 SYN 是否到达目标
# 在目标机器上:
tcpdump -i eth0 'tcp port 8080 and tcp[tcpflags] & tcp-syn != 0' -c 10

4.3 间歇性丢包

间歇性丢包是最难排查的故障类型,因为无法按需复现。

排查策略:持续监控 + 关联分析

# 1. 持续 ping 监控(记录时间戳)
ping -D -i 0.5 10.0.2.100 | tee ping-log-$(date +%Y%m%d).txt
# -D 打印 UNIX 时间戳

# 2. mtr 持续路径监控
mtr -rw -c 1000 -i 0.1 10.0.2.100 | tee mtr-log.txt

# 3. 抓包准备(滚动捕获,等待故障复现)
tcpdump -i eth0 -w /tmp/capture-%H%M.pcap \
  -G 300 -C 100 -W 20 \
  host 10.0.2.100 and tcp port 443

# 4. 关联系统指标
# 在 ping 丢包的时间点,检查:
sar -n DEV 1           # 网卡流量
sar -n EDEV 1          # 网卡错误
sar -n TCP,ETCP 1      # TCP 统计
vmstat 1                # CPU/内存/IO

# 5. 检查 NIC ring buffer 丢包
ethtool -S eth0 | grep -i drop
# rx_dropped: 12345     ← ring buffer 满导致丢包

# 6. 检查内核丢包
cat /proc/net/snmp | grep -A 1 "Tcp:"
# 关注 InErrs、RetransSegs

# 7. 检查 netfilter/conntrack 丢包
dmesg | grep "nf_conntrack: table full"
conntrack -C    # 当前连接数
conntrack -L | wc -l

间歇性丢包的常见根因

根因 检测方法 解决方案
NIC ring buffer 溢出 ethtool -S eth0 \| grep drop 增大 ring buffer:ethtool -G eth0 rx 4096
conntrack 表满 dmesg \| grep nf_conntrack 增大 nf_conntrack_max
CPU softirq 来不及处理 cat /proc/net/softnet_stat 调整 RPS,增加 netdev_budget
TCP backlog 满 netstat -s \| grep overflow 增大 somaxconn 和应用 backlog
带宽饱和 sar -n DEV 1 扩容或限流
MTU 黑洞 ping -M do -s 1472 target 修复 PMTUD 或降低 MTU

4.4 MTU 黑洞

MTU 黑洞是一种隐蔽的故障:小包正常,大包丢失。原因是路径上某个设备的 MTU 小于发送端,且 ICMP “Fragmentation Needed” 消息被防火墙丢弃。

检测方法

# 1. 用不同大小的包测试
# 1472 = 1500 (MTU) - 20 (IP header) - 8 (ICMP header)
ping -M do -s 1472 -c 3 target.example.com    # 标准 MTU
ping -M do -s 1400 -c 3 target.example.com    # 常见 VPN/隧道 MTU
ping -M do -s 1300 -c 3 target.example.com    # 更小
ping -M do -s 1200 -c 3 target.example.com    # 更小

# 2. 二分法找到精确的 Path MTU
# 如果 1472 失败、1300 成功,在中间尝试 1400、1350...

# 3. tracepath 自动发现 Path MTU
tracepath target.example.com
# ...
# Resume: pmtu 1420    ← Path MTU 是 1420

# 4. 修复:降低本地 MTU 或 MSS
# 方法 A:直接降低接口 MTU
ip link set dev eth0 mtu 1400

# 方法 B:用 iptables 钳制 MSS(推荐,不影响本地通信)
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --set-mss 1360

4.5 DNS 故障

DNS 故障的表现多种多样,排查时要区分 DNS 本身的问题和 DNS 配置的问题:

# 1. 确认 DNS 解析是否正常
dig example.com
# 如果 ;; connection timed out → DNS 服务器不可达
# 如果 SERVFAIL → DNS 服务器有问题
# 如果 NXDOMAIN → 域名不存在(或 DNS 劫持)

# 2. 测试不同 DNS 服务器
dig @8.8.8.8 example.com
dig @1.1.1.1 example.com
dig @$(grep nameserver /etc/resolv.conf | head -1 | awk '{print $2}') example.com

# 3. 完整解析链路追踪
dig +trace example.com

# 4. 检查是否被劫持
# 从多个 DNS 服务器查询,结果应该一致
for dns in 8.8.8.8 1.1.1.1 223.5.5.5 114.114.114.114; do
    echo -n "$dns: "; dig @$dns +short example.com
done

# 5. 检查本地 DNS 配置
cat /etc/resolv.conf
resolvectl status
systemd-resolve --status

五、排查决策树

5.1 “完全不通”的排查决策树

#!/bin/bash
# diagnose-connectivity.sh — 连通性排查自动化
TARGET_IP="${1:?Usage: $0 <target-ip> [target-port]}"
TARGET_PORT="${2:-443}"

echo "=== 连通性诊断: $TARGET_IP:$TARGET_PORT ==="

# L1: 本地网卡
echo -n "[L1] 网卡状态: "
if ip link show eth0 | grep -q "state UP"; then
    echo "UP ✓"
else
    echo "DOWN ✗ → 检查网卡和网线"
    exit 1
fi

# L2: ARP/网关
GW=$(ip route | grep default | awk '{print $3}')
echo -n "[L2] 网关 ARP ($GW): "
if arping -c 1 -w 2 -I eth0 "$GW" &>/dev/null; then
    echo "OK ✓"
else
    echo "FAILED ✗ → 检查 VLAN 和交换机"
    exit 1
fi

# L3: IP 可达
echo -n "[L3] 网关 ping: "
if ping -c 2 -W 2 "$GW" &>/dev/null; then
    echo "OK ✓"
else
    echo "FAILED ✗ → 检查 IP 配置和路由"
    exit 1
fi

echo -n "[L3] 目标 ping: "
if ping -c 2 -W 2 "$TARGET_IP" &>/dev/null; then
    echo "OK ✓"
else
    echo "FAILED (ICMP 可能被过滤,继续检查 L4)"
fi

# L3: 路由
echo -n "[L3] 路由: "
ROUTE=$(ip route get "$TARGET_IP" 2>/dev/null)
if [ $? -eq 0 ]; then
    echo "$(echo $ROUTE | head -1) ✓"
else
    echo "无路由 ✗ → 检查路由表"
    exit 1
fi

# L4: TCP 连接
echo -n "[L4] TCP $TARGET_PORT: "
if nc -zv -w 5 "$TARGET_IP" "$TARGET_PORT" &>/dev/null; then
    echo "OPEN ✓"
else
    echo "CLOSED/FILTERED ✗"
    echo "    → Connection refused = 端口未监听"
    echo "    → Timed out = 防火墙 DROP"
fi

# DNS: 如果目标是域名
if [[ "$TARGET_IP" =~ [a-zA-Z] ]]; then
    echo -n "[DNS] 解析: "
    RESOLVED=$(dig +short "$TARGET_IP" | head -1)
    if [ -n "$RESOLVED" ]; then
        echo "$RESOLVED ✓"
    else
        echo "FAILED ✗ → 检查 DNS 配置"
    fi
fi

# L7: HTTP(如果是 Web 服务)
if [ "$TARGET_PORT" = "443" ] || [ "$TARGET_PORT" = "80" ]; then
    SCHEME="http"
    [ "$TARGET_PORT" = "443" ] && SCHEME="https"
    echo -n "[L7] HTTP 状态: "
    STATUS=$(curl -sk -o /dev/null -w "%{http_code}" \
      --connect-timeout 5 "${SCHEME}://${TARGET_IP}/")
    if [ "$STATUS" != "000" ]; then
        echo "HTTP $STATUS"
    else
        echo "无响应 ✗"
    fi
fi

5.2 “慢”的排查决策树

用户说"慢"
  │
  ├── 所有请求都慢?
  │    ├── 是 → 基础设施问题
  │    │    ├── curl -w 做延迟分解
  │    │    ├── DNS 阶段慢?→ 检查 DNS 服务器
  │    │    ├── TCP 阶段慢?→ mtr 检查路径
  │    │    ├── TLS 阶段慢?→ openssl 检查版本/证书
  │    │    └── Server 阶段慢?→ 检查服务端负载
  │    └── 否 → 部分请求慢
  │         ├── 特定用户?→ 检查该用户的网络路径
  │         ├── 特定接口?→ 检查接口性能(慢 SQL?)
  │         └── 随机分布?→ 检查负载均衡器和后端健康
  │
  ├── P50 正常但 P99 高?
  │    ├── → 长尾延迟问题
  │    ├── GC 暂停?→ 检查 GC 日志
  │    ├── 锁竞争?→ 检查锁等待指标
  │    ├── 慢查询?→ 检查数据库慢日志
  │    └── 外部依赖超时?→ 检查外部服务 SLA
  │
  └── 最近才开始慢?
       ├── 关联最近部署 → 回滚验证
       ├── 关联最近配置变更 → 回退配置
       └── 无明显关联 → 检查流量增长和资源利用率

六、防火墙与安全组排查

6.1 iptables 规则排查

防火墙是网络故障的高频元凶。排查时要同时检查入站和出站:

# 查看所有链的规则(包含计数)
iptables -L -n -v --line-numbers

# 查看 NAT 表
iptables -t nat -L -n -v

# 关键指标:看 pkts 和 bytes 列
# 如果某条 DROP 规则的 pkts 持续增长,它就是嫌疑人

# 实时监控被 DROP 的包
iptables -I INPUT -j LOG --log-prefix "IPT-DROP: " --log-level 4
# 然后:
tail -f /var/log/kern.log | grep "IPT-DROP"

# 临时插入 ACCEPT 规则验证
iptables -I INPUT 1 -s 10.0.1.0/24 -p tcp --dport 8080 -j ACCEPT
# 验证后记得删除或持久化

6.2 conntrack 问题排查

conntrack(连接跟踪)表满是高并发服务的常见故障:

# 当前连接跟踪数和上限
echo "当前: $(cat /proc/sys/net/netfilter/nf_conntrack_count)"
echo "上限: $(cat /proc/sys/net/netfilter/nf_conntrack_max)"

# 如果接近上限
# 1. 临时增大上限
sysctl -w net.netfilter.nf_conntrack_max=1048576

# 2. 减少超时时间(释放旧连接)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=1200

# 3. 查看连接分布
conntrack -L 2>/dev/null | awk '{print $3}' | sort | uniq -c | sort -rn
# 100000 tcp
#   5000 udp
#   2000 icmp

# 4. 查看 TCP 状态分布
conntrack -L -p tcp 2>/dev/null \
  | awk '{for(i=1;i<=NF;i++) if($i ~ /^[A-Z_]+$/ && length($i)>3) print $i}' \
  | sort | uniq -c | sort -rn

七、容器与 Kubernetes 网络排查

7.1 Docker 网络排查

容器网络增加了一层虚拟化,排查时要区分宿主机和容器内的视角:

# 1. 进入容器网络命名空间
docker exec -it <container> sh

# 在容器内检查
ip addr show
ip route show
cat /etc/resolv.conf

# 2. 从宿主机检查容器网络
# 找到容器的 PID
PID=$(docker inspect --format '{{.State.Pid}}' <container>)

# 用 nsenter 进入容器网络命名空间
nsenter -t $PID -n ip addr show
nsenter -t $PID -n ss -tlnp
nsenter -t $PID -n iptables -L -n

# 3. 检查 docker 网络
docker network ls
docker network inspect bridge

# 4. 容器间通信问题
docker exec container-a ping container-b
# 如果用 docker-compose,用服务名
docker exec container-a ping service-b

7.2 Kubernetes 网络排查

K8s 的网络排查更复杂,因为涉及 Pod 网络、Service 网络和 Ingress 多层:

# 1. Pod 网络检查
kubectl exec -it <pod> -- sh
# 在 Pod 内
ip addr show
ip route show
cat /etc/resolv.conf
nslookup kubernetes.default

# 2. Service DNS 解析
kubectl exec -it <pod> -- nslookup my-service.my-namespace.svc.cluster.local

# 3. Service Endpoint 检查
kubectl get endpoints my-service
# 如果 ENDPOINTS 为空 → selector 没有匹配到 Pod

# 4. 从 Pod 到 Service 的连通性
kubectl exec -it <pod> -- curl -s http://my-service:8080/health

# 5. 检查 NetworkPolicy
kubectl get networkpolicy -A
kubectl describe networkpolicy <policy-name>

# 6. 用 debug 容器抓包
kubectl debug -it <pod> --image=nicolaka/netshoot -- tcpdump -i eth0 -c 20

# 7. 检查 CNI 状态
kubectl get pods -n kube-system | grep -E "calico|cilium|flannel"
kubectl logs -n kube-system <cni-pod> --tail=50

K8s 网络排查决策树

Pod 无法访问 Service
  ├── DNS 解析正常?
  │    ├── 否 → 检查 CoreDNS Pod 状态
  │    └── 是 → 继续
  ├── Service 有 Endpoints?
  │    ├── 否 → selector 不匹配 Pod labels
  │    └── 是 → 继续
  ├── 直接访问 Pod IP 正常?
  │    ├── 否 → CNI 或 NetworkPolicy 问题
  │    └── 是 → kube-proxy / iptables 规则问题
  └── 检查 NetworkPolicy 是否阻断了流量

八、真实故障案例复盘

8.1 案例一:新服务部署后”网络不通”

现象:新部署的服务在 K8s 集群中无法被其他服务访问,curl 超时。

排查过程

# 1. Service 存在且有 Endpoints
kubectl get svc my-new-service
# NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
# my-new-service   ClusterIP   10.96.123.45    <none>        8080/TCP

kubectl get endpoints my-new-service
# NAME             ENDPOINTS           AGE
# my-new-service   10.244.1.15:8080    5m

# 2. Pod 在运行
kubectl get pod -l app=my-new-service
# NAME                              READY   STATUS    RESTARTS
# my-new-service-7f8b9c6d4-x2k9f   1/1     Running   0

# 3. 直接 curl Pod IP → 超时
kubectl exec -it debug-pod -- curl -m 5 http://10.244.1.15:8080/health
# curl: (28) Connection timed out

# 4. 进入 Pod 检查 → 端口在监听
kubectl exec -it my-new-service-7f8b9c6d4-x2k9f -- ss -tlnp
# LISTEN  0  128  127.0.0.1:8080  *:*
# ← 绑定了 127.0.0.1!只接受 localhost 连接

# 5. 根因:应用配置绑定了 127.0.0.1 而不是 0.0.0.0

修复:修改应用配置,监听 0.0.0.0:8080

教训:本地开发环境绑定 127.0.0.1 是好习惯,但部署到容器时必须改成 0.0.0.0。

8.2 案例二:生产环境周期性超时

现象:每天下午 2 点左右,服务的 P99 延迟飙升,持续约 15 分钟。

排查过程

# 1. 关联时间线
# 下午 2 点有什么定时任务?
crontab -l
# 14:00 /opt/scripts/daily-report.sh  ← 嫌疑人

# 2. 检查定时任务做了什么
cat /opt/scripts/daily-report.sh
# 大量数据库查询生成日报告

# 3. 确认数据库是瓶颈
# 在 2 点时刻检查:
mysql -e "SHOW PROCESSLIST" | wc -l
# 在正常时段:50
# 在 2 点:450  ← 连接数暴增

# 4. 检查慢查询
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries'"
# 2 点时段慢查询数量是平时的 10 倍

# 5. 根因:日报脚本的全表扫描查询锁住了表

修复

  1. 日报脚本使用只读从库
  2. 大查询拆分成批次,每批之间 sleep
  3. 添加适当的索引

8.3 案例三:跨可用区间歇性丢包

现象:跨可用区的服务调用偶发超时,约 0.5% 的请求受影响。

# 1. 持续 ping 监控
ping -i 0.1 -D cross-az-service.internal | while read line; do
    echo "$(date '+%H:%M:%S.%N') $line"
done > ping-crossaz.log &

# 运行 1 小时后分析
grep "time=" ping-crossaz.log | awk -F'time=' '{print $2}' | \
  awk -F' ' '{print $1}' | sort -n | \
  awk 'BEGIN{n=0} {a[n++]=$1} END{
    printf "P50: %.1f ms\n", a[int(n*0.5)];
    printf "P99: %.1f ms\n", a[int(n*0.99)];
    printf "Max: %.1f ms\n", a[n-1];
  }'
# P50: 0.8 ms
# P99: 3.2 ms
# Max: 245.0 ms  ← 偶发高延迟

# 2. 检查网卡丢包
ethtool -S eth0 | grep -E "drop|error"
# rx_queue_0_drops: 15234  ← 网卡队列丢包

# 3. 检查 softirq 处理延迟
cat /proc/net/softnet_stat | awk '{print $2}' | head -8
# 每列是一个 CPU 的 softnet_stat
# 第 2 列 > 0 表示因为 netdev_budget 用完而被 drop

# 4. 根因:CPU 0 处理了所有网络中断,其他 CPU 空闲

# 5. 修复:启用 RPS 分散到多个 CPU
echo "ff" > /sys/class/net/eth0/queues/rx-0/rps_cpus

九、排查记录模板

每次故障排查都应该记录,既是知识沉淀也是复盘材料:

## 故障记录:[简短标题]

**时间**:YYYY-MM-DD HH:MM — HH:MM
**影响**:[受影响的服务/用户/范围]
**严重程度**:P0/P1/P2/P3

### 现象
[用户视角的故障表现]

### 时间线
- HH:MM 收到告警
- HH:MM 开始排查
- HH:MM 定位根因
- HH:MM 实施修复
- HH:MM 确认恢复

### 排查过程
[使用的工具和命令,每步的发现]

### 根因
[技术根因分析]

### 修复措施
[短期修复 + 长期预防]

### 经验教训
[这次故障暴露了什么系统性问题?]

十、总结:系统化排查的核心原则

  1. 先分类后排查。区分连通性故障、性能故障和间歇性故障,选择对应的排查策略。盲目猜测只会浪费时间。

  2. 分层隔离。从 L1 到 L7 逐层验证,确认每一层的状态。低层故障必然影响高层,如果 L3 不通,不要去检查 L7。

  3. 用数据说话。不要凭感觉判断”网络慢”,用 curl -w 量化延迟,用 mtr 量化丢包,用 ss 量化连接状态。数据是排查的基础。

  4. 先确认变更。90% 的故障可以关联到最近的变更。问清楚”什么时候开始的”和”最近改了什么”,往往比跑任何诊断工具都快。

  5. 记录和复盘。每次故障排查都是一次学习机会。记录排查过程、根因和修复措施,建立团队的故障知识库。


参考文献


上一篇:网络延迟分析方法论:从现象到瓶颈定位

下一篇:ss/netstat/ip 工具链:Socket 状态分析与连接审计

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-08-15 · network

【网络工程】BPF 网络诊断:bpftrace 与 bcc 工具实战

系统讲解 eBPF 在网络诊断中的工程应用:bcc 工具集(tcplife/tcpretrans/tcpdrop)的使用场景、bpftrace 自定义网络探针编写、XDP 丢包分析、内核协议栈延迟追踪,建立基于 eBPF 的系统化网络诊断方法。

2026-04-22 · network

网络工程索引

汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。


By .