Kuberbetes Pod间无法通信问题处理

k8s集群创建service(服务)后,集群内pod所在节点可以访问该服务,但其它节点无法正常访问该服务,调试解决后,觉得过程挺有意义,遂记录下整个调试解决过程。

0x01 故障及集群信息

这里先说明一下k8s集群的情况,集群时一个master(节点A)一个worker(节点B),他们的IP信息如下:

机器标识

机器作用

内网IP

公网IP

节点A

master节点

10.200.200.7

129.226.176.163

节点B

worker节点

172.19.0.13

43.129.71.69

节点A和节点B并不在同一内网,他们直接通过公网IP进行数据传输。

故障的表现主要是,部署一个nginx服务并创建对应的service后,在master节点无法通过ClusterIP访问服务,但是在worker节点可以正常访问该服务。

上图中,cni0和flannel.1网络设备都是flannel网络插件生成的虚拟网络设备,使用cni0是一个bridge设备,flannel.1是一个vxlan设备,cni0连接着该节点上的各个pod以及flannel.1。而flannel.1的另一头连着flanneld进程,所有进入flannel.1的流量都会交给flanneld进程进行处理,而flanneld会把数据包3层(IP层)及以上的包封装到一个udp包中,然后查找系统配置,找到数据包需要发送到的节点的IP,然后发送给对端节点的指定udp端口(5678),这个技术就是vxlan,可以将非同一网段的设备通过公网组成同一网段(内网)。

图2.2、设备类型

我们还是通过网络流向图来具体说明,节点A中的pod-1向节点B的pod-3发出一个请求,数据包首先会查询pod-1中的路由表,通过默认路由数据包到达cni0,随后转发给flannel.1设备,然后竟有flanneld进程处理后以udp数据包的形式发送给节点B的5678端口,节点B的flanneld收到数据后并解包数据,然后丢给节点B的flannel.1设备,进而请求转发到节点B的cni0设备,通过查找arp表,确定目的pod-3,最后数据包顺利到达pod-3。而如果请求是在节点A上直接发出的,那一开始会查询节点A的路由表,然后通过路由表确定数据包转发给flannel.1设备,后面的过程跟上面相同。

明白了上面两类IP以及转发规则后,我们来看具体遇到的问题。

0x03 故障分析

首先匹配到nat表的OUTPUT链(Chain),可以看到,请求被转到KUBE-SERVICES

查看KUBE-SERVICES链,根据目标IP(10.110.36.99)和端口(80),请求被转到KUBE-SVC-HL5LMXD5JFHQZ6LN

查看KUBE-SVC-HL5LMXD5JFHQZ6LN链得知请求被转到KUBE-SEP-SADCJIHRQW7RJ62U链,然后查看KUBE-SEP-SADCJIHRQW7RJ62U链的详情,可以看到,请求被以DNAT的方式转到了10.244.1.2:80

通过上面的分析,我们知道请求最终被转到10.244.1.280端口,那10.244.1.2这个IP又是谁的呢?

其实这个IP是服务关联的容器的IP,通过kubectl get pods查询deployment创建的pod,然后使用kubectl describe pods nginx-6fd9b8bcc7-ln2ws查询pod详情可以看到pod的IP。

那么我们现在的问题就转换成了在master节点中访问10.244.1.2的服务,这个也是不通的。

通过上一节的分析,我们知道数据包首先是到flannel.1设备,然后flanneld进程进而eth0网卡。直接上抓包工具对flannel.1抓包,结果如下:

上图抓包结果显示,数据包确实到达了flannel.1,但是只有去的SYN包,没有回包。那数据包应该是在某一个阶段丢了。我们先放一放这个,继续到下一收到包的配备看。由于flanneld是进程,不到万不得已,先不动它。flanneld的下一个设备是eth0,具备抓包条件,直接在eth0上抓端口为8472的udp包:

这里就发现了端倪,数据包虽然是发给节点B的,但使用的目的IP却是节点B的内网IP,而节点A和B并不在同一内网,并无法直接访问,这就导致了数据包中途丢包而无法连接。

找到了问题的原因,下一步就是分析是什么造成了这种情况。通过上面的分析我们知道,错误目的地址的数据包是节点A的flanneld进程发送的。此时我们的排查方向就转到了flanneld上,在查看了flannel部署信息及master和worker上flannel容器的配置文件后依然没有任何头绪。遂查看flannel文档(https://github.com/flannel-io/flannel/blob/master/Documentation/kubernetes.md)发现了flannel.alpha.coreos.com/public-ip-overwrite注解,该注解可以重写节点对外的IP,我们可以使用kubectl describe nodes worker01查看节点详情。

通过查看节点详情,我们发现当前节点的注解flannel.alpha.coreos.com/public-ip值为worker01的内网IP,这也是上面数据包目的地址错误的原因。执行下面的命令对worker01节点打上flannel.alpha.coreos.com/public-ip-overwrite注解,值为worker01的外网IP。

# Set annotation
kubectl annotate node worker01 flannel.alpha.coreos.com/public-ip-overwrite=43.129.71.69 --overwrite
# Get flannel pod on worker01
kubectl -n kube-system get pods --field-selector spec.nodeName=worker01 | grep flannel
# Restart flannel
kubectl -n kube-system get pod kube-flannel-ds-xq7b9 -o yaml | kubectl replace --force -f -

修改后需要重启worker01的flannel pod以生效,然后重新在master上执行curl 10.244.1.2

0x04 总结

此类问题产生的原因是不在同一内网且机器的公网IP并未显示的绑定在机器网卡上(典型的云主机),而通常情况下我们都是在同一内网搭建k8s集群,因此很少会遇到。而解决这个问题的过程可以让我们更好的理解k8s的网络通信原理。

至于不再同一内网且公网IP未显示绑定在网卡上的机器如何搭建集群,后面我会单独写一篇文章。

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
Kuberbetes Pod间无法通信问题处理
k8s集群创建service(服务)后,集群内pod所在节点可以访问该服务,但其它节点无法正常访问该服务,调试解决后,觉得过程挺有意义,遂记录下整个调试解决过程...
<<上一篇
下一篇>>