公司部署K8s后Pod跨节点无法通信Service找不到后端怎么办一文讲清CNI插件IP分配Service DNS与NetworkPolicy核心原理让6岁孩子也能看懂Kubernetes网络模型
一、先别急着骂街,咱们先聊聊你遇到了什么
上周二凌晨三点,我收到了运维老张的求助消息。他的眼神告诉我,这次又出大事了。
“K8s集群跑起来了,但是两个节点上的Pod连不上,Service也找不到后端。”
我一边吃着泡面一边问他:”你用的是哪个CNI插件?Flannel、Calico还是其他?”
他说:”Calico,部署在两个节点上,IP池也配了,NetworkPolicy也没开。”
我沉默了三秒钟,然后开始问了一系列问题。
因为K8s网络这个问题,就像是你家里装修,水电都通了,但是水龙头出来的却是空气。你得一个个检查,从源头到末端。
今天这篇文章,我要把K8s网络模型从里到外拆给你看,让你不仅知道怎么修,还知道为什么这样修。
二、先让6岁的小朋友能听懂:什么是K8s网络模型
想象一下,你是一家幼儿园的园长。
幼儿园里有几十个小朋友,每个小朋友都有自己的座位(这个座位就是Pod)。幼儿园分布在两栋楼里(这两个楼栋就是节点/Node)。
现在问题来了:
- 一楼的明明想找二楼的丽丽玩游戏,但是找不到她在哪里。
- 楼里的广播说”丽丽在3号房间”,但是明明跑到3号房间,发现里面没有人。
这就是你遇到的两个典型问题:
问题1:跨节点Pod无法通信 —— 明明在节点A,丽丽在节点B,但是明明喊丽丽,丽丽听不见。
问题2:Service找不到后端 —— 广播说丽丽在某个房间,但是那个房间是空的。
那幼儿园管理员是怎么解决这个问题呢?这就是CNI插件要干的活。
三、CNI插件到底是什么?它是怎么给Pod分配IP的
3.1 先搞清楚:为什么Pod需要IP?
每个Pod在K8s里都有自己独立的IP地址。这个IP不是虚拟的,而是真实的、可以在网络里路由的IP。
但是问题来了:K8s怎么知道给这个Pod分配哪个IP?
这就是CNI插件的工作。
3.2 CNI插件的工作流程
当你创建一个Pod的时候,K8s会触发下面这个过程:
Pod创建
↓
Kubelet发现新Pod
↓
调用CNI插件(根据配置)
↓
CNI插件为Pod创建网络命名空间
↓
CNI插件给Pod分配一个IP地址
↓
CNI插件配置路由,让Pod能通信
↓
Pod启动,拥有独立IP
3.3 不同CNI插件的区别
常见的CNI插件有:
| CNI插件 | IP分配方式 | 跨节点通信方式 | 特点 |
|---|---|---|---|
| Flannel | 给每个节点分配一个子网 | VXLAN隧道封装 | 简单,适合小集群 |
| Calico | 给每个Pod分配独立IP | BGP路由或IPIP封装 | 灵活,支持NetworkPolicy |
| ** Cilium** | 基于eBPF | 直接路由或VXLAN | 高性能,安全策略强大 |
老张用的是Calico,那Calico是怎么给Pod分配IP的呢?
Calico工作原理:
1. Calico Node运行在每个节点上
2. 它从IP池中分配一个IP给新创建的Pod
3. 通过BGP协议把路由信息告诉其他节点
4. 其他节点收到路由后,知道怎么找到这个Pod
3.4 为什么Pod跨节点无法通信?
这里有个关键概念:Pod网络必须跨节点可达。
如果你的集群有两个节点:
- 节点A的Pod IP是
10.244.1.2 - 节点B的Pod IP是
10.244.2.3
那节点A必须知道怎么去 10.244.2.3,节点B也必须知道怎么去 10.244.1.2。
如果路由没配好,两个Pod就永远找不到对方。
常见原因:
原因1:CNI插件没装好
原因2:IP池配置冲突
原因3:节点之间网络不通(防火墙、安全组)
原因4:BGP邻居没建立(Calico场景)
原因5:VXLAN隧道没起来(Flannel场景)
四、Service是什么?它是怎么找到后端的
4.1 Service的基本概念
想象你在幼儿园,你想找”小红”玩。
但是幼儿园有100个小朋友,其中有3个叫”小红”。你不能一个一个找,对吧?
所以幼儿园有一个通讯录,上面写着:
“找小红 → 她去3号房间、7号房间、或者12号房间”
这个通讯录就是K8s里的Service。
- Service有一个虚拟IP(ClusterIP),比如
10.96.0.10 - Service后面挂着真实的Pod,这些Pod可能是1个、2个、或者10个
- 当你访问
10.96.0.10的时候,K8s会自动帮你转发到某个真实的Pod
4.2 Service是怎么找到后端的?
这里涉及到一个核心机制:kube-proxy。
kube-proxy运行在每个节点上,它负责维护Service到Pod的映射关系。
Service创建
↓
apiserver记录Service和Pod的对应关系
↓
kube-proxy监听apiserver变化
↓
kube-proxy更新iptables规则(或ipvs规则)
↓
流量来到节点时,被正确转发到后端Pod
但是! 如果kube-proxy出了问题,或者iptables规则没更新成功,Service就找不到后端了。
4.3 常见排查步骤
# 1. 检查Service是否存在
kubectl get svc <service-name> -n <namespace>
# 2. 检查Service的endpoints
kubectl get endpoints <service-name> -n <namespace>
# 3. 检查endpoints有没有后端Pod
kubectl describe endpoints <service-name> -n <namespace>
# 4. 检查kube-proxy是否正常运行
kubectl get pods -n kube-system | grep kube-proxy
kubectl logs -n kube-system <kube-proxy-pod-name>
# 5. 检查iptables规则(如果是iptables模式)
iptables -t nat -L -n -v | grep <service-cluster-ip>
老张遇到的问题:他的endpoints是空的,也就是说,Service找不到任何Pod。
为什么找不到?因为标签选择器不匹配。
# Service的定义
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app # ← 这里选择标签是 my-app 的Pod
ports:
- port: 80
# Pod的定义
apiVersion: v1
kind: Pod
metadata:
labels:
app: my-application # ← 这里写的是 my-application,不是 my-app!
spec:
containers:
- name: app
image: my-app
看到了吗?Service的selector是 app: my-app,但是Pod的label是 app: my-application,差了一个字母,所以永远匹配不上。
五、DNS在K8s里是怎么工作的
5.1 CoreDNS是什么?
在K8s里,Service不是通过IP来访问的,而是通过域名。
比如你有一个Service叫 my-service,在 default 命名空间里,你可以这样访问它:
my-service.default.svc.cluster.local
这个域名是谁负责解析的呢?是CoreDNS(或者以前用的kube-dns)。
5.2 DNS解析流程
Pod A 要访问 Service B
↓
Pod A 查询 DNS:B-service.default.svc.cluster.local
↓
请求发送到 CoreDNS(通常是 ClusterIP 形式)
↓
CoreDNS 返回 Service B 的 ClusterIP
↓
Pod A 用 ClusterIP 访问 Service B
↓
kube-proxy 把流量转发到后端 Pod
5.3 DNS解析失败怎么办?
# 1. 检查CoreDNS是否运行
kubectl get pods -n kube-system | grep coredns
# 2. 检查CoreDNS的日志
kubectl logs -n kube-system <coredns-pod-name>
# 3. 在Pod里测试DNS解析
kubectl run test -it --rm --image=busybox -- sh
# 进入后执行:
nslookup my-service.default.svc.cluster.local
如果DNS解析失败,常见原因:
原因1:CoreDNS Pod没有启动成功
原因2:Pod的DNS配置有问题(/etc/resolv.conf)
原因3:网络插件不支持DNS查询
原因4:CoreDNS的ConfigMap配置错误
六、NetworkPolicy是什么?它为什么会阻断通信
6.1 NetworkPolicy的基本概念
NetworkPolicy是K8s里的网络安全策略。
它的作用是:控制哪些Pod可以通信,哪些不能。
没有NetworkPolicy的时候,默认所有Pod都可以互相通信。
开启了NetworkPolicy之后,你必须明确告诉K8s:谁可以访问谁。
6.2 为什么NetworkPolicy会导致通信失败?
这是最常见的问题之一。
比如你定义了这样一条规则:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
这条规则的意思是:default命名空间下,所有Pod默认拒绝所有入站流量。
然后你心想:”我再加一条规则,允许某些Pod通信。”
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-my-app
namespace: default
spec:
podSelector:
matchLabels:
app: my-app
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 80
这条规则的意思是:只有标签是 app:frontend 的Pod,才能访问标签是 app:my-app 的Pod的80端口。
6.3 常见错误
# 错误示例:from写成了podSelector但没写matchLabels
spec:
ingress:
- from:
- podSelector: {} # ← 这个意思是允许所有Pod,但如果你前面有deny-all,这里需要明确
老张的NetworkPolicy问题:他开启了一个Policy,但是没有正确配置allow规则,导致所有流量被阻断。
排查NetworkPolicy:
# 查看命名空间下的NetworkPolicy
kubectl get networkpolicy -n <namespace>
# 查看Policy详情
kubectl describe networkpolicy <policy-name> -n <namespace>
七、实战排查:老张的问题是怎么解决的
7.1 问题复现
老张的集群有两个节点,部署了一个应用:
Node-1: Pod-A (10.244.1.2)
Node-2: Pod-B (10.244.2.3)
Service: my-service (10.96.0.10)
问题:
- Pod-A 无法访问 Pod-B
- Service 找不到后端
7.2 排查过程
# 第一步:检查CNI插件状态
kubectl get pods -n kube-system | grep calico
# 发现 calico-node 在两个节点上都运行正常
# 第二步:检查Pod IP分配
kubectl get pods -o wide
# Pod-A: 10.244.1.2 (Node-1)
# Pod-B: 10.244.2.3 (Node-2)
# IP看起来正常
# 第三步:测试跨节点通信
kubectl exec Pod-A -- ping 10.244.2.3
# 超时!无法连通
# 第四步:检查路由表
kubectl exec Pod-A -- ip route
# 发现路由指向 calico0 接口,但没有跨节点的路由
# 第五步:检查BGP邻居
kubectl exec calico-node-on-Node-1 -- calico-bgpctl peers
# 没有BGP邻居!
# 原因找到了:BGP邻居没有建立
7.3 解决方案
# 检查Calico的配置
kubectl get felixconfigurations.crd.projectcalico.org default -o yaml
# 检查BGP配置
kubectl get bgppeers -A
# 发现问题:Node-2的BGP配置缺失
# 解决方案:重新部署Calico,确保所有节点都正确配置
kubectl delete -f calico.yaml
kubectl apply -f calico.yaml
# 重新检查
kubectl get bgppeers -A
# 现在BGP邻居建立了
# 再次测试
kubectl exec Pod-A -- ping 10.244.2.3
# 通了!
7.4 Service问题排查
# 检查Service的endpoints
kubectl get endpoints my-service
# 显示:无后端
# 检查Service的selector
kubectl describe svc my-service
# selector: app: web-app
# 检查Pod的标签
kubectl get pods --show-labels | grep web-app
# 没有匹配的Pod!
# 发现问题:Pod的标签写错了
# 解决方案:修正Pod的标签
kubectl label pod <pod-name> app=web-app --overwrite
# 再次检查
kubectl get endpoints my-service
# 现在显示了后端Pod的IP
八、总结:K8s网络模型的核心要点
让我用一张图来总结:
┌─────────────────────────────────────────────────────────────┐
│ K8s网络模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Pod-A │ │ Pod-B │ │ Pod-C │ │
│ │ 10.244.1.2│ │10.244.2.3│ │10.244.3.4│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ CNI插件 │ ← IP分配、路由配置 │
│ │ (Calico/Flannel)│ │
│ └────────┬────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌──────▼─────┐ ┌────▼────┐ │
│ │Node-1 │ │ Node-2 │ │Node-3 │ │
│ │(物理网络)│ │ (物理网络) │ │(物理网络)│ │
│ └─────────┘ └────────────┘ └─────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ kube-proxy │ ← Service流量转发 │
│ │ (iptables/ │ │
│ │ ipvs) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Service │ ← 虚拟IP、负载均衡 │
│ │ 10.96.0.10 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ CoreDNS │ ← 域名解析 │
│ │ my-svc.svc.域名 │ │
│ └─────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ NetworkPolicy │ ← 安全策略(可选) │
│ │ 控制谁可以通信 │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
核心要点:
- CNI插件负责给Pod分配IP和配置路由
- kube-proxy负责Service到Pod的流量转发
- CoreDNS负责域名解析
- NetworkPolicy负责安全策略(不配置则默认允许所有)
排查口诀:
Pod跨节点不通 → 检查CNI插件状态和BGP邻居
Service找不到后端 → 检查endpoints和label匹配
DNS解析失败 → 检查CoreDNS Pod和ConfigMap
NetworkPolicy阻断 → 检查policy配置和selector
九、给新手的建议
如果你刚接触K8s网络,我给你几个建议:
- 从小集群开始:先用两个节点的集群,熟悉网络模型
- 不要用太复杂的CNI:Flannel适合入门,Calico适合生产
- NetworkPolicy谨慎使用:先让它跑通,再考虑安全策略
- 善用kubectl:
kubectl get pods -o wide、kubectl describe是最好的朋友 - 理解原理比记住命令更重要:知道为什么,比知道怎么做更重要
十、最后的絮叨
写这篇文章的时候,我想起刚学K8s的那个下午。
我对着两个Pod之间的ping命令发呆,不知道为什么就是不通。后来问了很多人,看了很多文档,才慢慢理解了CNI、Service、DNS之间的关系。
现在回头看,K8s网络其实没那么难。它就是一个分配IP、配置路由、转发流量、解析域名的过程。
你遇到的问题,十有八九有人遇到过。只是你需要知道去哪里找答案,以及怎么理解那个答案。
希望这篇文章能帮你少走一点弯路。
如果还有问题,欢迎在评论区问我。虽然我不一定能秒回,但我会认真看的。
这篇文章写于一个普通的晚上,泡面已经凉了,但问题解决了,心是热的。
