Calico 架构及BGP实现
BGP是互联网上一个核心的去中心化自治路由协议,它通过维护IP路由表或“前缀”表来实现自治系统AS之间的可达性,属于矢量路由协议。不过,考虑到并非所有的网络都能支持BGP,以及Calico控制平面的设计要求物理网络必须是二层网络,以确保 vRouter间均直接可达,路由不能够将物理设备当作下一跳等原因,为了支持三层网络,Calico还推出了IP-in-IP叠加的模型,它也使用Overlay的方式来传输数据。IPIP的包头非常小,而且也是内置在内核中,因此理论上它的速度要比VxLAN快一点 。Calico 3.x的默认配置使用的是IPIP类型的传输方案而非BGP。
Calico
是Kubernetes
生态系统中另一种流行的网络选择。虽然Flannel
被公认为是最简单的选择,但Calico
以其性能、灵活性而闻名。Calico
的功能更为全面,不仅提供主机和pod
之间的网络连接,还涉及网络安全和管理。Calico CNI
插件在CNI
框架内封装了Calico
的功能。
Calico
是一个基于BGP
的纯三层的网络方案,与OpenStack
、Kubernetes
、AWS
、GCE
等云平台都能够良好地集成。Calico
在每个计算节点都利用Linux Kernel
实现了一个高效的虚拟路由器vRouter
来负责数据转发。每个vRouter
都通过BGP1
协议把在本节点上运行的容器的路由信息向整个Calico
网络广播,并自动设置到达其他节点的路由转发规则。Calico
保证所有容器之间的数据流量都是通过IP
路由的方式完成互联互通的。Calico
节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT
、隧道或者Overlay Network
,没有额外的封包解包,能够节约CPU
运算,提高网络效率。
上面并不是什么严重的问题。但是有一点,Calico控制平面的上述设计中,物理网络最好是L2 Fabric,这样vRouter间都是直接可达的,路由不需要把物理设备当做下一跳。如果是L3 Fabric,控制平面的问题就来了:物理设备如果要存32位的路由,路由表将变得巨大无比。
因此,为了解决以上问题,Calico不得不采取了妥协,为了支持L3 Fabric,Calico推出了IPinIP的选项,用作cross-subnet下容器间通讯,但是有一定性能损耗。另一种方案是回退到L3 Hierarchy(calico目前支持),如果对容器迁移保持IP不变的需求不大,可以选择这种,这也是我们最后选择的方案。不过,对于使用L2 Fabric、没有多租户需求的企业来说,用calico上生产环境应该最理想选择。
Calico最理想的部署方式是基于L2 Fabrics的,官方也有相应说明([Calico over an Ethernet interconnect fabric](http://docs.projectcalico.org/ ... abric "Calico over an Ethernet interconnect fabric")),这里就不做详细说明了。由于我们数据中心规模比较大,显然L2 Fabrics不是理想的选择。事实也是如此,我们多个数据中心组成的网络是以BGP为控制层面组成的L3 Fabrics。每个数据中心为一个AS。这里不讨论跨数据中心的问题,大家只需知道我们跨数据中心在L3上是互通的便可以。
Calico是一个用于容器、虚拟机和基于本机主机的工作负载的开源网络和网络安全解决方案。Calico支持广泛的平台,包括Kubernetes, OpenShift, Docker EE, OpenStack和裸机服务。Calico支持多种网络架构,其中IPIP和BGP两种网络架构较为常用。这里简单说明一下这两种模式。
这两种模式下的calico所管理的容器内部联通主机外部网络的方法都是一样的,用linux支持的veth-pair,一端在容器内部一般名称是eth0@if66,这个66表示的是主机网络命名空间下的66号ip link.另一端是主机网络空间下的cali97e45806449,这个97e45806449是VethNameForWorkload函数利用容器属性计算的加密字符的前11个字符。每个Calico容器内部的路由如下所示:
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
再去看Calico所有的veth-pair在主机空间的calixxx的MAC地址,无一例外都是ee:ee:ee:ee:ee:ee
, 这样的配置简化了操作,使得容器会把报文交给169.254.1.1来处理,但是这个地址是本地保留的地址也可以说是个无效地址,但是通过veth-pair会传递到对端calixxx上,注意,因为calixxx网卡开启了arpproxy,所以它会代答所有的ARP请求,让容器的报文都发到calixxx上,也就是发送到主机网络栈,再有主机网络栈的路由来送到下一站. 可以通过cat /proc/sys/net/ipv4/conf/calixxx/proxy_arp/
来查看,输出都是1.
这里注意,calico要响应arp请求还需要具备三个条件,否则容器内的ARP显示异常:
- 宿主机的arp代理得打开
- 宿主机需要有访问目的地址的明确路由,这里我理解为宿主机要有默认路由
- 发送arp request的接口与接收arp request的接口不能是相同,即容器中的默认网关不能是calico的虚拟网关
IPIP模式
IPIP模式是calico的默认网络架构,其实这也是一种overlay的网络架构,但是比overlay更常用的vxlan模式相比更加轻量化。IPinIP就是把一个IP数据包又套在一个IP包里,即把 IP 层封装到 IP 层的一个 tunnel它的作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需 IP,而这个 ipip 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来.
IPIP通信原理
calico中用环境变量CALICO_IPV4POOL_IPIP来标识是否开启IPinIP Mode. 如果该变量的值为Always那么就是开启IPIP,如果关闭需要设置为Never(大小写不敏感,代码里有strings.ToLower操作)。
IPIP的calico-node启动后会拉起一个linux系统的tunnel虚拟网卡tunl0, 并由二进制文件allocateip给它分配一个calico IPPool中的地址,log记录在本机的/var/log/calico/allocate-tunnel-addrs/
目录下。tunl0是linux支持的隧道设备接口,当有这个接口时,出这个主机的IP包就会本封装成IPIP报文。同样的,当有数据进来时也会通过该接口解封IPIP报文。然后IPIP模式的网络通信模型如下图所示。
上图所示的通信如下:
- Pod-1 -> calixxx -> tunl0 -> eth0 <----> eth0 -> calixxx -> tunl0 -> Pod-2 1.c1访问c2时,ip包会出现在calixxx
- 根据c1宿主机中的路由规则中的下一跳,使用tunl0设备将ip包发送到c2的宿主机
- tunl0是一种ip隧道设备,当ip包进入该设备后,会被Linux中的ipip驱动将该ip包直接封装在宿主机网络的ip包中,然后发送到c2的宿主机
- 进入c2的宿主机后,该ip包会由ipip驱动解封装,获取原始的ip包,然后根据c2宿主机中路由规则发送到calixxx。