想象一下,你正在教孩子搭建一个乐高城堡。在Kubernetes(简称K8s)的世界里,每一个Pod就像是一个小小的乐高积木块,它们需要在一张巨大的、动态变化的网络地图上找到自己的位置,并且还能和其他积木块顺利“对话”。很多孩子(甚至大人)在这里卡住了,不是代码写错了,而是搞不清楚这些积木之间是怎么连线的。
别急,今天我们就把这个看似高大上的K8s网络,拆解成几个你能亲手摸到的“大坑”,并带着你一起把坑填平。我们不说虚的理论,直接看那些让人头秃的真实场景。
第一关:为什么我的Pod IP总是“撞车”?
在K8s里,每个Pod都有一个独一无二的IP地址,这叫Pod IP。理论上,这应该像每个人的身份证号一样唯一。但在实际动手时,很多孩子会发现:诶?我的Pod启动失败,日志里写着“IP conflict”(IP冲突)?这是怎么回事?
真相:多网卡与CNI插件的“锅”
想象你家有两条宽带,一条给电脑,一条给游戏机,结果它们都用同一个电话号码,运营商就懵了。在K8s节点(Node)上,情况类似。
当一个Pod被调度到某个节点时,K8s的网络插件(CNI,Container Network Interface)需要给这个Pod创建一根“虚拟网线”(veth pair),一端插在Pod里,另一端插在节点的内核网络命名空间里。如果CNI插件配置不当,或者你手动操作过节点的网络,可能会导致:
- 同一节点上的Pod使用了相同的IP段:有些实验性的CNI配置,或者错误的flannel/calico参数,可能导致不同Pod被分配了同一个IP。
- 节点本身已有IP占用:你的Pod IP网段和宿主机的eth0 IP网段重叠了。比如,你的Pod网段是10.244.1.0/24,但你的节点IP恰好也是10.244.1.5,那这个Pod就永远起不来。
实战排雷:如何找到“撞车”的元凶?
假设你有一个Pod一直Pending,查看事件:
kubectl describe pod <pod-name> -n <namespace>
你会看到类似这样的错误:
Warning FailedCreatePodSandBox 5s kubelet, node01 Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "abc123": plugin type="flannel" name="cni0" failed (add): IP conflict on interface eth0
排查步骤:
检查节点IP和Pod网段是否重叠: 登录到报错的节点(node01),运行:
ip addr show eth0 # 假设输出:inet 192.168.1.100/24然后检查你的CNI配置(通常在/etc/cni/net.d/下的json文件):
cat /etc/cni/net.d/10-flannel.conflist看里面的
subnet字段,比如10.244.0.0/16。确保192.168.1.0/24和10.244.0.0/16没有重叠。如果重叠了,你需要修改CNI配置,重启kubelet,或者给节点换一个新的、不冲突的IP。检查是否有残留的网络接口: 有时候,旧的Pod清理不干净,留下了僵尸的veth接口,占用了IP。
# 在节点上查看 ip link show | grep veth如果发现大量
vethxxx状态为DOWN,可以尝试清理,或者重启节点服务:systemctl restart kubelet
给孩子的比喻:就像你和同桌两个人都用了“张三”这个名字,老师点名时就会乱套。K8s要求每个Pod的IP在全局唯一,如果节点IP和Pod IP“同名”,就得改名。
第二关:Service ClusterIP通了,但Pod就是访问不到?
这是最常见的坑。你创建了一个Service,分配了一个ClusterIP(比如10.96.0.100),你在另一个Pod里用curl http://10.96.0.100,结果timeout了。为什么?
真相:iptables/ipvs规则没生效,或Endpoints为空
K8s的Service本质是一套iptables规则(或ipvs规则),它把对ClusterIP的请求,转发到后端Pod的IP和端口上。如果这个转发规则没建立,或者后端没有健康的Pod,请求就会石沉大海。
实战排雷:分层检查
第一步:检查Endpoints是否存在
kubectl get ep <service-name> -n <namespace>
如果输出是空的,或者ENDPOINTS列是<none>,说明Service没有绑定到任何Pod。为什么?
Selector不匹配:检查你的Service的
selector和Pod的labels是否完全一致。这是90%的原因! “`yamlService
spec: selector:
app: my-web # 必须和Pod的label一样ports:
- port: 80Pod
metadata: labels:
app: my-web # 差一个字母都不行!”`
Pod状态不是Running:如果Pod处于
ContainerCreating或Error状态,它不会被加入Endpoints。kubectl get pods -n <namespace> -l app=my-web
第二步:在节点上检查iptables规则
如果Endpoints有值,但还访问不通,可能是节点上的iptables规则丢了。登录到任意一个节点,运行:
sudo iptables -t nat -S | grep <ClusterIP>
你应该能看到类似这样的规则:
-N KUBE-SERVICES
-A PREROUTING -j KUBE-SERVICES
-A KUBE-SERVICES -d 10.96.0.100/32 -p tcp -m comment --comment "default/web-service:http" -m tcp --dport 80 -j KUBE-SVC-XXXX
-A KUBE-SVC-XXXX -m statistic --mode random --probability 0.33333 -j KUBE-SEP-YYYY
-A KUBE-SEP-YYYY -s 10.244.1.5/32 -m comment --comment "default/web-service:http" -j KUBE-MARK-MASQ
-A KUBE-SEP-YYYY -p tcp -m comment --comment "default/web-service:http" -m tcp -j DNAT --to-destination 10.244.1.5:80
如果找不到这些规则,说明CNI插件(如flannel, calico)的kube-proxy没有正常工作。检查kube-proxy的日志:
kubectl logs -n kube-system kube-proxy-<node-name>
给孩子的比喻:Service就像一个总机号码(ClusterIP),它需要一张“员工分机表”(Endpoints)。如果总机查不到任何分机,或者查到了但员工电话没接(Pod未Ready),你就打不通。iptables规则就是总机内部的接线员,如果接线员下班了(kube-proxy故障),总机也就失效了。
第三关:跨节点Pod通信失败,防火墙在捣鬼?
同一个节点内的Pod通信通常没问题,但一旦跨节点,比如Node1的Pod要访问Node2的Pod,就突然不通了。这时候,十有八九是底层网络的Flannel/VXLAN隧道没建立,或者防火墙拦截了。
真相:VXLAN接口未UP,或防火墙阻断了UDP 8472端口
K8s默认使用overlay网络(如VXLAN)来跨节点通信。Node1的Pod发数据包给Node2的Pod,数据包会被封装在VXLAN帧里,通过节点的物理网卡(eth0)发送,目标端口通常是UDP 8472。如果Node2的防火墙挡住了这个端口,或者VXLAN接口(vxlan.1)没起来,数据包就进不去。
实战排雷:
第一步:检查VXLAN接口状态
登录到两个节点,运行:
ip link show flannel.1
# 或者 ip link show vxlan-1
状态应该是UP。如果状态是DOWN,尝试重启CNI服务:
systemctl restart flannel
# 或者重启kubelet
systemctl restart kubelet
第二步:检查防火墙规则
这是重灾区!很多云厂商的默认安全组,或者物理机的firewalld/iptables规则,会阻止非标准端口的通信。
在Node2上,检查是否允许UDP 8472入站:
sudo iptables -L -n | grep 8472
如果没有允许的规则,你需要添加。以firewalld为例:
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --reload
以iptables为例:
sudo iptables -A INPUT -p udp --dport 8472 -j ACCEPT
sudo iptables-save
第三步:ping测试底层隧道
在Node1上,尝试ping Node2的VXLAN接口IP(不是Pod IP,是节点上的flannel.1的IP,通常是10.244.x.0):
# 在Node1上
ping 10.244.2.0 # 假设这是Node2的flannel网段网关
如果ping不通,说明底层节点网络就不通,问题在网络层,而不是K8s层。检查节点间的物理网络连通性。
给孩子的比喻:跨节点通信就像两个城市之间要修一条高速公路(VXLAN隧道)。如果高速公路的入口(节点网络接口)没开,或者过路费检查站(防火墙)不让货车(UDP 8472数据包)通过,货物(数据包)就运不过去。
第四关:ExternalName服务解析失败?
有些孩子喜欢用ExternalName类型的Service来访问外部服务,比如把内部的db.example.com指向外部的actual-db.mysql.svc.cluster.local。结果发现nslookup db.example.com返回错误。
真相:CoreDNS插件未配置ExternalName,或DNS缓存
第一步:检查CoreDNS配置
现代K8s使用CoreDNS作为DNS服务。确保你的CoreDNS ConfigMap里没有禁用ExternalName。通常默认是支持的。检查:
kubectl get configmap coredns -n kube-system -o yaml
看plugins部分,确保有kubernetes插件。
第二步:验证解析
kubectl run -it --rm dns-test --image=busybox:latest --restart=Never -- nslookup db.example.com
如果返回server can't find,检查Service是否正确创建:
kubectl get svc db.example.com -n <namespace>
给孩子的比喻:ExternalName就像一个“假电话号码簿”,它不指向真正的房间(Pod),而是指向另一个名字。如果电话号码簿(DNS)坏了或者没更新,你就找不到这个假名字。
总结:一张排错地图
当孩子遇到K8s网络问题时,不要慌,按照这个顺序来:
- 看Pod状态:是Pending?Error?还是Running?
kubectl get pods - 看Pod日志:
kubectl logs <pod-name> - 看事件:
kubectl describe pod <pod-name> - 看Service和Endpoints:
kubectl get svc和kubectl get ep - 看节点网络:登录节点,检查iptables、VXLAN接口、防火墙。
- 看CNI插件日志:
kubectl logs -n kube-system <cni-pod>
记住,K8s网络虽然复杂,但它是由一个个简单的组件(Pod、Service、Node、CNI)组成的。把大问题拆成小问题,逐个验证,你就能成为网络排雷专家!
希望这篇指南能帮你和孩子一起,在K8s的网络迷宫中找到出口。下次再遇到IP冲突或路由失败,别怕,拿出我们的“排雷地图”,一步步来!
