
Flannel Vxlan
同节点同Pod不同容器
使用共享的网络名称空间进行通信,使用Container网络模式,通过lo网卡建立两个容器之间的通讯。
其他名称空间仍然隔离。(文件名称空间,系统名称空间,进程列表)
同节点不同Pod
精简
首先走2层16位的路由段,到主网络名称空间下。由于是桥接,所以再从桥接下的网络查询mac地址到指定的接口不同的网络名称空间中。
Pod的里面eth0对应的peer是谁?
可通过ethtool -S eth0
查询该Pod的虚拟网卡对在主机网卡中所对应的下标。
如何知道该主机虚拟网卡的pod peer是在哪个Linux Bridge下?
可以通过brctl show
来查看。
Pod如何发包到同节点的Pod?
通过Linux Bridge桥接模式实现的,可通过brctl showmacs cni0
查看cni0网桥下的所有Mac地址。
cni0网桥是如何学习到同节点下面其他网络的?
首先一个Pod在Ping同节点另一个Pod的时候,会发一个ARP包进行广播询问,有Pod响应,然后主机Bridge记录两端的IP与mac信息。
然后可进行通信。
Pod本地会记录对端IP和MAC地址到ARP表中,当然主机的arp表会记录所有的。
是走路由还是查ARP表直接进行通信?
首先通过查询路由表走二层的网络,然后可以看到走的eth0的网卡。
细节实践
首先创建三个Pod,两个相同节点一个不同的节点。
vim deploy.yaml
apiVersion: v1
kind: Pod
metadata:
name: c1
spec:
containers:
- name: c1
image: burlyluo/nettoolbox
nodeSelector:
kubernetes.io/hostname: k8s-125-node1
---
apiVersion: v1
kind: Pod
metadata:
name: c2
spec:
containers:
- name: c2
image: burlyluo/nettoolbox
nodeSelector:
kubernetes.io/hostname: k8s-125-node1
---
apiVersion: v1
kind: Pod
metadata:
name: c3
spec:
containers:
- name: c3
image: burlyluo/nettoolbox
nodeSelector:
kubernetes.io/hostname: k8s-125-node2
---
kubectl apply -f deploy.yaml
由于c1和c2 pod在同一个k8s-125-node1
节点中,我就以这两个pod为例,c1 ping c2 是没有问题的。
kubectl exec c1 -- ping 192.168.1.4 -c 1
首先我们通过查询c1 Pod的路由,当ping c2的时候会从第三条路由出去,并且会从eth0
接口出去,如何知道连接的哪条线到root ns(主机网络名称空间)呢?
kubectl exec c1 -- route -n
我们可以通过ethtool -S eth0
命令来查询该接口对应主机接口的下标,我们看到下标为10,在node1主机上我们通过ip -a
来查看插入两头的虚拟网卡对,我们查到对应的peer名为vethf8fbd60e@if2
并且指定网桥设备为cni0
。
kubectl exec c1 -- ethtool -S eth0
然后在主机上再次查看路由表,发现c2 192.168.1.4
从第四条路由出去,并且是从cni0
这个接口出去,我们从上图得知cni0
是一个虚拟网桥,那么我们可以通过brctl show
查询Bridge下有几个虚拟网卡对连接的,便可以从这几个接口中通过发送ARP包获取两端mac地址,并记录到Bridge Mac表中。
那么Bridge的mac地址该如何查询呢?可通过brctl showmacs cni0
命令进行查询。
我们再来查询一下c2的mac地址为:E6
。所以结合上图我们就知道该mac从第三个接口42
1A:59
veth6d1d420a
出去,完成整个通信。
kubectl exec c2 -- ifconfig
不同节点不同的Pod通信。
精简
首先查询路由到主网络名称空间下,然后查询路由获得对端Pod所在的主机的网关地址,并发现从flannel.1
接口出去,那么我们源IP和目的IP不变是pod的ip,Mac地址改变为两个Node中flannel.1
接口的mac。
主机是如何知道对端IP和Mac地址的?
是有一个flanneld
进程通过ip monitor
的一些网络事件来收集信息这些信息将存储到etcd中,然后将相关mac地址与ip记录到(IP)fdb表中和(mac)arp表中,然后flannel.1
通过ARP表查询对端mac地址(通过三层IP查询二层mac地址就该查ARP表,如果没有还是会发ARP包进行询问的)得到mac地址后查询fdb表查询知道了对方的节点的ip地址,再通过封装Vxlan的包发出去,然后到对端的flannel.1
后再进行解析内部IP。
细节实践
首先我们通过c1来ping c3发现是没有问题的。
接下来我们分析路由。
接下来我们查询路由表,发现走的是第二条路由并且是从主机eth0接口出去的,也是通过对端插入的vethf8fbd60e@if2
网卡到cni0下面后,将包递给主机的内核协议栈中主网络名称空间下面。
kubectl exec pod/c1 -- route -n
然后根据上面的路由,我们发现它是走的第五条路由,该包会从flannel.1
接口出去。
192.168.2.0 192.168.2.0 255.255.255.0 UG 0 0 0 flannel.1
当数据包到达flannel.1
时,需要对其进行二层Vxlan封装,网关下一跳为192.168.2.0/32
,那么我们知道IP但不知道目的mac地址啊,将如何得知呢?
首先我们查ARP表可以得到该目标地址的mac地址,我们可以通过执行arp
命令进行查看。
这一条Mac地址arp表又是如何得到的呢?
是通过flanneld
的进程通过etcd数据库中记录的ip和mac地址信息插入arp表中的。
那么我们如何知道该ip是在哪台主机上呢?
其实是通过fdb
表查询mac获得具体主机的ip地址。
# 对端的192.168.2.0
bridge fdb show dev flannel.1 | grep 'e6:a0:83:60:8d:92'
我们知道了是10.211.55.9
主机上面。
那么又是谁在维护fdb
的信息?以及是怎么管理的?
答案还是flanneld
插入的这些信息,先通过ip monitor
获取网络的事件然后将其中的网络信息存储到etcd中,最后flanneld
将更新和维护fdb表和arp表、route表。
注意这个时候封装的Vxlan的包将修改源mac和目的mac为两端的flannel.1
网卡的mac地址,当然IP上层为主机IP就是10.211.55.8
和10.211.55.9
。
我们可以通过抓包来查看。
tcpdump -pne -i flannel.1
# 9e:72:63:96:91:7e 是node1中的flannel.1
# e6:a0:83:60:8d:92 是node2中的flannel.1
当然我们在进行UDP封包的时候,还需要知道VNI和UDP发出的Port端口如何获取呢?
任何一个VXLAN设备在创建时都会指定一个三层物理网络设备作为VTEP,这个物理网络设备的IP就是UDP的源IP。可使用以下命令查询flannel.1的VTEP信息、VNI信息、UDP Port信息。
ip -d link show flannel.1
我们可以看到VNI为1,然后UDP的port为8472,与我们概述的Flannel相符合。
然后UDP发送对端节点后,就会去对比对端VNI是否一致,Mac地址是否一致,都一致就会去掉Vxlan的头和Inner Ethernet 的头,然后把数据包从flannel.1网卡进行发送,然后通过对端路由到cni0中。
大致就是这样的。唯一需要注意的就是当跨节点flannle.1到对端的flannle.1的时候,mac地址会更改但vxlan解析后mac地址除了源mac地址改为cni0的地址外目的mac是c3 pod的mac。
所以从理论来讲,Flannel使用的还不是真正的Vxlan模式。
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739


默认昵称
mac地址会更改但vxlan解析后mac地址除了源mac地址改为cni0的地址外目的mac是c3 pod的mac. 这里的源地址是被改为flannel.1的mac地址吧 ,你这里写的是cni0的地址