CNI学习系列3 calico

介绍

calico也是一种非常流行的CNI实现。
项目地址:
https://github.com/projectcalico/calico
calico并没有类似flannel构建了虚拟交换机,而是使用纯路由的方式。

架构图

1

存储后端

这个图比较老,目前新版的架构中,calico在存储方面还支持一个中间层Typha。存储后端上支持三种:

  1. etcd
  2. CRD
  3. Typha

对于小规模集群,使用1,2方式就可以了,对于大规模集群使用前两种方案会对etcd带来较大压力。官方推荐使用Typha。
3

Typha自身维护一个对存储的链接就可以了,避免大量节点的felix对k8s Apiserver,etcd造成巨大压力。

路由后端

calico_backend bird/vxlan

  1. bird 即BGP
  2. vxlan calico也支持这种方式在一些不支持ipip的常见下部署overlay网络。
    通常在DaemonSet中

BGP下的传输后端

  1. ipip模式 即ip in ip模式,有封装和解封装的损耗,可以跨子网。
  2. BGP模式 没有封装的损耗,配合BGP反射,可以支撑超大规模的集群。

节点

每个节点将会安装calico-node,其中包含两个进程:

  • Felix:calico的核心组件,运行在每个节点上。主要的功能有接口管理、路由规则、ACL规则和状态报告
    1. 接口管理:Felix为内核编写一些接口信息,以便让内核能正确的处理主机endpoint的流量。特别是主机之间的ARP请求和处理ip转发。
    2. 路由规则:Felix负责主机之间路由信息写到linux内核的FIB(Forwarding Information Base)转发信息库,保证数据包可以在主机之间相互转发。
    3. ACL规则:Felix负责将ACL策略写入到linux内核中,保证主机endpoint的为有效流量不能绕过calico的安全措施。
    4. 状态报告:Felix负责提供关于网络健康状况的数据。特别是,它报告配置主机时出现的错误和问题。这些数据被写入etcd,使其对网络的其他组件和操作人员可见。
  • BIRD :BGP客户端,Calico在每个节点上的都会部署一个BGP客户端,它的作用是将Felix的路由信息读入内核,并通过BGP协议在集群中分发。
    当Felix将路由插入到Linux内核FIB中时,BGP客户端将获取这些路由并将它们分发到部署中的其他节点。这可以确保在部署时有效地路由流量。

Calico-Controller

Calico-Controller的主要作用为,负责识别Kubernetes对象中影响路由的变化。比如新建Pod和新增节点都需要Calico参与相关网络配置。这里需要Controller watch到对应的event后将变更保存到calico自己IDE存储中。并通知到对应的组件。

部署

对于测试学习场景,我们直接部署默认配置就可以了。
如果之前有安装过别的插件需先删除/etc/cni/net.d下相关配置,并删除相关设备。

1
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

默认情况下,calico使用的是ipip隧道模式

1
2
3
2021-10-02 05:04:26.657 [INFO][9] startup/startup.go 651: CALICO_IPV4POOL_NAT_OUTGOING is true (defaulted) through environment variable
2021-10-02 05:04:26.657 [INFO][9] startup/startup.go 992: Ensure default IPv4 pool is created. IPIP mode: Always, VXLAN mode: Never
2021-10-02 05:04:26.718 [INFO][9] startup/startup.go 1002: Created default IPv4 pool (10.244.0.0/16) with NAT outgoing true. IPIP mode: Always, VXLAN mode: Never

生产部署

对于生产部署官方的建议是:

  1. 安装 Typha 以确保数据存储可扩展性。
  2. 对单个子网集群不使用封装。
  3. 对于多子网集群,在 CrossSubnet 模式下使用 IP-in-IP。
  4. 根据网络 MTU 和选择的路由模式配置 Calico MTU。
  5. 为能够增长到 50 个以上节点的集群添加全局路由反射器。

ipip模式

4
需要注意的是ipip模式也需要bird的存在,来告知每个节点的ip段。
容器内部可以看到的设备

节点上看到的路由信息

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@node3 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.31.1 0.0.0.0 UG 100 0 0 enp0s3
10.233.90.0 192.168.31.170 255.255.255.0 UG 0 0 0 tunl0
10.233.92.0 0.0.0.0 255.255.255.0 U 0 0 0 *
10.233.92.3 0.0.0.0 255.255.255.255 UH 0 0 0 calib8bf7d77053
10.233.92.4 0.0.0.0 255.255.255.255 UH 0 0 0 cali57c3e1bd0c7
10.233.92.5 0.0.0.0 255.255.255.255 UH 0 0 0 cali48737a02ab9
10.233.96.0 192.168.31.171 255.255.255.0 UG 0 0 0 tunl0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.31.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s3

node3的节点ip为192.168.31.172,cni网段为10.233.92.0/24。
节点192.168.31.171 被分配了10.233.96.0/24 网段,192.168.31.170 被分配了 10.233.90.0/24网段。
podip根据网段,下一条选择对应节点IP,和tunl0这个设备。如果是本节点网段则会直接路由到对应设备,他们都是calixxxx开头的。这个设备直接映射到容器内的veth 名字是eth0。

1
2
3
4
5
6
7
8
9
10
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
3: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1480 qdisc noqueue
link/ether 1a:99:14:82:20:14 brd ff:ff:ff:ff:ff:ff
inet 10.233.92.4/32 scope global eth0
valid_lft forever preferred_lft forever
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0

路由全部走eth0

1
2
3
4
5
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0

如果是10.233.92.0/24中其他ip不在本地的,则会走到blackhole 10.233.92.0/24 proto bird 这条黑洞规则。
所以即使一个节点访问另一个节点并没有分配的ip,也会走tunl0到对应节点才会走到黑洞中。

同节点访问

从前面的路由表看,同节点访问会直接路由到对应的calixxxx设备。

跨节点访问流程

  1. 容器中的网卡用的是veth的pair虚拟网卡,一端连接到容器,另一端连着宿主机。
  2. 容器中的报文通过calixxx到达宿主机。然后根据宿主机的路由,报文发往tunl0设备。
  3. 报文到达tunl0时,会再次封装一层ip报文头,把目标node的ip作为目的ip,然后通过宿主网卡机将报文发出。
  4. 对端node收到报文后,解开外层ip报文头,发现是ipip报文,会转交给tunl0
  5. tunl0查找路由,就发送给calixxxxxx,对端容器就收到了报文。

BGP模式

bgp工作模式和flannel的host-gw模式非常类似;
限制:和flannel的host-gw限制一样,要求物理机在二层是连能的,不能跨网段;
配置方式:

1
2
3
4
- name: CALICO_IPV4POOL_IPIP
value: "Never"
- name: CALICO_IPV4POOL_VXLAN
value: "Never"

5
BGP网络相比较IPIP网络,最大的不同之处就是没有了隧道设备 tunl0。 前面介绍过IPIP网络pod之间的流量发送到 tunl0,然后tunl0发送对端设备。BGP网络中,pod之间的流量直接从网卡发送目的地,减少了tunl0这个环节。这种模式下每个节点都相当于一个能通过BGP协议交换路由信息的虚拟路由。

arp代理

Calico在BGP模式下使用了 ARP代理(ARP proxy)的方式解决了 ARP广播域的问题,实现了跨节点的通信。在ARP代理模式下,每个节点上都有一个 ARP代理,节点之间不再进行 ARP广播,而是由 ARP代理响应 ARP请求。

在 ARP代理模式下,节点A发送ARP请求时,ARP请求会被拦截并转发到目标节点所在的节点B,节点B的 ARP代理会响应 ARP请求,将自己的 MAC地址返回给节点A。节点A收到节点B的 MAC地址后,就可以向节点B发送数据了。

需要注意的是,ARP代理模式只适用于同一个网络内的节点通信,如果两个节点在不同的网络内,则需要使用其他方式,如路由器进行通信。

1
2
3
[13:39:56 root@node-3 ~]#cat /proc/sys/net/ipv4/conf/cali385152345a1/proxy_arp
1 #proxy_arp代理是启用状态
#容器在没有任何网络请求时查看arp

BGP原理

  • 路由反射器RR(Route Reflector):允许把从IBGP对等体学到的路由反射到其他IBGP对等体的BGP设备,类似OSPF网络中的DR。
  • 客户机(Client):与RR形成反射邻居关系的IBGP设备。在AS内部客户机只需要与RR直连。
  • 非客户机(Non-Client):既不是RR也不是客户机的IBGP设备。在AS内部非客户机与RR之间,以及所有的非客户机之间仍然必须建立全连接关系。
  • 始发者(Originator):在AS内部始发路由的设备。Originator_ID属性用于防止集群内产生路由环路。
  • 集群(Cluster):路由反射器及其客户机的集合。Cluster_List属性用于防止集群间产生路由环路。

calico的BGP

calico的IPIP与不使用封装会使用到BGP进行组网,保证不同node节点的Pod互相通信,默认情况下calico所有的node节点都是以对等方式进行BGP的互相连接,如果集群node节点数量大于50以上这种方式会造成每个节点的BGP连接指数级增多,所以需要引入BGP RR即路由反射器。即一部分node与RR进行对等连接之后RR之间在进行对等连接,以减少默认模式下的node全对等连接。如果集群规模超过200个节点需要配置实际的底层网络集成BGP进行相关配置。

配置BGP Peer

6

1
2
3
4
5
6
7
8
9
10
[17:06:18 root@node-1 ~]#cat BGPPeer-rr.yml
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: rr-to-node
spec:
# 规则1:普通 bgp node 与 rr 建立连接
nodeSelector: "!has(calico-rr)"
peerSelector: has(calico-rr)

1
2
3
4
5
6
7
8
9
---
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: rr-to-rr
spec:
# 规则2:route reflectors 之间也建立连接
nodeSelector: has(calico-rr)
peerSelector: has(calico-rr)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#应用
[17:15:41 root@node-1 ~]#calicoctl apply -f BGPPeer-rr.yml

#之后需要导出node1与node4的配置,进行修改
[17:17:09 root@node-1 ~]#calicoctl get node 192.168.10.11 -oyaml --export >11.yml
spec:
bgp:
ipv4Address: 192.168.10.11/24
ipv4IPIPTunnelAddr: 172.20.36.128
routeReflectorClusterID: 224.0.0.1 #要充当路由反射器的每个节点都必须有一个集群ID,通常是一个未使用的IPv4地址,如果多个路由反射器是高可用的他们的集群id应该一致,用于冗余和防环
orchRefs:
- nodeName: 192.168.10.11
orchestrator: k8s
status:
podCIDRs:
- ""
- 172.20.4.0/24

#给node1与node4节点打标签
[17:22:59 root@node-1 ~]#kubectl label nodes 192.168.10.11 calico-rr=true
[17:22:59 root@node-1 ~]#kubectl label nodes 192.168.10.14 calico-rr=true

#之后在node2查看node对等状态
[17:14:51 root@node-2 ~]#calicoctl node status
Calico process is running.

IPv4 BGP status
+---------------+---------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+---------------+---------------+-------+----------+-------------+
| 192.168.10.11 | node specific | up | 09:14:47 | Established | #这里可以看到node2与node1和node4为对等状态
| 192.168.10.14 | node specific | up | 09:23:12 | Established |
+---------------+---------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

#在node1或node4查看
[17:23:11 root@node-1 ~]#calicoctl node status
Calico process is running.

IPv4 BGP status
+---------------+---------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+---------------+---------------+-------+----------+-------------+
| 192.168.10.12 | node specific | up | 09:14:48 | Established | #node1和node4与没有calico-rr=true的节点都建立了对等连接
| 192.168.10.13 | node specific | up | 09:14:48 | Established |
| 192.168.10.15 | node specific | up | 09:14:50 | Established |
| 192.168.10.16 | node specific | up | 09:14:48 | Established |
| 192.168.10.14 | node specific | up | 09:23:02 | Established |
+---------------+---------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.
  • 本文作者: fenix
  • 本文链接: https://fenix0.com/cni-calico/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC 许可协议。转载请注明出处!