calico网络模型中的路由原理

fzu_huang

calico的部署

1、下载模板
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/kubeadm/1.7/calico.yaml

可以得到一份calico官方提供的v3.1版本calico的部署模板(基于kubeadm)。当然里面都是社区的镜像,我们可以替换成本地镜像.建议到网易云的景象中心下载,很多其他的国内镜像仓库都不做维护了。

2、启/禁用 ip-ip

目前官方提供的模板里,默认打开了ip-ip功能,该功能会在node上创建一个设备:tunl0,容器的网络数据会经过该设备被封装一个ip头再转发。这里,calico.yaml中通过修改calico-node的环境变量:CALICO_IPV4POOL_IPIP来实现ipip功能的开关:默认是Always,表示开启;Off表示关闭ipip; cross-subnet表示开启并支持跨子网,目前用不到这种类型。
sed -i "s#Always#Off#g" calico.yaml

3、部署:

注意:部署前,要配置一个参数,让calico-node组件能够识别node的IP,node上可能有多块网卡,官方提供的yaml文件中,ip识别策略(IPDETECTMETHOD)没有配置,即默认为first-found,这会导致一个网络异常的ip作为nodeIP被注册,从而影响node-to-node mesh。我们可以修改成can-reach的策略,尝试连接某一个Ready的node的IP,以此选择出正确的IP。

为了方便,下面的脚本里,我以任意一个node的ip地址为reach 地址

connective_ip=`kubectl get node -o wide |grep Ready |head -n1 |awk '{print $6}'`
sed -i  -rn 'p;/name: IP/,/autodetect/H;/autodetect/{g;s/^\n//;p}' calico.yaml 
sed -i '1,/name: IP/{s/name: IP/name: IP_AUTODETECTION_METHOD/}' calico.yaml
sed -i '1,/\"autodetect\"/{s/\"autodetect\"/can-reach=__ONE_CONNECTIVE_ENDPOINT__/}' calico.yaml
sed -i "s#__ONE_CONNECTIVE_ENDPOINT__#$connective_ip#g" calico.yaml

执行:
kubectl apply -f calico.yaml

4、calico使用过程中的一些其他点

  1. calico以ipip模式部署完毕后,node上会有一个tunl0的网卡设备,这是ipip做隧道封装用的。当我们把节点下线,calico容器都停止后,这个设备依然还在,执行 rmmod ipip 命令可以将它删除(如果calico-node仍在运行,会自动再建一个新的)
  2. calico部署完毕后,其数据记录在calico-etcd容器运行节点的/var/etcd/calico-data目录中,如果要完全清理集群中的etcd数据,需要将该目录删除。
  3. calico支持以kubernetes为存储后端,以这种方式部署时,calico不再需要额外部署etcd,而是将数据以CRD的方式存到k8s中。calico的组件依赖kubeconfig与k8s交互。在这种模式下,部署calico需要执行:
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml
wget https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
kubectl apply -f rbac-kdd.yaml
kubectl apply -f calico.yaml
此处下载的calico.yaml 仍可以参照上文的部署方式进行ipip、ip pool等的定制化。

calico bgp 网络

通过bgp方式部署好calico后,我们在集群中创建几个pod:

root@k8s-calico1:~# pods 
NAMESPACE     NAME                                       READY     STATUS    RESTARTS   AGE       IP                NODE
huang1        huangtest-69d9fddd97-cbzbm                 1/1       Running   0          20m       192.168.211.1     k8s-calico4
huang1        huangtest-69d9fddd97-pbvvw                 1/1       Running   0          3m        192.168.210.194   k8s-calico3
huang2        hhh-6897d64fcd-4zph4                       1/1       Running   0          1d        192.168.97.2      k8s-calico2

我们在k8s-calico2这个node上来看。执行ip r, 在这个node的路由中,需要我们关注的有:

192.168.97.2 dev calic285cddbb40 scope link 
blackhole 192.168.97.0/26 proto bird 
192.168.210.192/26 via 10.173.32.26 dev eth0 proto bird 
192.168.211.0/26 via 10.173.32.25 dev eth0 proto bird

192.168.97.2 dev calic285cddbb40 scope link 这条路由将通向容器ip的请求导向veth:calic285cddbb40 ,进而让请求直达容器内的网卡。
blackhole 192.168.97.0/26 proto bird 表示发往192.168.97.0/26网段的报文都会被丢弃且不会回复源端。配置这条路由的原因是:这台机器上的calico网络可分配的cidr是192.168.97.0/26,容器里访问同网段的其他IP时,配置该路由以避免报文被发到外部。
最后两条,分别记录了:要访问calico网络中的某个网段,需要以对应的node IP为网关,通过eth0发包。也就是说通过这两条路由,可以将部分网段(目的IP)的包经由eth0发送到正确的地方。

我们可以这么理解:pod hhh-6897d64fcd-4zph4中ping huangtest-69d9fddd97-pbvvw时,流量是这么走的:

0、数据包封装完成,srcip:192.168.97.2 , destip:192.168.210.194
1、容器中只有eth0网卡,容器里ip r 看到的是default **** dev eth0,所以流量通过容器的eth0发送。
2、容器网卡的配置是通过vethpair做的,也就是说,容器里网卡发的包,在宿主机上都会被calic285cddbb40设备发出。
3、通过第2步,网络报文就在宿主机的网络中,受宿主机路由影响,192.168.210.192/26 via 10.173.32.26 dev eth0 proto bird这条路由会将数据从eth0转发,并发给路由中记录的网关:10.173.32.26(这个ip,就是pod huangtest-69d9fddd97-pbvvw所在的node:k8s-calico3 的ip)
4、10.173.32.26是node:k8s-calico3上eth0的ip,收到包后,在机器自身的路由表中寻找合理的路由,当然这个地方也会有路由:192.168.210.194 dev calif6874dae1d2 scope link,于是包顺利被对端接收

calico ipip 网络

我们通过ip-ip模式部署calico,然后将原有的pod全部删掉重建,如下:

root@k8s-calico1:~# pods 
NAMESPACE     NAME                                       READY     STATUS             RESTARTS   AGE       IP                NODE
huang1        huangtest-69d9fddd97-2b8hr                 1/1       Running            0          1m        192.168.97.1      k8s-calico2
huang1        huangtest-69d9fddd97-npwzw                 1/1       Running            0          1m        192.168.210.65    k8s-calico4
huang2        hhh-6897d64fcd-kqsj4                       1/1       Running            0          10s       192.168.210.129   k8s-calico3

我们再去看看k8s-calico2这个node 上的路由,同样的需要我们关注的路由有下面的几条:

root@k8s-calico2:~# ip r  |grep bird
192.168.97.1 dev cali3683f65394b scope link
blackhole 192.168.97.0/26 proto bird 
192.168.110.64/26 via 10.173.32.24 dev tunl0 proto bird onlink 
192.168.210.64/26 via 10.173.32.25 dev tunl0 proto bird onlink 
192.168.210.128/26 via 10.173.32.26 dev tunl0 proto bird onlink

前两条不再赘述,我们看到最后三条路由,其实他们描述的逻辑与BGP的那两条没有差别,只不过网卡换成了tunl0.

我们以一个例子来解释清楚:pod huangtest-69d9fddd97-2b8hr ping hhh-6897d64fcd-kqsj4

0、封装报文,SRCIP:192.168.97.1 DESTIP:192.168.210.129
1、容器中只有eth0网卡,容器里ip r 看到的是default **** dev eth0,所以流量通过容器的eth0发送。
2、容器网卡的配置是通过vethpair做的,也就是说,容器里网卡发的包,在宿主机上都会被calic285cddbb40设备发出。
3、通过第2步,网络报文就在宿主机的网络中,受宿主机路由影响,192.168.210.128/26 via 10.173.32.26 dev tunl0 proto bird onlink 识别到匹配的对端IP,将报文从tunl0发出到10.173.32.26。这里,tunl0会对报文进行封装,在原有的ip报文之上封装了一个ip头部,新的头部中,srcip是宿主机自身的ip:10.173.32.23, destip是对端的ip地址:10.173.32.26。
我们可以通过tcpdump抓包看到这个步骤:
封装前:

clipboard.png

封装后(注意到输出内容结尾有ipip-proto-4):

clipboard.png
4、由于宿主机上只有一个eth的物理机网卡,所以流量终究还是从eth0向外发出到10.173.32.26。10.173.32.26这个节点的eth0网卡,收到了报文后,发现带有ipip协议的标记(第3步中tcpdump抓包看到的ipip-proto-4),将ipip头部解开,于是处理到了真实的报文。
5、宿主机上192.168.210.129 dev cali5ce61eb6bc2 scope link 这个路由将包发给vethpair,从而被容器内的eth0接收。
6、icmp响应包的走向与上述的走向逻辑上相同。

why ipip

通过上面的了解,我们知道了纯bgp模式和ipip模式的数据访问路径。我们注意到二者最大的特别是ipip模式增加了一个tun0设备,对容器访问其他节点的pod网段的包进行封装。为什么要这么做?

关键在于网关的不同。普通的BGP适用于L2环境,也就是说node之间属于一个L2网络,彼此是知晓mac地址的。当pod A访问pod B的包到达宿主机后,经过BGP路由发给网关(对端node),网关必须要知晓一个包的目的IP地址应该发给谁(mac地址),所以这里网关(对端node)根据scope link路由发给对应的hostveth。

而在vpc场景下,node之间属于一个L3,彼此是通过路由打通的,node是不知道对端node的mac地址的。当pod A访问pod B的包到达宿主机后,也需要经过BGP路由发给网关(对端node),但此时node是不知道对端node的mac的,他会发给虚拟网关,包给到虚拟网关后,虚拟网关也不知道pod B的IP要找谁。

因此我们需要ipip。 在pod A访问pod B的包到达宿主机后,经过ipip的路由,从tunl0封装并发出。封装后变成nodeA访问nodeB,可以经过虚拟网关发到node B,nodeB解封后得知是podA访问podB, 就可以通过scope link路由发给pod B

总结:ipip模式可以兼容L2、L3的场景。而纯bgp模式只能在L2场景下使用。

待定:补充calico的BGP路由配置方法(AS,node-mesh)

阅读 12.7k

kubernetes+docker集群管理
我学到的关于k8s和docker的种种。 仅作学习笔记用,不会发布在别的地方
269 声望
99 粉丝
0 条评论
你知道吗?

269 声望
99 粉丝
宣传栏