资讯动态

Kubernetes网络通信全解析:从数据链路到Flannel/Calico排障实战

发布时间:2026/10/21 21:26:36 来源:尧图企业网站定制
一直想写篇关于k8s网络通信的文章因为问我这个问题的人实在太多了。前阵子刚处理完一个典型故障服务A访问服务BDeployment全是Running日志里找不到任何报错但请求就是卡到超时最后定位下来是跨节点通信的报文在封装环节出了问题。这种“看起来全对实际不通”的体验几乎每个刚开始接触K8s的人都撞见过。这篇文章想把这套东西从头到尾理一遍从网络模型到底层数据链路再到Flannel、Calico这类主流方案的差异最后把我的排查套路和踩过的坑拿出来分享。适合两类人一是被k8s网络通信搞到头大的新手二是会部署但不会排障的运维同学。看完你至少能回答自己一个问题一个请求从浏览器到后端容器中间到底经过了哪些看不见的环节。1. 为什么k8s网络通信是初学者最先崩溃的一关1.1 传统网络经验为什么会失灵做传统运维的时候网络很好理解。服务器IP是固定的交换机路由表一配包从哪来、往哪去清清楚楚。排查问题也简单ping不通就逐跳看路由再不通就抓包整个链路是有形的。到了K8s里这套经验直接失效原因就三个Pod不是常驻的随时可能被重建重建后IP就变了Service对外暴露的是一个虚拟IP它不直接绑定到任何容器上网络访问不再靠某个静态路由表而是由网络插件动态维护。三个变化叠加的结果是原来“查路由表”能解决的问题现在必须先理解整个抽象层次。IP漂移、虚拟IP、动态路由、负载转发全混在一起。新手最常见的操作就是到处ping、到处看日志最后也没定位到问题在哪。这不是不努力而是脑子里没有数据链路图只能乱试。所以我的建议是学K8s网络第一步不是先装集群而是先把“从用户到Pod”这条链路画出来。1.2 先在心里画一张完整数据链路图一个典型的用户请求在K8s集群里大概要走这么几步用户请求 → Ingress或NodePort入口 → Service的ClusterIP → kube-proxy的转发规则 → 后端Pod的IP:Port → 容器内端口每一段都有自己的组件在负责。Ingress负责域名和七层路由Service负责抽象和负载均衡kube-proxy负责把虚拟IP转发到真实PodCNI插件负责让Pod真正拿到IP且互相可达。你排查时遇到的任何一个“网络不通”本质上都是这张链路图中某个环节断了。把这个链路背下来后面看什么都顺了。下面挨个拆这些环节。2. 三层网络模型Pod、Service、Node谁在跟谁通信2.1 网络模型的三条铁律K8s本身不实现网络功能它只是定义了一套规则真正干活的是CNI插件也就是Container Network Interface容器网络接口翻译成人话就是一把统一的插头标准。你的Pod、Service、节点怎么组网由选定的CNI插件决定。这套网络模型有三条铁律同一个Pod里的所有容器共享一个网络命名空间共享同一个IP、同一组端口连回环地址都是同一个任意两个Pod之间可以直接通信不需要NAT不管两个Pod在同一个节点还是不同节点通信效果必须一样。这三条决定了K8s网络的设计基调。你写yaml的时候感觉不到它们但每一条背后都有实在的机制在支撑。2.2 一个Pod是怎么拿到IP的要理解Pod的IP得先知道它的网络命名空间是怎么搭出来的。每个Pod启动时会先创建一个基础设施容器名字一般叫pause或者sandbox。这个容器先初始化一个独立的网络命名空间并且持有它。业务容器启动之后再加入这个网络命名空间。这么设计有个很实际的好处业务容器随便重启、崩溃、替换网络栈不重建Pod的IP保持不变。这也是为什么你经常看到“容器重启了好几次但Pod IP一直没变”的原因根本不是巧合是pause容器把网卡和IP攥在手里了。具体到一台节点上Pod启动后CNI插件会在Pod的网络命名空间里创建一张虚拟网卡veth另一头挂在宿主机的网桥上。于是Pod发出的包先到了宿主机网桥再根据宿主机的路由规则决定下一步走向哪里。2.3 同一个节点和跨节点数据走的完全是两条路同节点的两个Pod通信路径很短Pod A → 虚拟网卡 → 宿主机网桥 → 虚拟网卡 → Pod B不经过物理网卡所以特别快。这也是为什么同节点通信几乎不会出问题。跨节点通信才是重头戏。每个节点都会从Pod大网段里分到一个子网比如Pod网段是10.244.0.0/16那节点1可能分到10.244.1.0/24节点2分到10.244.2.0/24。节点上的路由表会写入类似于“访问10.244.2.0/24的包下一跳是节点2的物理IP”这样的规则。包从Pod A出来后通过虚拟网卡到本节点网桥一看目标IP网段不属于本节点就根据路由把包从物理网卡送出去。这里面最核心的部分是overlay封装。以VXLAN模式为例内层是完整的原始数据包源IP是Pod A目标IP是Pod B这个包整体被作为payload再套一层新的UDP头外层源IP是本节点外层目标IP是目的节点。包到达对端节点后解开外层UDP头还原出内层原始包再送入对端Pod。整个过程对两个Pod来说是完全透明的它们感知不到中间还有这么一层。用一个生活化的比喻两个不同园区的朋友寄文件各自把文件放进信封写上对方地址但你不会直接用这个信封去寄而是把信封再塞进一个快递包裹里填上对方所在园区的收发室地址。对方收发室收到包裹后拆开再把里面的信封送去给你朋友。快递包裹就是外层UDP封装里面的信封就是原始数据包。2.4 Service的虚拟IP到底虚在哪Service是一个很抽象的组件。它暴露一个ClusterIP但这个IP不是任何一张真实网卡你抓包时抓不到一个“属于Service”的包。真实转发靠的是kube-proxy。kube-proxy会监听Service和后端Pod的变化动态维护一套iptables规则或者IPVS规则。当数据包访问ClusterIP时iptables里的DNAT规则会把目标IP改写成一个真实后端Pod的IP然后继续走Pod间通信的链路。所以你可以把Service理解成一张“便利贴”它不干活干活的是拿着便利贴做转发的kube-proxy。不少初学者会在这个地方绕很久总觉得Service的IP应该像物理机的VIP一样可以ping通、可以做路由。其实没必要ClusterIP就只在K8s集群内部有效而且它存在的意义就是提供一个稳定的访问入口让你不用关心后端Pod怎么变。3. Flannel与Calico两种主流CNI的选型实测3.1 Flannel的VXLAN和host-gw两种后端Flannel是新手入门最常用的CNI部署简单一条kubectl apply就能搞定。它有两种常用的工作后端。VXLAN模式就是前面说的overlay封装方案数据包走UDP封装对底层网络要求宽松能跨三层网络工作只要节点之间UDP能通就行。代价是多了一层封装性能和同节点通信没法比而且每个包会多几十字节的额外开销。这个开销平时没感觉一旦MTU配置不对就容易出问题后面单独讲。host-gw模式则完全不加封装。它做的其实是静态路由管理每个节点相当于其他节点Pod网段的网关路由表直接写“目标网段 → 对端节点物理IP”。因为不封装性能好很多转发效率和传统路由基本持平。但它有个硬要求节点之间必须在同一个二层网络里网关都能直接到达否则路由写进去也白搭。3.2 Calico走的是纯三层路由而不是封装Calico的核心思路和Flannel完全不同。它不搞overlay而是让每个节点都扮演一个路由器用BGP协议把本节点的Pod网段路由通告给其他节点。数据包从一个节点到另一个节点就是标准的三层路由转发没有额外封装。没有封装意味着更好的性能和更低的开销网络路径也更符合传统运维的直觉。Calico的另一个优势是原生支持NetworkPolicy。它通过在每个节点上生成iptables规则来强制执行Pod间的访问控制可以精确到“哪个Pod能访问哪个Pod的哪个端口”。这对生产环境非常重要。但注意Calico对底层网络的依赖也更强它要求节点之间的三层网络本来就能通如果底层网络设备做了奇怪的ACL或者路由策略排查起来会牵扯到更多外部因素。3.3 选型对比与我的决策逻辑直接给一张对比表就是我实际选型时参考的维度对比维度Flannel VXLANFlannel host-gwCalico BGP部署复杂度极低极低中性能损耗中有UDP封装低无封装低无封装底层网络要求宽松三层可通即可要求节点二层互通要求节点三层路由可达支持NetworkPolicy不支持不支持支持大规模集群扩展性中中好排查难度需要关注overlay链路简单直观需要了解BGP状态我的决策逻辑比较简单也分享给很多团队过。如果是测试环境或者规模在几十个节点以内的内部系统选Flannel VXLAN就够了省心、够用、好排查。如果底层网络条件好节点二层通又对性能敏感可以换成host-gw模式。一旦需要NetworkPolicy做细粒度访问控制或者集群规模会扩大到上百节点直接选Calico别纠结。大部分犹豫不决的项目最后都因为“要用NetworkPolicy”或者“不想老处理大包问题”倒向Calico了。4. 一次线上故障的完整排查链路从“服务全正常”到“超时不背锅”4.1 现象与初步判断某次在内部环境遇到一个很难啃的问题服务A需要调用另一个命名空间里的服务B控制台持续报连接超时。看任务状态所有Pod都是RunningB服务本地端口可以正常访问日志也没有报错。也就是说Pod活着、端口在听问题一定出在A到B的“路上”。这里有个很重要的排查习惯别急着进代码里找原因先确认这是不是一个“网络链路问题”。“连接超时”和“连接拒绝”是两个方向。连接拒绝通常是端口没起来或监听在内网地址连接超时则多半是包没到、到了没人回、或回包丢了。这类问题不按链路排查靠翻日志基本找不到答案。4.2 一步步沿着数据链路段段验证我的排查顺序是固定的从CNI状态到路由再从Pod内测到抓包最后看转发规则和DNS。第一步先确认CNI插件的Pod状态。如果Flannel或Calico的Pod在CrashLoopBackOff新Pod根本拿不到IP集群早就乱了。命令是kubectl get pod -n kube-system一眼看过去RR状态问题立刻锁定。第二步检查节点路由表。在源节点或目标节点上执行ip route看Pod网段的路由是否存在且指向正确。比如节点1上应该能看到类似“10.244.2.0/24 via 192.168.1.12 dev eth0”的条目。路由少了包就找不到出口。第三步从Pod内部做连通性测试。kubectl exec -it app-xxx -- ping 目标Pod IP。这里有个坑要提醒ping走的是ICMP很多环境会屏蔽ICMP所以ping不通不一定代表网络不通。更可靠的测试是TCP端口探测比如kubectl exec进去执行nc -vz 目标IP 端口或者用busybox的telnet命令。第四步当Pod IP连通但Service域名不通时重点检查kube-proxy和DNS。先看iptables规则iptables -t nat -L KUBE-SERVICES -n | grep ClusterIP确认DNAT规则存在。再看CoreDNS的情况。实际排查中域名解析超时占了一大半的比例后面单独说。第五步跨节点通信仍然不通的时候才考虑抓包。在目标节点执行tcpdump -i eth0 port 4789VXLAN模式或者直接抓Pod网卡流量确认封装层是否收到了包以及有没有回包。4.3 那次故障的真实根因封装端口被防火墙拦截那次问题是这么定位出来的同节点上的服务访问B一切正常换一个节点上的服务去访问B就超时。根据这个现象基本排除B本身的问题问题锁定在“跨节点通信”。在源节点和目标节点抓包时发现源节点一直在发UDP 4789端口的包但目标节点完全没收到。继续查目标节点的防火墙策略发现防火墙放行了业务端口但没放行集群通信用的UDP 4789端口。VXLAN封装包全被防火墙静默丢掉跨节点通信自然全断。修复方式就是放通节点之间UDP 4789端口。这个案例典型的点在于所有业务端口都是通的网络插件Pod也都是Running表面看“一切正常”实际上节点之间悄悄断了一条路。这也再次说明排查K8s网络问题必须按链路逐段验证只看静态状态远远不够。5. 新手最容易踩的五个网络坑和处置方案5.1 MTU不匹配导致“时通时不通”这个坑非常隐蔽症状是ping小包通ping大包比如-s 1472以上不通或者大文件传输频繁卡住、超时。原因是VXLAN封装会在原始数据包外加几十字节的头部。物理网卡MTU默认1500如果你没给网络插件设置MTU它会默认按物理网卡的MTU来创建Pod虚拟网卡结果封装后超过1500只能分片而很多网络设备是不处理分片包的直接丢弃。处理方式就是统一把Pod网卡MTU调低比如物理MTU为1500时VXLAN模式的MTU改成1450左右保留封装开销。规划集群时就要确认所有节点的物理MTU一致别只在部分节点调。5.2 集群网段和现有网络段冲突某环境的Pod网段用了10.244.0.0/16结果这个网段正好和办公网某个段重叠了。集群外部设备不管这些Pod是不是私有的路由表里一看到这个网段就直接把包导去了错误的方向。最典型的表现是某些服务访问正常某些服务偶发超时因为部分网络设备做了精确路由而另外一些没做。处理方案是部署前强制规划好Pod网段和Service网段用一套独立的、确认不跟现有业务网段重叠的地址段。集群建好再改网段代价极大基本等于重建。这个坑一定要踩在规划阶段之前。5.3 NetworkPolicy的默认拒绝规则埋雷Calico部署后通常会配NetworkPolicy但很多人忽略了Namespace上可能被创建了一条默认拒绝规则。K8s的NetworkPolicy逻辑是允许优先还是拒绝优先和防火墙设备一样有默认策略。一旦有人创建了一条“default-deny”策略没有被明确放行的跨命名空间流量就会全部被丢弃。现象极为隐蔽所有Pod都是Running容器内单独访问本地端口正常但两个服务一联动就超时。排查时记得查看NetworkPolicykubectl get networkpolicy -A不要只在Deployment和Service里打转。很多“看起来是网络故障”的问题最后都在策略配置上翻车。5.4 CoreDNS解析超时反而让后端服务背了锅有一类故障的根子在DNS解析。现象是应用里偶发“connection refused”或“could not resolve host”每次重试又好了。不少人先怀疑后端服务挂了其实问题出在CoreDNS上。CoreDNS作为集群内DNS服务一旦所在节点CPU资源紧张或者requests和limits没有合理配置高并发时会丢弃或延迟解析请求。处置办法给CoreDNS设置合理的资源请求与上限把它的副本数调起来必要时用节点亲和调度到性能稳的节点上。另外可以通过kubectl exec进入业务Pod内执行nslookup服务名来验证解析是否正常。能单独把DNS这一层摘出来测排错效率会高很多。5.5 节点增删后的路由残留和conntrack问题节点下线后如果相关路由没有清理干净其他节点的路由表或核心转发表里还保留着旧节点的信息跨节点通信时包被发往一台已经不在的机器表现为部分Pod通、部分不通甚至同一个服务一会行一会不行。所以在生产环境删除节点前一定要先执行节点排空和下线流程让网络插件自己回收路由。如果已经出现路由残留重启一下网络插件Pod会让路由表重新收敛通常能解决大部分问题。另外宿主机conntrack表满也会造成丢包现象是连接建立不了、超时严重这种时候可以通过增大conntrack相关系统参数来缓解。这两个属于节点运维层面的细节新手很容易忽略但线上故障一半以上和它们有关。先分享到这儿。K8s网络这块还能往下拆很多比如Ingress控制、多集群通信、流量策略的精细化配置每一块都有大量实操细节。如果这篇对你有帮助顺手点个赞关注一下后面我继续更新企业运维里的K8s实战系列。下次再遇到网络报错记得先问自己一句这层流量走到哪了再从那个位置往下排查。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑