Kubernetes CNI 网络排障:Calico 实战
Kubernetes 网络出问题时,应用日志往往只写“连接超时”,根因却藏在 CNI、Service、kube-proxy 或 NetworkPolicy 中的某一层。Calico 是最常用的 CNI 之一,本文按三层定位法排查:Pod 内网络是否正常、Service 是否可达、跨节点是否通,并覆盖 BGP、IP 池与 NetworkPolicy 三类高频故障。
定位前先确认 Calico 自身健康:calico-node 是否 Running、calico-kube-controllers 是否正常、Felix 是否在同步策略。CNI 自己不健康时,查应用网络没有意义。
一、先看 Calico 自身是否健康
kubectl -n kube-system get pods -l k8s-app=calico-node -o wide
kubectl -n kube-system get pods -l k8s-app=calico-kube-controllers
kubectl -n kube-system logs -l k8s-app=calico-node --tail=50 | grep -i -E 'error|failed|bird' | tail -20
# Calico 命令行工具(需先安装 calicoctl)
calicoctl node status
calicoctl get ippools -o wide
calicoctl get felixconfiguration -o yaml | grep -E 'ipip|vxlan|logSeverity'
重点看三项:calico-node 在每个节点上都是 Running、calicoctl node status 显示 BGP 会话为 Established、IP 池没有耗尽。BGP 会话没建立时跨节点 Pod 必然不通,先修 BGP 再查别的。
二、Pod 内网络:先确认 Pod 自己的网络栈
Pod 不通先分内外:进 Pod 里 ping 网关、ping 同节点 Pod、ping 跨节点 Pod,三步定位故障在哪一跳。能通网关不通跨节点,问题在节点间路由或隧道;连网关都不通,问题在 Pod 的 veth 或 CNI 分配。
<pre class="wp-block-syntaxhighlighter-code"># 进 Pod 排查(以调试镜像为例)
kubectl run netdebug --rm -it --image=nicolaka/netshoot -- /bin/bash
# Pod 内执行:
ip addr | grep -A2 eth0
ip route | head
ping -c3 10.244.0.1
ping -c3 <同节点 Pod IP>
ping -c3 <跨节点 Pod IP>
# 节点上看 Pod 的 veth 对是否正常
kubectl get pod <pod> -o jsonpath='{.spec.nodeName} {.status.podIP}'
ip link | grep -i cali | head</pre>
若 Pod 没有拿到 IP 或 IP 不在 IP 池范围内,查 calico-node 日志里的 IPAM 分配错误;若 veth 对缺失,重启 calico-node 让 CNI 重新下发配置,重启前先确认该节点上其他 Pod 是否正常,避免扩大影响。
三、Service 不通:先分清 ClusterIP、NodePort 与 DNS
Service 不通要先定性:是 DNS 解析不到 Service 名,还是 ClusterIP 不通,还是 NodePort 不通。三者根因完全不同:DNS 问题查 CoreDNS,ClusterIP 问题查 kube-proxy 的 iptables/IPVS 规则,NodePort 问题查节点防火墙与端口监听。
<pre class="wp-block-syntaxhighlighter-code"># DNS 是否正常
kubectl run netdebug --rm -it --image=nicolaka/netshoot -- nslookup kubernetes.default
kubectl -n kube-system get pods -l k8s-app=kube-dns
# ClusterIP 是否可达(Pod 内)
kubectl run netdebug --rm -it --image=nicolaka/netshoot -- curl -v http://<service-cluster-ip>:<port> --max-time 5
# kube-proxy 规则是否下发
iptables -t nat -L KUBE-SERVICES -n | grep <service-cluster-ip>
ipvsadm -Ln | grep <service-cluster-ip>
kubectl get endpoints <service> -o wide</pre>
Endpoints 为空是最常见的 Service 不通原因:Service 的 selector 没匹配到任何 Pod,或 Pod 的 readinessProbe 一直失败。先修 Endpoints,再查网络。
四、跨节点不通:BGP、隧道与 IP 池三件套
同节点通、跨节点不通,基本锁定在节点间路由:Calico 默认用 BGP 直连(无封装),节点间需要三层可达;若用了 IPIP 或 VXLAN 封装,还要确认隧道端口与 MTU。IP 池耗尽时新 Pod 拿不到 IP,表现为 Pod 一直 Pending 或 CrashLoopBackOff。
# BGP 会话状态(每个节点都应与其它节点 Established)
calicoctl node status
# 节点间路由是否学到
ip route | grep -E '10\.244|bird' | head -20
# 隧道模式检查:IPIP 协议号 4、VXLAN 端口 4789 是否被防火墙放行
iptables -L -n | grep -E '4789|ipip'
# IP 池使用率
calicoctl get ippools -o yaml | grep -E 'cidr|blockSize'
kubectl get ipamblocks -o wide 2>/dev/null | head
云上部署要特别注意:BGP 直连模式要求节点间三层互通,某些云厂商的 VPC 默认不转发 Pod 网段,需要改用 IPIP 或 VXLAN 封装模式。改封装模式要重启 calico-node,放在维护窗口做。
五、NetworkPolicy 把流量拦住了:最容易误判的一层
网络策略出问题时现象是“突然不通”,且只影响部分 Pod。排查方法:先看 Pod 所在命名空间有没有 NetworkPolicy,再用 calicoctl get networkpolicy 看规则,最后临时放宽策略验证——确认是策略问题后再收紧,不要直接删策略上线。
# 列出影响该 Pod 的策略
kubectl get networkpolicy -A
calicoctl get networkpolicy -A -o wide
calicoctl get globalnetworkpolicy -o wide
# 看 Felix 是否正常下发策略(策略数异常为 0 说明同步有问题)
calicoctl node status
kubectl -n kube-system logs -l k8s-app=calico-node --tail=100 | grep -i policy | tail -20
NetworkPolicy 默认是“白名单”语义:只要 Pod 被某个策略选中、且策略没有放行某条流量,这条流量就会被丢弃。排障时先确认策略的 podSelector 是否误选中了目标 Pod,再看 ingress/egress 规则是否漏了 DNS(53 端口)——漏 DNS 会让所有域名解析失败,现象像“全网不通”。
六、DNS 链路专项:域名不通先查 DNS,不要先查路由
Pod 里域名解析失败时,现象经常被误判成“网络不通”:curl 报 Could not resolve host,但 IP 直连是通的。DNS 链路有三段——Pod 的 resolv.conf、CoreDNS、以及到上游的解析,逐段验证才能定位。ndots 设置不当还会让短域名解析走一圈无用的搜索域,表现为解析慢而不是解析失败。
<pre class="wp-block-syntaxhighlighter-code"># 1. 看 Pod 的 DNS 配置(重点看 ndots 与搜索域)
kubectl exec -it <pod> -- cat /etc/resolv.conf
# 2. 在 Pod 内直接测 CoreDNS(10.96.0.10 为示例 ClusterIP,以实际为准)
kubectl exec -it <pod> -- nslookup kubernetes.default.svc.cluster.local 10.96.0.10
kubectl exec -it <pod> -- nslookup www.example.com
# 3. 看 CoreDNS 自身是否健康
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50 | grep -i error
# 4. 查 conntrack 里的 DNS 会话是否被丢(UDP 无连接,表满时先丢 DNS)
conntrack -L -p udp --dport 53 | head
# 5. 短域名解析慢:检查 ndots,业务域名可改用 FQDN 绕过搜索域
kubectl exec -it <pod> -- getent hosts myservice.default.svc.cluster.local</pre>
DNS 问题的常见根因排序:CoreDNS 副本数不足或被限流、NetworkPolicy 漏放 53 端口、conntrack 表满先丢 UDP、ndots 导致搜索域放大。修完后用 nslookup 在故障 Pod 内实测,不要只在节点上测——节点和 Pod 走的不是同一条 DNS 路径。
七、MTU 与分片:大包不通、小包正常时先怀疑 MTU
Calico 的 IPIP 或 VXLAN 隧道会在原始报文外再包一层,留给业务的 MTU 会比网卡 MTU 小几十字节。小包正常、大文件传输或大包 ping 失败,就是典型的 MTU 不匹配。更隐蔽的是对端禁了 ICMP 之后,分片需要的路径 MTU 发现失效,大包直接黑洞,连报错都没有。
<pre class="wp-block-syntaxhighlighter-code"># 看各层 MTU:网卡、隧道接口、Pod 内网卡
ip link | grep -E 'mtu|vxlan|tunl'
kubectl exec -it <pod> -- ip link | grep eth0
# 用不分片 ping 探测路径 MTU(从 1400 开始往上试)
ping -M do -s 1400 -c 3 <对端 Pod IP>
ping -M do -s 1450 -c 3 <对端 Pod IP>
# 看 Calico 的 MTU 配置(IP 池与 Felix)
kubectl get ippool -o yaml | grep -i mtu
calicoctl get felixconfiguration default -o yaml | grep -i mtu
# 临时验证:把 Pod 网卡 MTU 调小,看大包是否恢复
# kubectl exec -it <pod> -- ip link set dev eth0 mtu 1400
# 修复方向:统一三层 MTU(网卡 ≥ 隧道 + 开销 ≥ Pod),或开 TCP MSS 钳制</pre>
MTU 修复要三层一起对:物理网卡 MTU、Calico 隧道开销、Pod 网卡 MTU,任何一层对不上都会复发。改完 IP 池的 MTU 后要重启受影响的 Pod 才能生效,只改配置不重启等于没改。跨云、跨机房的集群尤其要核对这三层,云厂商的网卡 MTU 未必是 1500。
八、升级与回滚:Calico 升级要逐节点,别一把梭
Calico 升级最怕的是“一把梭”:新版 calico-node 在所有节点同时重启,中间任何一个节点出问题,整个集群的 Pod 网络都会抖动。正确做法是先在测试集群验证、再逐节点灰度,每升级一个节点就验证该节点上的 Pod 互通,确认无误再继续。
<pre class="wp-block-syntaxhighlighter-code"># 升级前:记录当前版本与关键配置,先备份
kubectl -n kube-system get ds calico-node -o yaml > /tmp/calico-node-backup.yaml
calicoctl version
kubectl get ippool -o yaml > /tmp/ippool-backup.yaml
# 逐节点灰度:先排空一个节点,升级该节点的 calico-node,再放回
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
kubectl -n kube-system rollout restart ds/calico-node # 或按节点逐个操作
kubectl -n kube-system rollout status ds/calico-node
kubectl uncordon <node>
# 升级后验证:该节点 Pod 互通、BGP 状态、Felix 正常
kubectl -n kube-system exec -it <calico-node-pod> -- calicoctl node status
# 回滚:用备份的 YAML 恢复 DaemonSet,再逐节点重启
# kubectl apply -f /tmp/calico-node-backup.yaml
# kubectl -n kube-system rollout restart ds/calico-node</pre>
升级前要核对版本兼容矩阵:Calico 版本与 Kubernetes 版本、与 CNI 插件版本都要对上,跨大版本要按官方升级路径逐级升。升级窗口期要有人值守,BGP 会话重建期间跨节点流量会短暂抖动,提前和业务方打好招呼。升级完保留备份 YAML 至少一周,确认稳定后再清理。
九、排障检查清单
- 先看 Calico 自身:calico-node Running、BGP Established、IP 池未耗尽。
- 再分三层:Pod 内 → Service → 跨节点,每层用 ping/curl 定性,不要跳步。
- Service 不通先看 Endpoints 是否为空,再看 kube-proxy 规则。
- 跨节点不通先看 BGP 与路由,再看隧道与防火墙。
- 突然不通先看 NetworkPolicy,重点查是否漏放 DNS 与健康检查端口。
- 改动封装模式、IP 池、Felix 配置前,先在测试集群验证,生产放在维护窗口做。







