AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
  • 网络专题

    • 网络心智模型:从一个包的"一生"讲起
    • 看懂网络命令输出 —— 把每一个数字讲清楚
    • 容器网络底层:netns / veth / bridge / VXLAN
    • K8s 网络深入:Service / NodePort / Ingress 流量端到端
    • DNS 深入:CoreDNS / resolv.conf / ndots / 解析坑
    • 生产网络故障排查方法论

生产网络故障排查方法论

这是整个网络专题的压轴篇。前面 4 篇讲心智模型 / 输出解读 / 底层 / K8s / DNS——这一篇是把它们串起来用:给定一个生产现象,怎么一步步排查到根因。

这篇怎么用

flowchart LR
    A[生产报警 / 用户反馈] --> B[判断现象类型]
    B --> C{选 runbook}
    C -->|完全连不通| RB1[Runbook 1: 连不上服务]
    C -->|偶发性慢/超时| RB2[Runbook 2: 偶发性问题]
    C -->|节点 NotReady| RB3[Runbook 3: 节点网络故障]
    C -->|DNS 慢| RB4[Runbook 4: DNS 问题]
    C -->|Pod 起不来| RB5[Runbook 5: Pod 启动失败]
    C -->|集群跨节点| RB6[Runbook 6: 跨节点通信]
    RB1 --> D[根因]
    RB2 --> D
    RB3 --> D
    RB4 --> D
    RB5 --> D
    RB6 --> D

每个 runbook 都按 分层排错 + 真实 case study。


0. 排错总原则(先内化这些)

原则 1 - 从下往上排错

OSI / TCP-IP 模型分层。90% 的网络问题在 L3-L4,不要先怀疑 L7。

错误顺序(反例):

"应用报 500" 
  → 看应用代码、改代码、重新发布
  → 还是 500
  → 改了三处后才发现 L4 的 port 配错
  → 浪费 2 小时

正确顺序:

"应用报 500"
  → curl 测 L7 直接打 backend:失败
  → nc -zv backend port: 失败  ← L4 不通
  → ping backend IP: 失败       ← L3 不通
  → ip route get <ip>: 路由不对  ← 找到了

5 分钟。

原则 2 - 缩小问题域

问题在哪一层?哪一个组件?哪一个时间窗口?

"是不是 X 的问题"——用一个测试验证 / 排除:

  • 同一个 pod 访问 A 通、访问 B 不通 → 不是网络全局问题、是 B 的问题
  • 所有 pod 都不通同一个 service → 是 service 的问题、不是某个 pod 的问题
  • 重启 CoreDNS 之后好了一段时间又开始 → CoreDNS 内部状态有问题
原则 3 - 观察 vs 假设

别假设。每一步都要用命令观察。

错误:

"我以为 CNI 应该 ok"        ← 假设
"我以为 iptables 规则在"    ← 假设
"我以为 DNS 配置对"          ← 假设

正确:

kubectl logs -n kube-system ⟨cni-pod⟩       ← 观察
iptables -L KUBE-SERVICES -n -v | grep ip   ← 观察
kubectl exec pod -- dig svc                  ← 观察
原则 4 - 保留事故现场

重启前先抓快照。一旦重启故障可能消失、根因丢失。

# 一键快照脚本
DEST=/tmp/incident-$(date +%F-%H%M)
mkdir -p $DEST

# Pod 状态
kubectl get pod -A -o wide > $DEST/pods.txt
kubectl describe pod my-pod > $DEST/pod-desc.txt

# 节点状态
ssh m1 'top -bn 1; free; df; ss -s; dmesg -T | tail -200' > $DEST/m1-state.txt

# 网络
ssh m1 'ip route; iptables -L -n -v; conntrack -L | head -1000' > $DEST/m1-net.txt

# CoreDNS
kubectl logs -n kube-system coredns-xxx --tail=500 > $DEST/coredns.log

# 应用日志
kubectl logs my-pod --since=10m > $DEST/app.log

tar czf incident-snapshot.tar.gz $DEST

事故现场到手再考虑恢复。


Runbook 1:连不上服务

典型现象:

  • "我的 pod 访问 backend service 一直 timeout"
  • "外部访问 NodePort 完全不通"
  • "kubectl exec curl service 卡住"

通用 7 步流程

flowchart TD
    Start[连不上 X] --> S1{1. X 是<br>Service / IP / 域名?}
    S1 -->|Service| L1[L7: 短名 → FQDN 拼对吗]
    S1 -->|IP| L2[L4: nc -zv]
    S1 -->|域名| L3[L7: dig 解析对吗]
    L3 --> L2
    L1 --> S2[2. dig 解析到 ClusterIP?]
    S2 -->|否| F1[DNS / Service 不存在]
    S2 -->|是| L2
    L2 -->|refused| F2[端口没监听 / 防火墙 REJECT]
    L2 -->|timeout| S3[3. ping IP 通?]
    L2 -->|succeeded| L4[4. L7: curl -v]
    L4 --> S4[L7 应用问题]
    S3 -->|否| S5[L3 路由问题]
    S3 -->|是| F3[L4 被防火墙挡<br>iptables DROP / NetworkPolicy]
    S5 --> F4[ip route get / mtr 查路径]

具体步骤 + 命令

Step 1 - 确认 Service 存在
$ kubectl get svc ⟨name⟩ -n ⟨ns⟩
NAME       TYPE        CLUSTER-IP   PORT(S)
my-svc     ClusterIP   10.96.1.5    8080/TCP

$ kubectl get endpoints ⟨name⟩ -n ⟨ns⟩
NAME       ENDPOINTS                 AGE
my-svc     10.244.0.5:8080,10.244.1.6:8080   1h
Step 2 - DNS 解析
$ kubectl exec test-pod -- dig my-svc.default.svc.cluster.local
;; status: NOERROR
;; ANSWER SECTION:
my-svc... 30 IN A 10.96.1.5
Step 3 - L4 端口连通
$ kubectl exec test-pod -- nc -zv 10.96.1.5 8080
Connection to 10.96.1.5 8080 port [tcp/*] succeeded!
Step 4 - 直接访问 pod
# 取一个 endpoint pod
$ kubectl get endpoints my-svc -o jsonpath='{.subsets[0].addresses[0].ip}'
10.244.0.5

# 跳过 Service 直接打 pod
$ kubectl exec test-pod -- curl -v http://10.244.0.5:8080
Step 5 - 抓包
# 在 backend pod 节点上抓包
$ ssh m4
$ PID=$(crictl inspect $(crictl ps -q --name backend) | jq '.info.pid')
$ nsenter -t $PID -n tcpdump -i any -nn -c 30 port 8080

# 在 client pod 触发请求
$ kubectl exec test-pod -- curl http://my-svc:8080

Case 1.1: nc 通了但 curl 卡

$ kubectl exec test -- nc -zv my-svc 8080
succeeded!

$ kubectl exec test -- curl http://my-svc:8080
# 卡住、最终 timeout

L4 通、L7 卡 → 应用层问题:

# 看应用日志、慢查询、阻塞
$ kubectl logs my-svc-pod --tail=100

# 看应用是不是真的有 HTTP server
$ kubectl exec my-svc-pod -- ss -lntp
# 0.0.0.0:8080 LISTEN  process=python ...

# 测应用内本机
$ kubectl exec my-svc-pod -- curl http://127.0.0.1:8080
# 也卡?应用 hang 了

应用 hang —— 重启 pod、看代码。

Case 1.2: 外部访问 NodePort timeout

$ curl http://10.0.24.28:30080
# timeout

排查:

# 1. 节点本身通吗
$ ping 10.0.24.28
# 通

# 2. 节点上有 NodePort 监听吗(iptables 模式:没有真监听)
$ ssh m1 'ss -lntp | grep 30080'
# 空 ← 这正常,NodePort 靠 iptables 不真监听

# 3. iptables 有 NodePort 规则吗
$ ssh m1 'iptables -t nat -L KUBE-NODEPORTS -n -v | grep 30080'
0 0 KUBE-EXT-XYZ tcp -- ... tcp dpt:30080
^^^
pkts = 0 → 流量没到

# 4. 流量真到节点了吗(抓包)
$ ssh m1 'tcpdump -i any -nn port 30080'
# 外部 curl 一下

# 没看到流量进来 → 中间防火墙挡 / 路由器没把流量送到这台机器
# 看到流量、iptables 没匹配 → 规则错

修法:

  • 防火墙:通常云上安全组没开 NodePort(30000-32767)。开放:
    阿里云/AWS 安全组 → 允许 30000-32767/tcp inbound
    
  • iptables 规则错:kubectl rollout restart -n kube-system ds kube-proxy

Runbook 2:偶发性慢 / 超时

最难排错的一类——故障窗口短、复现概率低。

通用思路:监控 + 抓现场

flowchart TD
    Start[偶发性慢] --> S1[1. 上监控<br>持续观察]
    S1 --> S2[2. 触发时<br>抓现场]
    S2 --> S3[3. 对比正常 vs 故障时<br>找差异]
    S3 --> S4[4. 排除单变量]
    S4 --> Fix[根因]

监控指标速查

# 节点 CPU / 内存
node_cpu_seconds_total{mode="idle"}
node_memory_MemAvailable_bytes

# 网络丢包 / 错误
rate(node_network_receive_errs_total[1m])
rate(node_network_transmit_drop_total[1m])

# conntrack 表
node_nf_conntrack_entries
node_nf_conntrack_entries_limit

# K8s pod CPU / 内存
container_cpu_usage_seconds_total
container_memory_working_set_bytes

# DNS 延迟
histogram_quantile(0.99, sum by (le) (rate(coredns_dns_request_duration_seconds_bucket[5m])))

# Service 流量 P99 延迟(如果有 service mesh)

Case 2.1: K8s pod 偶发性 timeout 几秒

现象:业务接口 99% 正常、偶尔某个请求卡 5-30 秒。

排查:

# 1. 抓现场(trigger 时立刻跑)
ssh m1 'tcpdump -i any -nn -w /tmp/cap.pcap host ⟨backend-IP⟩ -G 60 -W 5'
# 滚动 5 个 60 秒文件

# 2. conntrack 表
ssh m1 'sysctl net.netfilter.nf_conntrack_count'
ssh m1 'sysctl net.netfilter.nf_conntrack_max'

# 3. DNS 响应慢?
ssh m1 'tcpdump -i any -nn port 53 -c 100'

常见根因:

根因信号修法
conntrack 表满dmesg nf_conntrack: table fullsysctl -w net.netfilter.nf_conntrack_max=1048576
TIME_WAIT 端口耗尽ss -s time-wait 大量 / cannot assign requested addresstcp_tw_reuse=1 + 调端口范围
DNS 偶发性慢业务慢的时候 dig 也慢见 Runbook 4
pod CPU 节流kubectl top pod 持续 100% / cgroup throttled调 resource.limits
节点 disk I/O 饱和iotop / iostat util > 90%移走密集 I/O 服务
NAT 翻译问题tcpdump 看到 RST看 conntrack 详细日志
跨节点 MTU 问题大包丢、小包通校准 MTU

Case 2.2: 服务跑半小时变慢

现象:服务启动后 30 分钟性能正常、半小时后逐渐变慢。

思路:随时间累积的东西——内存 / fd / 连接 / TIME_WAIT。

$ kubectl exec my-pod -- ss -s
Total: 50000                                  ← 太多
TCP:   45000 (estab 1000, closed 10000, time-wait 30000)

$ kubectl exec my-pod -- ss -tn state time-wait | wc -l
30000                                          ← TIME_WAIT 堆积!

$ ss -tn state close-wait | wc -l
500                                            ← CLOSE_WAIT 也涨

根因:应用没用连接池、每次请求新建 TCP 连接、TIME_WAIT 堆积。

修法:

# Python: 用 requests.Session 不要每次 requests.get
import requests
session = requests.Session()
session.get(...)

或者:

sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.ip_local_port_range="10000 65535"

Case 2.3: 跨机房 pod 通信间歇丢包

现象:A 机房 m1 → B 机房 m6 偶尔丢包。

排查:

# 持续 mtr
ssh m1 'mtr -rn -c 1000 -T -P 4789 ⟨m6-IP⟩' > /tmp/mtr.txt &
# 同时业务跑

# 看结果
cat /tmp/mtr.txt
# 如果中间某跳持续 5-10% 丢包 → 跨机房链路问题

根因:跨机房专线 / VPN / 互联网中间路径丢包。

修法:

  • 联系 IDC / 云厂商查专线
  • 改用更稳定的跨机房网络
  • 应用层重试机制

Runbook 3:节点 NotReady

现象:

$ kubectl get nodes
NAME   STATUS     ROLES    AGE   VERSION
m1     Ready      cp       1d    v1.29.0
m4     NotReady   <none>   1d    v1.29.0       ← 这个

分层排查流程

flowchart TD
    Start[Node NotReady] --> S1[1. kubelet 状态]
    S1 -->|挂| F1[systemctl start kubelet]
    S1 -->|跑着| S2[2. journalctl 看错误]
    S2 -->|CNI 错| F2[CNI 配置 / pod 健康]
    S2 -->|runtime 错| F3[containerd 状态]
    S2 -->|网络通信错| S3[3. 节点能连 apiserver?]
    S3 -->|不通| F4[apiserver / 路由问题]
    S3 -->|通| S4[4. 看 conditions]
    S4 -->|MemoryPressure| F5[内存紧张、看 top]
    S4 -->|DiskPressure| F6[df / du]
    S4 -->|PIDPressure| F7[ps、kill / 调 pid 上限]

Step-by-step

Step 1 - kubelet 跑没
ssh m4

systemctl status kubelet
# active (running)? 

# 没跑就启
systemctl start kubelet
journalctl -u kubelet --since "10 min ago" | tail -50
Step 2 - kubelet 报错
journalctl -u kubelet --since "10 min ago" | grep -iE "error|fail|panic"
Step 3 - 看 conditions
$ kubectl describe node m4
Conditions:
  Type             Status  Reason
  MemoryPressure   True    KubeletHasInsufficientMemory   ← 内存紧张
  DiskPressure     False
  PIDPressure      False
  Ready            False   KubeletNotReady                ← 不 ready
Step 4 - 节点到 apiserver
# 节点上测
ssh m4 'curl -k https://<apiserver>:6443/healthz'
# OK ?

# 看 iptables 是否挡
ssh m4 'iptables -L OUTPUT -n -v | head'

# 看路由
ssh m4 'ip route get ⟨apiserver-IP⟩'

Case 3.1: 5 节点中 1 个 NotReady

$ kubectl get nodes
m1 Ready
m2 Ready
m3 Ready
m4 NotReady     ← 这台
m5 Ready

# 排查
$ ssh m4 'journalctl -u kubelet --since "30 min ago" | tail -100'
... cni plugin not initialized
... failed to setup network for pod

$ ssh m4 'kubectl get pods -n kube-system -o wide | grep m4'
cilium-abc12  0/1  CrashLoopBackOff  m4    ← CNI pod 挂了

$ kubectl logs -n kube-system cilium-abc12 --previous
... Failed to start cilium-agent: failed to load bpf program

eBPF 程序加载失败——可能内核版本不够、可能 BPF 文件系统没挂。

修:

ssh m4 '
  mount bpffs /sys/fs/bpf -t bpf
  echo "bpffs /sys/fs/bpf bpf defaults 0 0" >> /etc/fstab
  systemctl restart kubelet
'

Case 3.2: 所有节点突然 NotReady

$ kubectl get nodes
NotReady, NotReady, NotReady, NotReady, NotReady

5/5 同时挂 → 多半是 control plane 的问题、不是节点的。

# 1. apiserver 在吗
$ ssh m1 'crictl ps | grep apiserver'

# 2. etcd 在吗
$ ssh m1 'crictl ps | grep etcd'

# 3. apiserver 报错
$ ssh m1 'crictl logs $(crictl ps -q --name kube-apiserver) --tail=100'

# 4. 证书过期?
$ ssh m1 'kubeadm certs check-expiration'

证书过期 = 控制面挂的最常见原因。生产 K8s 必定时续期(见 kubeadm.md)。


Runbook 4:DNS 问题

速查决策

flowchart TD
    Start[DNS 问题] --> S1{pod 完全不解析?}
    S1 -->|是| L1[CoreDNS / 网络通断]
    S1 -->|否| S2{解析慢?}
    S2 -->|是| L2[NXDOMAIN 雪崩 / 上游慢]
    S2 -->|否| S3{某些域名错?}
    S3 -->|集群内| L3[Service / EndpointSlice / cache]
    S3 -->|外部| L4[forward / 上游 DNS]

Case 4.1: 完全不解析

$ kubectl exec test-pod -- dig kubernetes.default
;; connection timed out

# 1. CoreDNS pod 健康吗
$ kubectl get pod -n kube-system -l k8s-app=kube-dns
NAME             READY   STATUS
coredns-xxx-1    0/1     CrashLoopBackOff   ← 挂了
coredns-xxx-2    0/1     CrashLoopBackOff

# 2. 看日志
$ kubectl logs -n kube-system coredns-xxx-1 --previous
... plugin/loop: Loop detected

根因:CoreDNS forward 配错、循环到自己。

修:CoreDNS Corefile 里 forward . 不能指向自己 IP 或 cluster DNS。

Case 4.2: 解析慢(详细案例见 04-dns-deep.md)

Case 4.3: K8s svc 解析对、外网慢

$ kubectl exec test-pod -- time dig kubernetes
real 0m0.005s          ← 集群内快

$ kubectl exec test-pod -- time dig github.com
real 0m4.123s          ← 外网慢

CoreDNS forward 到节点上游 DNS 慢。

# CoreDNS pod 自己测
$ kubectl exec -n kube-system coredns-xxx -- dig github.com
;; Query time: 3800 msec       ← CoreDNS 本身慢

# 看节点 DNS 配置
$ ssh m1 'cat /etc/resolv.conf'
nameserver 192.168.1.1    ← 老路由器、慢

# 改 CoreDNS 显式上游
kubectl edit cm -n kube-system coredns
# forward . 223.5.5.5 119.29.29.29

Runbook 5:Pod 起不来

决策树

flowchart TD
    Start[Pod 起不来] --> S1{Pod Status}
    S1 -->|Pending| Pending[Pending: 调度问题]
    S1 -->|ContainerCreating| Creating[Creating: CRI / 镜像 / volume]
    S1 -->|CrashLoopBackOff| Crash[Crash: 容器跑挂]
    S1 -->|ImagePullBackOff| Pull[镜像问题]
    S1 -->|ErrImageNeverPull| Pull
    S1 -->|Init:0/1| Init[Init container 没起]

    Pending --> P1[describe pod 看 events:<br>资源不足 / 节点 taint / 亲和性]
    Creating --> C1[describe pod 看 events:<br>CNI 失败 / volume 挂载失败]
    Crash --> CR1[logs --previous 看挂的原因]
    Pull --> PU1[describe 看具体镜像名 + 错误<br>imagePullSecret? 镜像 tag 对吗]

Step 1: describe 是关键

$ kubectl describe pod my-pod
Events:
  Type     Reason       Age   From               Message
  Normal   Scheduled    1m    default-scheduler  Successfully assigned ...
  Warning  FailedCreatePodSandBox 30s  kubelet  Failed to create pod sandbox: ...

Events 段几乎总有答案。

Case 5.1: ContainerCreating 卡住

$ kubectl describe pod my-pod
Events:
  ...
  Warning  FailedCreatePodSandBox  30s  kubelet  Failed to create pod sandbox:
    rpc error: code = Unknown desc = failed to setup network for sandbox:
    plugin type="cilium-cni" failed (add): unable to allocate IP

CNI 分配 IP 失败。

# 看 CNI pod
$ kubectl get pod -n kube-system | grep cilium

# 看 cilium-agent 日志
$ kubectl logs -n kube-system cilium-xxx | tail -30
... IP pool exhausted

修:扩大 pod CIDR / 清理孤儿 pod IP。

Case 5.2: CrashLoopBackOff + 应用日志看不出来

$ kubectl logs my-pod --previous
... Killed                                 ← 就这一行

$ kubectl describe pod my-pod
... Last State: Terminated
    Reason: OOMKilled                       ← 被 OOM 杀
    Exit Code: 137

$ kubectl top pod my-pod
NAME       CPU      MEMORY
my-pod     100m     500Mi/256Mi          ← 内存超 limit

修:调 resources.limits.memory 大点 / 查应用内存泄漏。

Case 5.3: ImagePullBackOff

$ kubectl describe pod my-pod
... Failed to pull image "registry.example.com/myapp:v1":
    rpc error: ... unauthorized

修:

  • 检查 imagePullSecret 配了没
  • 测:crictl pull registry.example.com/myapp:v1
  • registry 凭证:docker login registry.example.com 看通不通

Runbook 6:跨节点通信

Pod A on m1 → Pod B on m2 不通

sequenceDiagram
    participant TS as 排查
    participant M1 as m1
    participant Net as 节点间网络
    participant M2 as m2

    TS->>M1: 1. ip route get ⟨pod-b-IP⟩
    Note over M1: 应该: via ⟨m2-IP⟩ dev eth0
    TS->>M1: 2. ping ⟨m2-IP⟩
    Note over M1: 节点间通吗
    TS->>Net: 3. mtr ⟨m2-IP⟩
    TS->>M2: 4. nsenter pod-b: ss -lntp
    Note over M2: pod 在监听吗
    TS->>M2: 5. tcpdump pod-b
    Note over M2: 流量到了吗
    TS->>M1: 6. iptables KUBE-FORWARD
    Note over M1: 防火墙挡了吗

详细步骤

# 1. 节点间通吗
ssh m1 'ping -c 3 ⟨m2-IP⟩'

# 2. 路由对吗
ssh m1 'ip route get ⟨pod-b-IP⟩'
# 应该: via ⟨m2-IP⟩ dev eth0

# 3. 不对? 看 CNI 路由是否同步
ssh m1 'ip route | grep 10.244'
# 应该有每个其它节点 pod CIDR 的路由

# 4. 节点上 iptables FORWARD 链
ssh m1 'iptables -L FORWARD -n -v | head'
# 默认 ACCEPT? 还是有 DROP?

# 5. VXLAN 设备
ssh m1 'ip link show flannel.1 | grep mtu'
ssh m1 'ip -s link show flannel.1'        # errors / dropped

# 6. 两边同时抓包
ssh m1 'tcpdump -i any -nn host ⟨pod-b-IP⟩ -c 30' &
ssh m2 'tcpdump -i any -nn host ⟨pod-a-IP⟩ -c 30' &

# 触发流量
kubectl exec pod-a -- ping ⟨pod-b-IP⟩

# 看结果
# m1 抓到 ICMP 出、m2 没收到 → VXLAN 路径问题
# m2 收到 ICMP、m1 没收到 reply → 回路问题
# 两边都通 → 应用问题

Case 6.1: Calico BGP 路由不传播

$ ssh m1 'ip route | grep bird'
# 空 → BGP 没工作

$ kubectl get pods -n calico-system -l app.kubernetes.io/name=calico-node
calico-node-m1   1/1  Running
calico-node-m2   0/1  CrashLoopBackOff   ← m2 上挂了

$ kubectl logs -n calico-system calico-node-m2
... failed to start BGP session: ...

修复 calico-node、BGP 自动恢复路由。

Case 6.2: VXLAN 包被防火墙挡

# m1 → m2 ping pod IP 不通
# 但 m1 → m2 ping 节点 IP 通

# 看是不是 VXLAN UDP 4789 被挡
$ ssh m1 'tcpdump -i any -nn dst host ⟨m2-IP⟩ and udp port 4789 -c 10'
# 没看到 VXLAN 出去 → flannel 没用 vxlan
# 看到 VXLAN 出去、m2 抓不到入 → 防火墙挡 UDP 4789

# 修云上安全组:允许节点间 UDP 4789

综合 cheatsheet:8 个救命命令

不论什么故障,先跑这 8 个看节点 / 集群整体状况:

# 1. K8s 全局
kubectl get nodes
kubectl get pod -A -o wide | grep -v Running
kubectl get events -A --sort-by=".lastTimestamp" | tail -30

# 2. 节点 top
ssh m1 'uptime; free -h; df -h; ss -s'

# 3. dmesg 内核错误
ssh m1 'dmesg -T | grep -iE "oom|drop|fail|nf_conntrack|tcp" | tail -30'

# 4. kubelet 日志
ssh m1 'journalctl -u kubelet --since "5 min ago" | tail -30'

# 5. CoreDNS 状态
kubectl get pod -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system coredns-xxx --tail=30

# 6. CNI 状态
kubectl get pod -n kube-system | grep -iE 'cilium|calico|flannel'

# 7. 资源用量
kubectl top nodes
kubectl top pods -A --sort-by=memory | head

# 8. 网络关键 sysctl
ssh m1 'sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max net.ipv4.ip_forward net.bridge.bridge-nf-call-iptables'

跑完这 8 条、大多数故障的"根因方向"就清楚了。


总结:网络排错的心理建设

几句"经验之谈"

  1. 不要假设、要观察。每个步骤都用命令验证。
  2. 从下往上排错(L3 → L4 → L7)。
  3. 保留事故现场——重启之前先 snapshot。
  4. 缩小问题域——一次只验证一个假设。
  5. 怀疑变量——昨天好今天不好,问"什么变了"。
  6. DNS 永远是嫌疑犯——50% 的"网络问题"是 DNS。
  7. 看监控历史——故障是突发还是逐渐?
  8. 学会读 mermaid 流程图——本系列每篇都是排错地图。

排错速度的差距,70% 在心智模型、20% 在工具熟练度、10% 在运气。


全系列回顾

篇内容
00-mental-model.md网络心智模型
01-output-reading.md看懂命令输出
02-namespaces.md容器网络底层
03-k8s-network-deep.mdK8s 网络深入
04-dns-deep.mdDNS / CoreDNS
本篇生产网络故障排查方法论

命令文档

排错涉及的所有命令独立文档在 ../commands/:

  • 网络诊断:ip / ss / iptables / tcpdump / dig / mtr / curl / nc / ethtool
  • K8s:kubectl / crictl / cilium
  • 底层:nsenter / strace / dmesg / sysctl
  • 监控:prometheus / grafana
在 GitHub 上编辑此页
Prev
DNS 深入:CoreDNS / resolv.conf / ndots / 解析坑