Kubernetes 网络故障排查:DNS 解析、Service 连通、CNI 深度调试
系统讲解 Kubernetes 网络故障排查方法:CoreDNS 解析失败定位、Service ClusterIP/NodePort/LoadBalancer 不通排查、Pod 间网络不通、CNI 插件问题调试,提供完整的诊断命令和排查思路,覆盖生产中 90% 的 K8s 网络故障场景。
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)查看网络策略。
