浅谈kubernete中的flannel网络插件
作者 | 朱晋君
来源 | 君哥聊技术(ID:gh_1f109b82d301)
容器中的网络,无非就是2个方面,同一台宿主机上面的容器是可以联通的,不同宿主机上的容器相互间也是可以联通的。
在kubernete的发展历程中,kubernete并没有实现自己的网络规范,而是专注于编排的核心功能。一个重要的原因就是当时已经有了coreos发起的cni网络规范,而flannel模型最初已经能满足kubernete使用了。后来即使有了一些复杂的问题,calico和weave也基本解决。
CNI网络插件已经成为容器领域事实上的网络标准,它主要有2部分
1.CNI插件负责给容器配置网络
2.IPAM插件负责给容器分配IP地址,主要实现方式有host-local和dhcp
flannel通过给每台宿主机分配一个子网的方式为容器提供虚拟网络,它基于Linux TUN/VTEP,使用UDP封装IP包来创建虚拟网络,使用etcd存储网络的分配情况。
下面再回顾一下我们上篇kubernete部署springboot的系统结构,见下图
主从节点部署在2个虚机vmware1和vmware2上面作为宿主机,主节点ip是192.168.59.132,从节点ip是192.168.59.138,springboot应用启动后,pod状态如下:
同一台宿主机上容器间通信
pod创建后,会在宿主机上创建一个网桥,我们在宿主机192.168.59.138上执行ifconfig命令,可以看到cni0的网桥,连接到这个网桥上的设备,都可以通过cni0来进行通信。也可以看到veth0f38c044、vetha234d8db这2个网卡。
输出如下:
执行brctl show命令可以看到,veth0f38c044、vetha234d8db这2个网卡被插在了cni0网桥上
而我们进入pod后查看网络,可以看到这2个pod都分别有一个eth0网卡,这个网卡正是和上面的veth0f38c044、vetha234d8db这2个网卡配对的网卡。
进入第一个pod
进入第二个pod
这2对虚拟网卡叫做veth pair设备,它总是成对出现,并且一个网卡上的数据会同时到达第二个网卡。而且这2对网卡一端都连接到cni0网桥,另一端分别在2个springboot的容器中,所以在2个容器中ping对方可以ping通。
注:在linux操作系统上,网桥是工作在数据链路层,网络包在数据链路层通过mac地址来进行数据发送。所以在10.244.1.4地址ping10.244.1.5时,10.244.1.4发出一个网络包,这个网络包通过veth0f38c044网卡到达cni0,cni0通过ARP广播找到对应10.244.1.5网卡的mac地址发送给它,数据到达vetha234d8db网卡后也就出现在了10.244.1.5的eth0网卡上,最终实现数据包接收。
数据包的返回跟这个过程完全一样。整个流程总结如下:
跨主机容器间通信
我们看一下宿主机192.168.59.132的网络信息
之前搭建的集群中,2个springboot的pod都被调度到了192.168.59.138上。这时我们如果在主节点上创建一个pod,部署应用后pod的ip地址是10.244.0.2。这时如果我们想让容器ip10.244.1.4访问10.244.0.2,因为目的地址10.244.0.2并不在宿主机192.168.59.138上的cni0网桥网段内,所以是不可能像同一台宿主机那样可以通过cni0网桥直接通信的。
这就需要选择新的路由规则。我们看一下192.168.59.138的路由规则如下:
这种情况下该宿主机上的的数据包要发出去,就需要匹配第二条规则,走flannel.1这个设备,这个设备其实是一个工作在网络层第三层的TUN设备,他本质上是一个宿主机上的flanneld进程,它的功能是在用户态和内核态之间传送IP网络包。
之前我们提到过,flannel方案的思想是为每一台宿主机创建一个子网,有了这个子网,TUN设备就可以找到下一跳的网络地址,比如本文中集群的2个子网地址分别是10.244.1.0/24和10.244.0.0/24。这样宿主机上容器10.244.1.4的网络包要发送到宿主机容器10.244.0.2上,就可以通过宿主机上的flanneld进程创建的子网找到对应的宿主机。
我们再来看宿主机192.168.59.132的路由规则
10.244.1.4到达宿主机192.168.59.132后,会根据上面的第2条路由规则,找到宿主机上的cni0网桥设备,这样就跟上一节的同一台宿主机的容器间通信一样,可以找到对应的容器地址了。
上面的过程数据包从目的容器所在宿主机发出前flanneld应用会进行一次UDP封包,而到达目的容器宿主机进入flanneld应用会进行数据包的解封装。这样的问题是数据拷贝次数太多,包括从docker应用到cni0网桥,从cni网桥到flanneld应用,从flanneld应用到宿主机的ens33网卡。
为了解决这个问题,flannel引入了VXLAN模式,上面的数据包封装和解封装都在内核态完成,它的解决方案是在上面的3层网络基础上构建出二层网络,它的做法是引入一个VTEP设备,回去再看我们的2台宿主机的网络,上面的flannel.1就成为一个VTEP设备,既有ip地址又有mac地址,它可以对二层数据包进行封装和解封装,减去了数据在内核态和用户态直接的拷贝。整个通信过程如下:
最后,flannel网络插件是怎么给容器分配网络呢?在上篇介绍的如何在kubernete集群上部署springboot应用,使用命令kubectl apply -f springboot-mybatis.yaml创建pod时,第一个pod里面创建的容器是一个infra容器,这个容器的作用其实就是要控制network-namespace,这个容器创建完成后,kubernete就会调用cni网络插件为这个容器配置网络,而pod里面的其他容器则会跟infra容器共享network-namespace。
总结
kubernete选择cni网络插件进行管理,有一定历史原因,但是集成了cni,对于网络的配置非常方便,自身可以专注于编排,由于个人能力有限,只能讲到这里了,里面的不正确的地方,请大佬们批评指正。
了解下