技术博客

Kubernetes 网络故障排查:DNS 解析、Service 连通、CNI 深度调试

系统讲解 Kubernetes 网络故障排查方法:CoreDNS 解析失败定位、Service ClusterIP/NodePort/LoadBalancer 不通排查、Pod 间网络不通、CNI 插件问题调试,提供完整的诊断命令和排查思路,覆盖生产中 90% 的 K8s 网络故障场景。

Kubernetes网络排查DNSServiceCNICoreDNSiptables故障排查

K8s 网络问题是生产中最难排查的故障类型之一,因为网络链路长(Pod → veth → 节点网桥 → CNI → iptables/ebpf → 目标 Pod)且对用户不透明。本文按故障类型分类,给出系统的诊断命令和排查思路。

K8s 网络链路总览

Pod A(10.244.1.5)
  → veth pair(Pod 网卡到节点网桥)
  → 节点网桥(cni0/cbr0)
  → CNI 插件(Flannel/Calico/Cilium 处理路由/封包)
  → Service(ClusterIP → iptables DNAT / eBPF)
  → 目标 Pod B(10.244.2.8)

DNS 链路:
Pod → /etc/resolv.conf(nameserver = CoreDNS ClusterIP)
  → CoreDNS Pod(kube-system)
  → 上游 DNS(外部域名)

一、DNS 解析故障

DNS 是最常见的 K8s 网络问题根源。

快速诊断 DNS

# 进入一个 debug Pod(带网络工具)
kubectl run debug --image=nicolaka/netshoot -it --rm -- bash
# 或者进入已有 Pod
kubectl exec -it <pod-name> -- bash

# 在 Pod 内测试 DNS
# 1. 测试 K8s 服务 DNS
nslookup kubernetes.default.svc.cluster.local
# Server: 10.96.0.10    ← CoreDNS ClusterIP
# Address: 10.96.0.10#53
# Name: kubernetes.default.svc.cluster.local
# Address: 10.96.0.1    ← kubernetes Service ClusterIP

# 2. 测试跨命名空间 DNS
nslookup my-service.production.svc.cluster.local

# 3. 测试外部 DNS
nslookup baidu.com

# 4. 查看 Pod 的 DNS 配置
cat /etc/resolv.conf
# nameserver 10.96.0.10        ← CoreDNS IP
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5              ← 域名少于5个点时先尝试补全再查原始域名

CoreDNS 故障排查

# 查看 CoreDNS Pod 状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
# NAME                      READY   STATUS    RESTARTS   AGE
# coredns-5d78c9869d-xxxxx  1/1     Running   0          10d

# 查看 CoreDNS 日志(开启 log 插件才有详细日志)
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

# 查看 CoreDNS 配置
kubectl get configmap coredns -n kube-system -o yaml
# 关键配置:
# . {
#   errors
#   health
#   ready
#   kubernetes cluster.local in-addr.arpa ip6.arpa {
#     pods insecure
#     fallthrough in-addr.arpa ip6.arpa
#   }
#   forward . /etc/resolv.conf    ← 上游 DNS
#   cache 30
#   loop
#   reload
#   loadbalance
# }

# 重启 CoreDNS(解决某些 CoreDNS 自身问题)
kubectl rollout restart deployment coredns -n kube-system

# 测试能否直接访问 CoreDNS(从另一个 Pod)
kubectl run debug --image=nicolaka/netshoot -it --rm -- \
  dig @10.96.0.10 kubernetes.default.svc.cluster.local

ndots 问题(常见坑)

# 问题:应用访问 baidu.com 很慢
# 原因:ndots:5 导致先尝试补全的 5 个域名都失败,最后才查原始域名

# 在 Pod 内可以看到请求流程(tcpdump)
tcpdump -i any port 53 -n

# 访问 baidu.com 时,K8s 会先尝试:
# 1. baidu.com.default.svc.cluster.local → 失败
# 2. baidu.com.svc.cluster.local         → 失败
# 3. baidu.com.cluster.local             → 失败
# 4. baidu.com.                          → 成功!
# 总共发了 4 次 DNS 查询,每次都要 RTT

# 解决方案1:应用中使用完整域名(末尾加点)
# 访问 baidu.com.(末尾有点)跳过搜索域直接查询

# 解决方案2:降低 ndots(仅影响该 Pod)
spec:
  dnsConfig:
    options:
    - name: ndots
      value: "2"    # 少于2个点时才尝试补全搜索域

二、Service 不通故障排查

Service 诊断流程

# 确认 Service 存在且 Endpoint 不为空
kubectl get svc my-service -n production
# NAME         TYPE        CLUSTER-IP      PORT(S)    AGE
# my-service   ClusterIP   10.100.200.30   8080/TCP   5d

kubectl get endpoints my-service -n production
# NAME         ENDPOINTS                     AGE
# my-service   10.244.1.5:8080,10.244.1.6:8080   5d
# 如果 ENDPOINTS 显示 <none>,说明没有 Pod 匹配 Service 的 selector

# 检查 selector 是否匹配
kubectl get svc my-service -n production -o jsonpath='{.spec.selector}'
# {"app":"web","version":"v2"}

kubectl get pods -n production -l app=web,version=v2
# 如果为空,说明 Pod 没有这些 label,或 Pod 不在同一命名空间

# 检查 Pod 的 Ready 状态(Endpoint 只包含 Ready 的 Pod)
kubectl get pods -n production -l app=web -o wide
# 如果有 Pod 但不 Ready,看 Readiness Probe 配置

直接访问 Pod 排查(绕过 Service)

# 先直接访问 Pod IP,确认应用本身是否正常
kubectl exec -it debug -- curl http://10.244.1.5:8080/healthz
# 成功:应用正常,问题在 Service 或 iptables 层

# 再访问 Service ClusterIP
kubectl exec -it debug -- curl http://10.100.200.30:8080/healthz
# 失败:检查 iptables 规则

iptables 规则排查(Service 转发机制)

# Service 通过 iptables DNAT 实现负载均衡
# 查看某 Service 的 iptables 规则(在节点上执行)

# 找到 Service 对应的 iptables chain
iptables -t nat -L KUBE-SERVICES -n | grep 10.100.200.30
# KUBE-SVC-XXXXX  tcp -- 0.0.0.0/0  10.100.200.30  tcp dpt:8080

# 查看 DNAT 规则
iptables -t nat -L KUBE-SVC-XXXXX -n
# KUBE-SEP-AAA  all  0.0.0.0/0  0.0.0.0/0  statistic mode random probability 0.5
# KUBE-SEP-BBB  all  0.0.0.0/0  0.0.0.0/0

# 查看具体 DNAT 到哪个 Pod
iptables -t nat -L KUBE-SEP-AAA -n
# DNAT  tcp -- 0.0.0.0/0  0.0.0.0/0  tcp to:10.244.1.5:8080

# 如果使用 Cilium(eBPF),iptables 可能没有规则,改用:
cilium service list
cilium endpoint list

NodePort 和 LoadBalancer 问题

# NodePort 测试
NODE_IP=$(kubectl get node node-01 -o jsonpath='{.status.addresses[0].address}')
NODE_PORT=$(kubectl get svc my-service -n production -o jsonpath='{.spec.ports[0].nodePort}')
curl http://$NODE_IP:$NODE_PORT

# 如果 NodePort 不通,检查:
# 1. 节点防火墙/安全组是否放行该端口
# 2. kube-proxy 是否在节点上运行
kubectl get pods -n kube-system -l k8s-app=kube-proxy

# LoadBalancer 问题(云环境)
kubectl describe svc my-loadbalancer -n production
# Events:
#   Warning  SyncLoadBalancerFailed  Failed to ensure load balancer
# → 通常是 cloud-controller-manager 的配置问题或权限问题
kubectl logs -n kube-system -l app=cloud-controller-manager

三、Pod 间网络不通

基础连通性测试

# 测试 Pod 到 Pod(同节点)
kubectl exec -it pod-a -- ping 10.244.1.6       # 同节点 Pod IP
kubectl exec -it pod-a -- ping 10.244.2.8       # 跨节点 Pod IP

# 测试 Pod 到节点
kubectl exec -it pod-a -- ping 192.168.1.101   # 节点 IP

# 测试跨命名空间(DNS 方式)
kubectl exec -it pod-a -- curl http://my-service.other-namespace.svc.cluster.local

# 测试 Pod 出口(访问外部)
kubectl exec -it pod-a -- curl -I https://baidu.com

节点网络诊断

# 在节点上查看 Pod 网卡和路由
# 查看 Pod 的 veth pair
ip link | grep veth

# 查看节点路由表(看各 Pod CIDR 如何路由)
ip route
# 10.244.1.0/24 via 192.168.1.101 dev eth0  ← 到其他节点的 Pod CIDR
# 10.244.0.0/24 dev cni0 proto kernel       ← 本节点的 Pod CIDR

# 抓包(节点上)
# 抓 Pod 的 veth 接口
# 找到 Pod 对应的 veth(Pod 内 ifindex 对应节点上的 veth)
kubectl exec pod-a -- cat /sys/class/net/eth0/ifindex
# 假设输出 15
# 找节点上 ifindex=15 对应的接口
ip link | grep "^15:"

# 抓 veth 接口的包
tcpdump -i vethXXXXXX -n
# 抓节点网卡(看跨节点流量)
tcpdump -i eth0 -n host 10.244.2.8

# 测试跨节点连通(手动 ping 路由)
# 从 node-01 ping node-02 上的 Pod
ping 10.244.2.8 -c 3

CNI 问题排查

# 查看 CNI 配置
ls /etc/cni/net.d/
cat /etc/cni/net.d/10-flannel.conflist  # Flannel
cat /etc/cni/net.d/10-calico.conflist   # Calico

# Flannel 问题排查
kubectl get pods -n kube-flannel -o wide   # 或 kube-system
kubectl logs -n kube-flannel -l app=flannel --tail=50

# 检查 Flannel 路由(每个节点应该有到其他节点 Pod CIDR 的路由)
ip route | grep 10.244
# 10.244.0.0/24 dev cni0 proto kernel scope link  ← 本节点
# 10.244.1.0/24 via 192.168.1.102 dev eth0 onlink ← 到其他节点
# 如果缺少某个节点的路由,说明 Flannel 没有同步

# Calico 问题排查
kubectl get pods -n kube-system -l k8s-app=calico-node
kubectl logs -n kube-system -l k8s-app=calico-node --tail=50

# Calico 工具(在 calico-node Pod 内)
kubectl exec -n kube-system -it <calico-node-pod> -- calicoctl get ipPool
kubectl exec -n kube-system -it <calico-node-pod> -- calicoctl node status

# Cilium 问题排查
kubectl get pods -n kube-system -l k8s-app=cilium
cilium status --verbose
cilium connectivity test   # 全面连通性测试(需要 cilium CLI)

四、常见故障场景速查

场景 1:Pod 内无法访问外部域名

# 现象:curl baidu.com 超时,curl 1.1.1.1 成功

# 1. 先测试 DNS 是否工作
nslookup baidu.com
# 如果超时:CoreDNS 故障或 DNS 规则问题

# 2. 检查 CoreDNS 是否运行
kubectl get pods -n kube-system -l k8s-app=kube-dns

# 3. 检查节点 DNS 转发配置
# CoreDNS forward 配置是否指向正确的上游 DNS
kubectl get configmap coredns -n kube-system -o yaml | grep forward

# 4. 检查节点本身是否能解析
ssh node-01 'nslookup baidu.com'
# 如果节点也失败:节点 /etc/resolv.conf 配置问题

# 5. NetworkPolicy 是否阻断了出口 DNS (UDP 53)
kubectl get networkpolicy -A

场景 2:Service 偶发 502/连接超时

# 现象:访问 Service 大部分时间正常,偶发 502 或连接失败

# 1. 检查是否有 Pod 频繁重启
kubectl get pods -n production -w
kubectl describe pod <pod-name> | grep -A 5 "Conditions\|Events"

# 2. Endpoint 是否有变化(Pod 重启时 Endpoint 会短暂移除)
kubectl get endpoints my-service -n production -w

# 3. 检查 Graceful Shutdown(Pod 停止时是否给正在处理的请求时间完成)
# 在 Deployment spec 中添加:
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60   # 给 60 秒完成未处理请求
      containers:
      - name: app
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 10"]   # 等 iptables 更新

场景 3:跨命名空间服务发现失败

# 现象:curl my-service 成功,curl my-service.other-ns 失败

# 验证正确的完整域名格式:
# <service-name>.<namespace>.svc.<cluster-domain>
nslookup my-service.production.svc.cluster.local

# 检查目标 Service 是否有 Endpoints
kubectl get endpoints my-service -n production

# 检查 NetworkPolicy 是否阻断跨命名空间流量
kubectl get networkpolicy -n production

# 验证 RBAC 是否影响(不太可能,但检查一下)
kubectl auth can-i get services -n production --as=system:serviceaccount:dev:default

一行诊断命令速查

# CoreDNS 状态
kubectl get pods,svc -n kube-system -l k8s-app=kube-dns

# Service Endpoints
kubectl get ep -A | grep -v "<none>"

# Pod 网络命名空间
kubectl exec -it <pod> -- ip addr; ip route; cat /etc/resolv.conf

# 节点 CNI 日志
journalctl -u kubelet --since "10m ago" | grep -i "cni\|network"

# 抓取 DNS 请求(节点上,适合诊断 DNS 慢)
tcpdump -i any port 53 -n -w /tmp/dns.pcap

# kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=100 | grep -i error

# 测试所有 CoreDNS 副本(排查单副本问题)
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide | \
  awk 'NR>1 {print $6}' | \
  while read ip; do
    echo "Testing CoreDNS $ip:"
    dig @$ip kubernetes.default.svc.cluster.local +short
  done

小结

K8s 网络故障排查的黄金法则:分层隔离,逐层验证。先确认 Pod IP 直连是否通(应用层问题还是网络问题)→ 再确认 Service ClusterIP 是否通(iptables/eBPF 问题)→ 再确认 DNS 解析是否正确(CoreDNS 问题)→ 最后看 CNI 层(节点路由/封包问题)。每一层都有对应的工具:kubectl exec + curl/ping 在容器内测试,tcpdump 在节点上抓包,iptables -t nat -L 查看 Service 转发规则,CNI 自带工具(calicoctl/cilium)查看网络策略。