之前文章,flannel的udp和vxlan模型.这两个有个共同特征.

就是用户的容器都连接在 docker0 网桥上。而网络插件则在宿主机上创建了一个特殊的设备(UDP 模式创建的是 TUN 设备,VXLAN 模式创建的则是 VTEP 设备),docker0 与这个设备之间,通过 IP 转发(路由表)进行协作.网络插件真正要做的事情,则是通过某种方法,把不同宿主机上的特殊设备连通,从而达到容器跨主机通信的目的。

 

1.host-gw模型

当需要从容器1请求到容器2时.

$ ip route
...
10.244.1.0/24 via 10.168.0.3 dev eth0

1.根据配置,会从eth0出去,mac地址是node2地址,目的ip肯定是10.244.1.3

2.node2收到后,会根据路由,将包转发给容器cni0,cni0转发给10.244.1.3.

可以看到node2充当了容器间请求的网关!这也是flannel-gw的由来

这种模式下,容器通信的过程就免除了额外的封包和解包带来的性能损耗。根据实际的测试,host-gw 的性能损失大约在 10% 左右,而其他所有基于 VXLAN“隧道”机制的网络方案,性能损失都在 20%~30% 左右

Flannel host-gw 模式必须要求集群宿主机之间是二层连通的.就是node1和node2在一个局域网.通过arp协议可以访问到.

那么如果node1和node2不在一个局域网咋整,那么就需要通过几个子网来达到通信的目的.就是多个路由器.

那么还有一个保证,这几个路由器的转发表也要有相应的配置,不能去的时候可以去,回不来了就尴尬了

Calico实现

calico和flannel-gw的实现是一样的.只不过他不是通过etcd来维护这个路由表的信息,而是通过一个BGP的组件.

BGP:大规模网络中实现节点路由信息共享的一种协议。

      用来维护集群路由信息.如果我们不用BGP,我们就需要手动去维护路由表,以达到网络互通.不太现实,而BGP ,每个边界网关上都会运行着一个小程序,它们会将各自的路由表信息,通过 TCP 传输给其他的边界网关。而其他边界网关上的这个小程序,则会对收到的这些数据进行分析,然后将需要的信息添加到自己的路由表里.

calico组成:

  • Calico的CNI插件,这是Calico与Kubernetes对接的部分
  • Felix:它是一个 DaemonSet,负责在宿主机上插入路由规则(写入 Linux 内核的 FIB 转发信息库),以及维护 Calico 所需的网络设备等工作.
  • BIRD:它就是BGP的客户端,专门负责在集群里分发路由规则信息.

实现

不会有任何网桥!!!! 需要为每一个容器设置一个Vethpear,设置到宿主机上.

1.目的mac地址是node2的mac地址;目的ip地址是容器ip10.233.2.3.

2.node2收到后,mac地址是自己,那么接受包;  目的ip不是自己,那么去查询route表,应该发给容器4.ending!!

从流程也能看出Flannel host-gw和Calico要求集群宿主机之间是二层连通

如果node1和node2不在一个子网,那node1根本没办法把包发送给node2.

Calico IPIP

既然没办法直接将包发送到node2,那么就让node1携带着容器1的请求 通过路由器 发送给node2吧..

谁来封装呢?flannel是TUN或者VTEP.  这里是tun0设备.如下,之后会被内核的IPIP驱动接管,然后封装,之后就是正常的设置目的mac是路由器的mac,目的ip是node2的ip.

10.233.2.0/24 via 192.168.2.2 tunl0

 

参考:https://time.geekbang.org/column/article/67775?utm_term=zeusV2IJA&utm_source=dilan  深入剖析kubernates

 

总结

Kubernetes通过一个叫做CNI的接口,维护了一个单独的网桥来代替docker0。这个网桥的名字就叫作:CNI网桥,它在宿主机上的设备名称默认是:cni0。

容器“跨主通信”的三种主流实现方法:UDP、host-gw、VXLAN。 之前介绍了UDP和VXLAN,它们都属于隧道模式,需要封装和解封装。接下来介绍一种纯三层网络方案,host-gw模式和Calico项目

Host-gw模式通过在宿主机上添加一个路由规则:

    <目的容器IP地址段> via <网关的IP地址> dev eth0

IP包在封装成帧发出去的时候,会使用路由表里的“下一跳”来设置目的MAC地址。这样,它就会通过二层网络到达目的宿主机。
这个三层网络方案得以正常工作的核心,是为每个容器的IP地址,找到它所对应的,“下一跳”的网关。所以说,Flannel host-gw模式必须要求集群宿主机之间是二层连通的,如果宿主机分布在了不同的VLAN里(三层连通),由于需要经过的中间的路由器不一定有相关的路由配置(出于安全考虑,公有云环境下,宿主机之间的网关,肯定不会允许用户进行干预和设置),部分节点就无法找到容器IP的“下一条”网关了,host-gw就无法工作了。

Calico项目提供的网络解决方案,与Flannel的host-gw模式几乎一样,也会在宿主机上添加一个路由规则:

    <目的容器IP地址段> via <网关的IP地址> dev eth0

其中,网关的IP地址,正是目的容器所在宿主机的IP地址,而正如前面所述,这个三层网络方案得以正常工作的核心,是为每个容器的IP地址,找到它所对应的,“下一跳”的网关。区别是如何维护路由信息:
Host-gw : Flannel通过Etcd和宿主机上的flanneld来维护路由信息
Calico: 通过BGP(边界网关协议)来实现路由自治,所谓BGP,就是在大规模网络中实现节点路由信息共享的一种协议。

隧道技术(需要封装包和解包,因为需要伪装成宿主机的IP包,需要三层链通):Flannel UDP / VXLAN / Calico IPIP
三层网络(不需要封包和解封包,需要二层链通):Flannel host-gw / Calico 普通模式

三层和隧道的异同:
相同之处是都实现了跨主机容器的三层互通,而且都是通过对目的 MAC 地址的操作来实现的;不同之处是三层通过配置下一条主机的路由规则来实现互通,隧道则是通过通过在 IP 包外再封装一层 MAC 包头来实现。
三层的优点:少了封包和解包的过程,性能肯定是更高的。
三层的缺点:需要自己想办法维护路由规则。
隧道的优点:简单,原因是大部分工作都是由 Linux 内核的模块实现了,应用层面工作量较少。
隧道的缺点:主要的问题就是性能低。

再简单回顾总结下flannel的udp和vxlan

udp模式

1.容器1发送请求到容器2,不是同一网段,查看路由,会把此ip包发送给flannel0(注意这个路由是flannel维护的),flannel0是一个TUN设备(一端接受ip包,一端连接用户进程flannelID)

2.flannelID进程把ip包封装为一个UDP包.目的ip就是宿主机.(那怎么知道目的容器所在的宿主机呢?etcd)

3.一个udp包过去后,通过协议栈,会交给flannelID进程,flannelID进程把udp包内容拿出来,是一个ip包,这时候再交给flannel0(TUN).tun设备再进行传递

 

VXLAN模式:

由于udp模式需要从用户态到内核态切换3次.很慢.是基于三层三层网络(封装的ip包)

1.VTEP是一个既有ip,同时也有mac地址.vxlan的目的就是不通过内核态-用户态切换.直接在VTEP间进行传输.

2.容器1->容器2,根据route表(启动时添加的规则,大体就是请求10.1.16.xx的时候,要走本机的flannel.1设备),将包发送给flannel.1设备.

3.flannel.1设备接收到后,

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.1.16.0       10.1.16.0       255.255.255.0   UG    0      0        0 flannel.1

3.flannel.1设备得想法把包直接发给另一个flannel.1设备.

目标ip知道吗?知道就是这个gateway

目标mac地址呢?node2启动的时候,flannelID进程就会把mac地址写入arp缓存了.

源ip,源mac都有.

但是还有个问题.这个目标mac地址是有,但是node2可能都不接受这个包,因为node2只知道eth0的.那咋增呢?

还是要包装一层.封装成一个普通的udp报文,发送给宿主机.宿主机的网络栈拆包的时候,识别到VXLAN Header就可以把包转发给VTEP,VTEP再转发给docker0

 

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐