calico配置步骤--IPIP模式vsBGP模式

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显示异常:

  1. 宿主机的arp代理得打开
  2. 宿主机需要有访问目的地址的明确路由,这里我理解为宿主机要有默认路由
  3. 发送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模式的网络通信模型如下图所示。

上图所示的通信如下:

  1. Pod-1 -> calixxx -> tunl0 -> eth0 <----> eth0 -> calixxx -> tunl0 -> Pod-2 1.c1访问c2时,ip包会出现在calixxx
  2. 根据c1宿主机中的路由规则中的下一跳,使用tunl0设备将ip包发送到c2的宿主机
  3. tunl0是一种ip隧道设备,当ip包进入该设备后,会被Linux中的ipip驱动将该ip包直接封装在宿主机网络的ip包中,然后发送到c2的宿主机
  4. 进入c2的宿主机后,该ip包会由ipip驱动解封装,获取原始的ip包,然后根据c2宿主机中路由规则发送到calixxx。

如果此时查看calico相关信息如下所示:

[root@k8s-node-1 ~]# calicoctl get ippool -o wide
NAME                  CIDR          NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ippool   172.16.0.0/16   true   Always     Never       false      all()

[root@k8s-x86-1 ~]#
[root@k8s-x86-1 ~]# calicoctl node status
Calico process is running.

IPv4 BGP status
+--------------+-------------------+-------+------------+-------------+
| PEER ADDRESS |     PEER TYPE     | STATE |   SINCE    |     INFO    |
+--------------+-------------------+-------+------------+-------------+
| 172.18.8.130 | node-to-node mesh | up    | 2022-03-18 | Established |
+--------------+-------------------+-------+------------+-------------+

其中calicoctl get ippool -o wide还可以用calicoctl get ippool -o yaml来查看更加详细的信息。
tunl0 最后把IP包通过哪个网卡传送出本主机,需要看本机(假设本机主机名是A)的calico是通过哪个网卡和外部建立BGP连接的。可以通过查看上面的calicoctl node status来查看A的对端(假设是B),然后再到对端B上可以看到它和A的那个网卡建立了BGP连接。这个可以通过配置calico-node daemonset的IP autodetect相关属性来指定那块网卡来建立BGP连接(详见我的另一篇关于calico部署踩坑记录的博文)
c)。

IPIP通信实例

这里通过两个例子来说明IPIP是如何封包的,一个是nodeA上的POD到nodeB上的POD,另一个是主机nodeA直接到nodeB上的POD.先根据前面的原理来推测一下这种情况下数据包如何走的呢?封包解包的流程如何呢?

1. nodeA上的POD到nodeB上的POD

主机名 容器名 容器IP
x86-1(172.18.8.130) net.perf-7db66c46c7-8jgnr 10.161.252.45
x86-2(172.18.8.129) net.nginx-database-7964bc9748-vbk5k 10.161.37.200

从容器perf去ping容器nginx,在各个关节口抓到的报文如下所示

从上图可以看出在tunl0上可以抓到的报文还是可以看到容器IP的,当tunl0封包后从eno3网口送出本机时就看不是容器IP了,能看到封包后的IP即eno3的IP也是建立BGP链接的网卡的IP. 可以通过命令ip -d link show tunl0来查看tunl0的详细信息。下图是一个k8s计算节点上的calico-node和另一个节点的calico-node建立了IPIP tunnel的tunl0信息:

2. nodeA到nodeB上的POD

非计算节点(172.18.8.210) 安装了calico,其 tunl0 IP为 10.161.99.128
x86-1(172.18.8.130)上的Pod: net.perf-7db66c46c7-8jgnr,IP 为10.161.252.45
从172.18.8.210是直接ping容器perf

从上图可以看出包头被改为了tunl0的IP,并被hostIP封包了。

下面,简述一下calico-node代码中如何给它创建的容器配置 IP address, route 的过程,网络架构还是上面的那个图:

# k8s-1 [echo 1 > /proc/sys/net/ipv4/conf/cali-xxx/proxy_arp]
ip link add cali-xxx type veth peer name eth0
ip netns add ns0
ip link set eth0 netns ns0 
ip netns exec ns0 ip a add 192.168.1.2/24 dev eth0
ip netns exec ns0 ip link set eth0 up
ip netns exec ns0 ip route add 169.254.1.1 dev eth0 scope link
ip netns exec ns0 ip route add default via 169.254.1.1 dev eth0
ip link set cali-xxx up
ip route add 192.168.1.2 dev cali-xxx scope link
ip route add 192.168.5.2/26 via 172.12.1.11 dev tunl0
# k8s-2 [echo 1 > /proc/sys/net/ipv4/conf/cali-yyy/proxy_arp]
ip link add cali-yyy type veth peer name eth0
ip netns add ns1
ip link set eth0 netns ns1
ip netns exec ns1 ip a add 192.168.5.2/24 dev eth0
ip netns exec ns1 ip link set eth0 up
ip netns exec ns1 ip route add 169.254.1.1 dev eth0 scope link
ip netns exec ns1 ip route add default via 169.254.1.1 dev eth0
ip link set cali-yyy up
ip route add 192.168.5.2 dev cali-yyy scope link
ip route add 192.168.1.2/26 via 172.12.1.10 dev tunl0

BGP模式

Calico常用的模式也是它的王牌模式BGP模式,虽然说calico有BGP模式和IPIP模式但是并不是说IPIP模式就不用建立BGP连接了,IPIP模式也是需要建立BGP连接的(可以通过抓取179端口的报文验证),只不过建立BGP链接的目标比较清晰,就是对端的tunl0对应的网卡。BGP模式对于IPIP模式的优点也并不是简单的描述为“可以跨节点通信”,IPIP模式也是可以跨节点通信的,只要两个节点能互相连通就可以。而BGP的优点是可以通过指定BGP的对端Peer,一般是交换机,那么只要能接入这个交换机的host或者PC都能通过路由的方式连通calico的其他节点上的容器。也就是说BGP的可扩展的网络拓扑更灵活。
在使用BGP时,第一步需要做的就是梳理好你的网络拓扑,然后根据这个拓扑再去进行相关的配置。这里以一个较为简单的例子来说明,就是所有的计算节点都链接到同一个交换机,即它们的BGP Peer都是同一个交换机,也就是中心型拓扑。这里暂且不考虑高可用性,仅举例说明。这里使用的BGP为IBGP.
接下来,就是要对calico进行配置,因为每个节点都和交换机建立BGP链接,那么每个节点的配置都是一样的。首先,修改calico-node daemonset的配置,其中最重要的配置如下所示:

        - name: IP
          value: autodetect
        - name: IP_AUTODETECTION_METHOD
          value: cidr=10.20.0.0/24
        - name: CALICO_IPV4POOL_IPIP
          value: "Off"

这段配置的含义是关闭IPinIP模式,并让calico-node自动探测建立BGP的网卡,条件是这个网卡的IP地址是10.20.0.0/24段的。注意,这里的IP值要是autodetect才能自动匹配。另外,还需要指定BGP configurateion (BGP AS number)和 BGP Peer.这里简单示例一下,更多信息可参考https://projectcalico.docs.tigera.io/reference/resources/bgpconfig

[root@master-1]# cat bgpcfg.yaml 
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  logSeverityScreen: Info
  nodeToNodeMeshEnabled: false
  asNumber: 64512
[root@master-1]# cat bgppeer.yaml 
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: node1-peer
spec:
  # node is the node hostname
  node: master-1
  # peerIP is the IP of BGP connection remote peer
  peerIP: 10.20.0.1
  asNumber: 64512

[root@master-1 ]# 

以上的bgpconfiguraton和bgppeer YAML文件都用calicoctl apply -f xxx 来生效. 最终和交换机10.20.0.1建立BGP链接。查看calico-node 的状态为:

[root@master-1]# kubectl exec -ti -n kube-system        calicoctl-77vms calicoctl node status
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Calico process is running.

IPv4 BGP status
+--------------+---------------+-------+------------+-------------+
| PEER ADDRESS |   PEER TYPE   | STATE |   SINCE    |    INFO     |
+--------------+---------------+-------+------------+-------------+
| 10.20.0.1    | node specific | up    | 2022-05-05 | Established |
+--------------+---------------+-------+------------+-------------+

IPv6 BGP status
No IPv6 peers found.

这种模式需要对端的交换机支持BGP协议,并且设置BGP的as number要和calico-node的AS number一致(因为是IBGP),另外,因为BGP链接是两端对等的关系,所以交换机也需要指定BGP Peer否则主机端的BGP链接建立不起来, 这里以华为交换机为例,说明一下交换机端的配BGP配置方法:

vlan 10
quit
interface vlanif 10     #创建vlan 10 

ip address 10.20.0.1 24 #vlan 10 的IP是10.20.0.1,也是交换机发布BGP连接的IP
display interface brief #看是哪个口插上线的,假如是22和24端口
interface GigabitEthernet 0/0/22 #下面三个命令是把22端口加到vlan10中来,这样10.20.0.2才能和10.20.0.1通
port link-type access
port default vlan 10

# 发起bgp连接
bgp 64512
peer 10.20.0.2 as-number 64512 #向10.20.0.2发起bgp连接
peer 10.20.0.3 as-number 64512 #当添加新节点时,需要在交换机上添加该peer

display bgp peer                 #连接状态显示为Established才说明连接建立完成
display bgp routing-table        #查看bgp路由

至此,calico的BGP应该就可以工作了。BGP模式下数据包的路径单纯依靠路由进行转发,网络拓扑更自由,IPinIP模式则是有个封包的过程,因此也是一种overlay的方式,封好的包再按照路由进行转发,到达目的节点还有个解包的过程,因此比BGP模式稍微低效一些,但是封包比vxlan添加的字段少因此比vxlan高效一些。但IPinIP模式部署起来相对简单,各有利弊。
另外,需要说明的一点是,容器使用的网络技术中基本都是通过veth-pair把容器和外界连通起来的,然后外面或者通过直接路由(BGP)或者通过overlay(vxlan、IPinIP等)的方式再出宿主机。而仅仅veth-pair就会造成10%的网络延迟(QPS大约减少5%),这是因为虽然 veth 是一个虚拟的网络接口,但是在接收数据包的操作上,这个虚拟接口和真实的网路接口并没有太大的区别。这里除了没有硬件中断的处理,其他操作都差不多,特别是软中断(softirq)的处理部分其实就和真实的网络接口是一样的.在对外发送数据的时候,peer veth 接口都会 raise softirq 来完成一次收包操作,这样就会带来数据包处理的额外开销。如果要减小容器网络延时,就可以给容器配置 ipvlan/macvlan 的网络接口来替代 veth 网络接口。Ipvlan/macvlan 直接在物理网络接口上虚拟出接口,在发送对外数据包的时候可以直接通过物理接口完成,没有节点内部类似 veth 的那种 softirq 的开销。容器使用 ipvlan/maclan 的网络接口,它的网络延时可以非常接近物理网络接口的延时。对于延时敏感的应用程序,我们可以考虑使用 ipvlan/macvlan 网络接口的容器。不过,由于 ipvlan/macvlan 网络接口直接挂载在物理网络接口上,对于需要使用 iptables 规则的容器,比如 Kubernetes 里使用 service 的容器,就不能工作了。这就需要你结合实际应用的需求做个判断,再选择合适的方案。

posted @ 2022-05-08 10:42  JaneySJ  阅读(8487)  评论(0编辑  收藏  举报