k8s网络原理之Calico

之前整理总结过有关flannel的相关原理以及详细的传输过程,一直都想总结一篇通俗易懂的有关calico的相关原理和传输过程的,因为平常事情较多,没想到一拖就是这么久,这回借着复习的机会,将calico的原理,传输过程,以及各组件的作用还有两种模式的对比进行了细致的讲解和分析对比,也是对自己之前学习的一个总结,希望对大家有帮助。

什么是Calico:

Calico是一个基于BGP的纯三层网络方案,其会为每个容器(pod)分配一个可路由的IP,在通信时不需要解包和拆包,因此网络性能损耗小,易于排查和水平扩展。Calico网络功能强大,可以与istio集成。Calico IPIP模式与Vxlan类似,也是通过网络隧道技术实现的,与Vxlan的差别就是,VXLAN本质上本质上是一个UDP包,而IPIP则将包封装在本身的报文包上。它其实是利用了Linux 的tun/tap设备,对IP层的报文再加了一层IP层的封装实现的一种overlay模式。因为IPIP模式比BGP模式多了一层封包与拆包,所以性能会有所损耗。既然如此,为什么不直接使用BGP模式就行了呢?因为BGP模式是需要通过路由广播交换容器网络的路由信息,而路由广播只能在局域网中进行,在BGP模式下,如果kubernetes集群中的工作节点不在同一个子网,则跨子网的工作节点上的POD无法正常通信,下面就来详细讲解calico的这两种网络模式的原理以及他们各自的优缺点和适用场景

Calico的两种模式:

ipip模式:

Calico 的ipip模式是适用于node节点处于不同网段之间的pod的通信模式,也就是pod节点所在的node节点不在同一个二层网络中,在VXLAN中的报头里VNI标记不同

BGP模式:

Calico 的BGP模式是适用于node节点处于相同网段之间的pod的通信模式,是把每个node节点都当成是路由器的一种模式**(将节点之间的路由规则配置在node节点上),** 每个node节点都维护着到其他节点的路由转发表(由felix进行维护,BIRD将路由表向其他节点进行广播和转发)

Calico 网络模型的设计思路:

相同网段转发原理(BGP模式):

我们看图中的两台物理机。它们的物理网卡是同一个二层网络里面的。由于两台物理机的容器网段不同,我们完全可以将两台物理机配置成为路由器,并按照容器的网段配置路由表。

例如,在物理机 A 中,我们可以这样配置:要想访问网段 172.17.9.0/24,下一跳是 192.168.100.101,也即到物理机 B 上去。

这样在容器 A 中访问容器 B,当包到达物理机 A 的时候,就能够匹配到这条路由规则,并将包发给下一跳的路由器,也即发给物理机 B。在物理机 B 上也有路由规则,要访问 172.17.9.0/24,从 docker0 的网卡进去即可。

当容器 B 返回结果的时候,在物理机 B 上,可以做类似的配置:要想访问网段 172.17.8.0/24,下一跳是 192.168.100.100,也即到物理机 A 上去。

不同网段转发原理(IPIP模式):

跨网段访问问题

上面的 Calico 模式还有一个问题,就是跨网段问题,这里的跨网段是指物理机跨网段。

前面我们说的那些逻辑成立的条件,是我们假设物理机可以作为路由器进行使用。例如物理机 A 要告诉物理机 B,你要访问 172.17.8.0/24,下一跳是我 192.168.100.100;同理,物理机 B 要告诉物理机 A,你要访问 172.17.9.0/24,下一跳是我 192.168.100.101。

之所以能够这样,是因为物理机 A 和物理机 B 是同一个网段的,是连接在同一个交换机上的。那如果物理机 A 和物理机 B 不是在同一个网段呢?

例如,物理机 A 的网段是 192.168.100.100/24,物理机 B 的网段是 192.168.200.101/24,这样两台机器就不能通过二层交换机连接起来了,需要在中间放一台路由器,做一次路由转发,才能跨网段访问。

本来物理机 A 要告诉物理机 B,你要访问 172.17.8.0/24,下一跳是我 192.168.100.100 的,但是中间多了一台路由器,下一跳不是我了,而是中间的这台路由器了,这台路由器的再下一跳,才是我。这样之前的逻辑就不成立了。

我们看刚才那张图的下半部分。物理机 B 上的容器要访问物理机 A 上的容器,第一跳就是物理机 B,IP 为 192.168.200.101,第二跳是中间的物理路由器右面的网口,IP 为 192.168.200.1,第三跳才是物理机 A,IP 为 192.168.100.100。

这是咱们通过拓扑图看到的,关键问题是,在系统中物理机 A 如何告诉物理机 B,怎么让它才能到我这里?物理机 A 根本不可能知道从物理机 B 出来之后的下一跳是谁,况且现在只是中间隔着一个路由器这种简单的情况,如果隔着多个路由器呢?谁能把这一串的路径告诉物理机 B 呢?

我们能想到的第一种方式是,让中间所有的路由器都来适配 Calico。本来它们互相告知路由,只互相告知物理机的,现在还要告知容器的网段。这在大部分情况下,是不可能的。

第二种方式,还是在物理机 A 和物理机 B 之间打一个隧道,这个隧道有两个端点,在端点上进行封装,将容器的 IP 作为乘客协议放在隧道里面,而物理主机的 IP 放在外面作为承载协议。这样不管外层的 IP 通过传统的物理网络,走多少跳到达目标物理机,从隧道两端看起来,物理机 A 的下一跳就是物理机 B,这样前面的逻辑才能成立。

calico架构图:

BGP模式下各组件的作用:

**Felix作用:**Calico Agent,运行在每一台 Host 的 agent 进程,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等,保证跨主机容器的网络互通。

**BGP Client(BIRD)作用:**在 Calico 的角色是监听 Host 上由 Felix 注入的路由信息,然后通过 BGP 协议广播告诉剩余 Host 节点,从而实现网络互通。

BGP Route Reflector:在大型网络规模中,如果仅仅使用 BGP client 形成 mesh 全网互联的方案就会导致规模限制,因为所有节点之间俩俩互联,需要 N^2 个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数。

C alico BGP模式的优点: Calico BGP模式 是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率可能是所有方案中最高的**,因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。** 因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。

二层网络通讯需要依赖广播消息机制,广播消息的开销与 host 的数量呈指数级增长,Calico 使用的三层路由方法,则完全抑制了二层广播,减少了资源开销。

另外,二层网络使用 VLAN 隔离技术,天生有 4096 个规格限制,即便可以使用 vxlan 解决,但 vxlan 又带来了隧道开销的新问题。而 Calico 不使用 vlan 或 vxlan 技术,使资源利用率更高

IPIP模式数据包传输流程:

测试环境:

一个msater节点,ip 172.171.5.95,一个node节点 ip 172.171.5.96 :

创建一个daemonset的应用,pod1落在master节点上 ip地址为192.168.236.3,pod2落在node节点上 ip地址为192.168.190.203:

pod1 ping pod2:

数据包传输的具体流程如下:

pod1上的路由信息:

根据路由信息,ping 192.168.190.203,会匹配到第一条。第一条路由的意思是:去往任何网段的数据包都发往网管169.254.1.1,然后从eth0网卡发送出去。

路由表中Flags标志的含义:

U up表示当前为启动状态

H host表示该路由为一个主机,多为达到数据包的路由

G Gateway 表示该路由是一个网关,如果没有说明目的地是直连的

D Dynamicaly 表示该路由是重定向报文修改

M 表示该路由已被重定向报文修改

master节点上的路由信息:

当ping包来到master节点上,会匹配到路由tunl0。该路由的意思是:去往192.169.190.192/26的网段的数据包都发往网关172.171.5.96。因为pod1在5.95,pod2在5.96。所以数据包就通过设备tunl0发往到node节点上。

node节点上路由信息:

当node节点网卡收到数据包之后,发现发往的目的ip为192.168.190.203,于是匹配到红线的路由。该路由的意思是:192.168.190.203是本机直连设备,去往设备的数据包发往caliadce112d250。

那么该设备是什么呢?如果到这里你能猜出来是什么,那说明你的网络功底是不错的。这个设备就是veth pair的一端。在创建pod2时calico会给pod2创建一个veth pair设备。一端是pod2的网卡,另一端就是我们看到的caliadce112d250。下面我们验证一下。在pod2中安装ethtool工具,然后使用ethtool -S eth0,查看veth pair另一端的设备号。

pod2 网卡另一端的设备好号是18,在node上查看编号为18的网络设备,可以发现该网络设备就是caliadce112d250。

所以,node上的路由,发送caliadce112d250的数据其实就是发送到pod2的网卡中。ping包的旅行到这里就到了目的地。

查看一下pod2中的路由信息,发现该路由信息和pod1中是一样的。

顾名思义,IPIP网络就是将IP网络封装在IP网络里。IPIP网络的特点是所有pod的数据流量都从隧道tunl0发送,并且在tunl0这增加了一层传输层的封包。

在master网卡上抓包分析该过程:

打开ICMP 285,pod1 ping pod2的数据包,能够看到该数据包一共5层,其中IP所在的网络层有两个,分别是pod之间的网络和主机之间的网络封装:

根据数据包的封装顺序,应该是在pod1 ping pod2的ICMP包外面多封装了一层主机之间的数据包:

之所以要这样做是因为tunl0是一个隧道端点设备,在数据到达时要加上一层封装,便于发送到对端隧道设备中。

两层IP封装的具体内容:

BGP模式数据包传输流程:

测试环境:

在安装calico网络时,默认安装是IPIP网络。calico.yaml文件中,将CALICO_IPV4POOL_IPIP的值修改成 "off",就能够替换成BGP网络。

BGP网络相比较IPIP网络,最大的不同之处就是没有了隧道设备 tunl0。 前面介绍过IPIP网络pod之间的流量发送tunl0,然后tunl0发送对端设备。BGP网络中,pod之间的流量直接从网卡发送目的地,减少了tunl0这个环节。

master节点上路由信息。从路由信息来看,没有tunl0设备。

同样创建一个daemonset,pod1在master节点上,pod2在node节点上。

数据包传输的具体流程如下:

pod1 ping pod2。

根据pod1中的路由信息,ping包通过eth0网卡发送到master节点上。

master节点上路由信息。根据匹配到的 192.168.190.192 路由,该路由的意思是:去往网段192.168.190.192/26 的数据包,发送网段172.171.5.96。而5.96就是node节点。所以,该数据包直接发送了5.96节点。

node节点上的路由信息。根据匹配到的192.168.190.192的路由,数据将发送给 cali6fcd7d1702e设备,该设备和上面分析的是一样,为pod2的veth pair 的一端。数据就直接发送给pod2的网卡。

当pod2对ping包做出回应之后,数据到达node节点上,匹配到192.168.236.0的路由,该路由说的是:去往网段192.168.236.0/26 的数据,发送给网关 172.171.5.95。数据包就直接通过网卡ens160,发送到master节点上。

通过在master节点上抓包,查看经过的流量,筛选出ICMP,找到pod1 ping pod2的数据包。

可以看到BGP网络下,没有使用IPIP模式,数据包是正常的封装。

值得注意的是mac地址的封装。192.168.236.0是pod1的ip,192.168.190.198是pod2的ip。而源mac地址是 master节点网卡的mac,目的mac是node节点的网卡的mac。这说明,在 master节点的路由接收到数据,重新构建数据包时,使用arp请求,将node节点的mac拿到,然后封装到数据链路层。

两种模式的对比:

区别主要有两个:

1.BGP模式适用于同网段之间node节点上的不同Pod的通信,而IPIP模式适用于不同网段之间的通信

2.BGP模式将容器所在节点化身为路由器(vRouter),提供了路由的功能,并通过BGP协议将路由规则进行分发,再通过路由器上的路由规则,将包转发到目的地。在这个过程中,没有IPIP模式隧道的封包解包,仅仅是单纯的路由转发,性能会好很多。