在Kubernetes中设计了一种网络模型,要求无论容器运行在集群中的哪个节点,所有容器都能通过一个扁平的网络平面进行通信,即在同一IP网络中。需要注意的是:在K8S集群中,IP地址分配是以Pod对象为单位,而非容器,同一Pod内的所有容器共享同一网络名称空间。
了解Docker的朋友们都应该清楚,Docker容器的原生网络模型主要有3种:Bridge(桥接)、Host(主机)、none。
1、使用以下命令查看docker网络
docker network ls
NETWORK ID NAME DRIVER SCOPE
2d636f1440a9 bridge bridge local
f7a1594a8e7c host host local
fa3518434b75 none null local
2、none网络,在该网络下的容器仅有lo网卡,属于封闭式网络,通常用于对安全性要求较高并且不需要联网的应用
docker run -it --network=none busybox
/ # ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
3、host网络,共享宿主机的网络名称空间,容器网络配置和host一致,但是存在端口冲突的问题
docker run -it --network=host busybox
/ # ipaddr
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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 08:00:27:a4:54:60 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.197/24 brd 192.168.10.255 scope global noprefixroute enp0s3
valid_lft forever preferred_lft forever
inet6 fe80::97f7:6981:54d9:5f3e/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:66:59:f3:fb brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4、bridge网络,Docker安装完成时会创建一个名为docker0的linux bridge,不指定网络时,创建的网络默认为桥接网络,都会桥接到docker0上
yum install bridge-utils -y
brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02426659f3fb no
运行一个centos7容器
docker run -it centos:7 bash
查看宿主机网桥
brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02426659f3fb no veth4086dd7
一个新的网络接口veth3f1b114桥接到了docker0上,veth3f1b114就是新创建的容器的虚拟网卡。进入容器查看其网络配置:
docker exec -it 781b6c8e5aa7 bash
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
33: eth0@if34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
从上可以看到容器内有一个网卡eth0@if34,实际上eth0@if39和veth4086dd7是一对veth pair。veth pair是一种成对出现的特殊网络设备,可以想象它们由一根虚拟的网线进行连接的一对网卡,eth0@if34在容器中,veth4086dd7挂在网桥docker0上,最终的效果就是eth0@if34也挂在了docker0上。
桥接式网络是目前较为流行和默认的解决方案。但是这种方案的弊端是无法跨主机通信的,仅能在宿主机本地进行,而解决该问题的方法就是NAT。所有接入到该桥接设备上的容器都会被NAT隐藏,它们发往Docker主机外部的所有流量都会经过源地址转换后发出,并且默认是无法直接接受节点之外的其他主机发来的请求。当需要接入Docker主机外部流量,就需要进行目标地址转换甚至端口转换将其暴露在外部网络当中。如下图:
容器内的属于私有地址,需要在左侧的主机上的eth0上进行源地址转换,而右侧的地址需要被访问,就需要将eth0的地址进行NAT转换。SNAT—->DNAT
这样的通信方式会比较麻烦,从而需要借助第三方的网络插件实现这样的跨主机通信的网络策略。
在K8S上的网络通信包含以下几类:
K8S网络的实现不是集群内部自己实现,而是依赖于第三方网络插件—-CNI(Container Network Interface)
flannel、calico、canel等是目前比较流行的第三方网络插件。
虚拟网桥、多路复用(MacVLAN)、硬件交换(SR-IOV)
无论是上面的哪种方式在容器当中实现,都需要大量的操作步骤,而K8S支持CNI插件进行编排网络,以实现Pod和集群网络管理功能的自动化。每次Pod被初始化或删除,kubelet都会调用默认的CNI插件去创建一个虚拟设备接口附加到相关的底层网络,为Pod去配置IP地址、路由信息并映射到Pod对象的网络名称空间。
在配置Pod网络时,kubelet会在默认的/etc/cni/net.d/目录中去查找CNI JSON配置文件,然后通过type属性到/opt/cni/bin中查找相关的插件二进制文件,如下面的"portmap"。然后CNI插件调用IPAM插件(IP地址管理插件)来配置每个接口的IP地址:
[root@localhost ~]# cat /etc/cni/net.d/10-flannel.conflist
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
CNI主要是定义容器网络模型规范,链接容器管理系统和网络插件,两者主要通过上面的JSON格式文件进行通信,实现容器的网络功能。CNI的主要核心是:在创建容器时,先创建好网络名称空间(netns),然后调用CNI插件为这个netns配置网络,最后在启动容器内的进程。
常见的CNI网络插件包含以下几种:
在各节点上的Docker主机在docker0上默认使用同一个子网,不同节点的容器都有可能会获取到相同的地址,那么在跨节点通信时就会出现如下问题:
针对以上两个问题flannel的解决办法如下: 1、预留一个使用网络,如10.244.0.0/16,然后自动为每个节点的Docker容器引擎分配一个子网,如10.244.1.0/24和10.244.2.0/24,并将分配信息保存在etcd持久存储。 2、Flannel采用不同类型的后端网络模型进行处理
由于官方已经不推荐使用 UDP 模式,性能相对较差。这里不做介绍,只介绍上面其他的两种网络模型
VxLAN(Virtual extensible Local Area Network)虚拟可扩展局域网,采用MAC in UDP封装方式,具体的实现方式为:
1、将虚拟网络的数据帧添加到VxLAN首部,封装在物理网络的UDP报文中 2、以传统网络的通信方式传送该UDP报文 3、到达目的主机后,去掉物理网络报文的头部信息以及VxLAN首部,并交付给目的终端
跨节点的Pod之间的通信就是以上的一个过程,整个过程中通信双方对物理网络是没有感知的。如下网络图
VxLan设计原理如下:
在现有的三层网络之上,“覆盖”一层虚拟的、由内核VxLAN模块负责维护的二层网络,使得连接在这个VxLAN二层网络上的“主机”(虚拟机或容器都可以),可以像在同一个局域网(LAN)里那样自由通信。 为了能够在二层网络上打通“隧道”,VxLAN会在宿主机上设置一个特殊的网络设备作为“隧道”的两端,叫VTEP:VxLAN Tunnel End Point(虚拟隧道端点),flannel.1设备,就是VxLAN的VTEP,即有IP地址。为了能够将“原始IP包”封装并发送到正常的主机,VxLAN需要找到隧道的出口:宿主机的VTEP设备,这个设备信息,由宿主机的flanneld进程维护。
VTEP设备之间通过二层数据桢进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个L2数据桢,发送给目的VTEP设备(获取 MAC地址需要通过三层IP地址查询,这是ARP表的功能)
封装过程只是加了一个二层头,不会改变“原始IP包”的内容 这些VTEP设备的MAC地址,对宿主机网络来说没什么实际意义,称为内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据桢,好让它带着“内部数据桢”通过宿主机的eth0进行传输,Linux会在内部数据桢前面,加上一个VxLAN头,VxLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。 在Flannel中,VNI的默认值是1,这也是为什么宿主机的VTEP设备都叫flannel.1的原因
一个flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。 在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的,linux内核再在IP包前面加上二层数据桢头,把Node2的MAC地址填进去。这个MAC地址本身,是Node1的ARP表要学习的,需 Flannel维护,这时候Linux封装的“外部数据桢”的格式如下
然后Node1的flannel.1设备就可以把这个数据桢从eth0发出去,再经过宿主机网络来到Node2的eth0,Node2的内核网络栈会发现这个数据桢有VxLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据桢,根据VNI的值,所它交给Node2的flannel.1设备
VxLAN的部署可以直接在官方上找到其YAML文件,如下:
kca -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
# 查看flannel 运行状态
[root@localhost ~]# kubectl get daemonset -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-flannel-ds 1 1 1 1 1 <none> 67m
kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 104m
运行正常后,flanneld会在宿主机的/etc/cni/net.d目录下生成自已的配置文件,kubelet将会调用它。
网络插件运行成功后,Node状态才Ready
root@k8s01 ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s01 Ready master 35m v1.19.8
k8s02 Ready <none> 4m5s v1.19.8
flannel运行后,在各Node宿主机多了一个网络接口:
[root@localhost ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.244.0.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::fc50:86ff:fe05:94f4 prefixlen 64 scopeid 0x20<link>
ether fe:50:86:05:94:f4 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0
从上面的结果可以知道:
1、启动一个容器
for i in `seq 1 8`;do kubectl run busybox-$i --image=busybox --command sleep 10000000;done
2、查看pod
[root@k8s01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-1 1/1 Running 0 2m26s 10.244.1.22 k8s02 <none> <none>
busybox-2 1/1 Running 0 2m26s 10.244.1.23 k8s02 <none> <none>
busybox-3 1/1 Running 0 2m26s 10.244.1.24 k8s02 <none> <none>
busybox-4 1/1 Running 0 2m26s 10.244.1.25 k8s02 <none> <none>
busybox-5 1/1 Running 0 2m26s 10.244.1.26 k8s02 <none> <none>
busybox-6 1/1 Running 0 2m26s 10.244.1.27 k8s02 <none> <none>
busybox-7 1/1 Running 0 2m25s 10.244.0.11 k8s01 <none> <none>
busybox-8 1/1 Running 0 2m25s 10.244.0.12 k8s01 <none> <none>
3、可以看到,6个Pod都分别运行在node节点之上,2个pod运行在master上,在node节点上查看网络接口可以发现在各个节点上多了一个虚拟接口cni0,其ip地址为10.244.1.1。它是由flanneld创建的一个虚拟网桥叫cni0,在Pod本地通信使用。 这里需要注意的是,cni0虚拟网桥,仅作用于本地通信!!!!
[root@k8s02 ~]# ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.244.1.1 netmask 255.255.255.0 broadcast 10.244.1.255
inet6 fe80::2c60:e5ff:fe48:506b prefixlen 64 scopeid 0x20<link>
ether 2e:60:e5:48:50:6b txqueuelen 1000 (Ethernet)
RX packets 4 bytes 112 (112.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 446 (446.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4、flanneld为每个Pod创建一对veth虚拟设备,一端放在容器接口上,一端放在cni0桥上。 使用brctl查看该网桥:
[root@k8s02 ~]# brctl show cni0
bridge name bridge id STP enabled interfaces
cni0 8000.2e60e548506b no veth2cffb815
veth30af0abc
veth3e3daf57
veth648e5bfd
veth94d7d5fe
vethbeeb4a05
#宿主机上测试和pod通信情况
[root@k8s01 ~]# ping 10.244.1.22
PING 10.244.1.22 (10.244.1.22) 56(84) bytes of data.
64 bytes from 10.244.1.22: icmp_seq=1 ttl=63 time=0.592 ms
64 bytes from 10.244.1.22: icmp_seq=2 ttl=63 time=0.388 ms
64 bytes from 10.244.1.22: icmp_seq=3 ttl=63 time=0.395 ms
64 bytes from 10.244.1.22: icmp_seq=4 ttl=63 time=0.381 ms
64 bytes from 10.244.1.22: icmp_seq=5 ttl=63 time=0.384 ms
64 bytes from 10.244.1.22: icmp_seq=6 ttl=63 time=0.382 ms
64 bytes from 10.244.1.22: icmp_seq=7 ttl=63 time=0.382 ms
64 bytes from 10.244.1.22: icmp_seq=8 ttl=63 time=0.351 ms
64 bytes from 10.244.1.22: icmp_seq=9 ttl=63 time=0.358 ms
5、在现有的Flannel VxLAN网络中,主机上的Pod间通信,也是正常的,如master节点上的Pod访问另外一个pod:
[root@k8s01 ~]# kexec -it busybox-7 -n default sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # ping 10.244.1.22
PING 10.244.1.22 (10.244.1.22): 56 data bytes
64 bytes from 10.244.1.22: seq=0 ttl=62 time=0.647 ms
64 bytes from 10.244.1.22: seq=1 ttl=62 time=0.458 ms
64 bytes from 10.244.1.22: seq=2 ttl=62 time=0.482 ms
64 bytes from 10.244.1.22: seq=3 ttl=62 time=0.428 ms
64 bytes from 10.244.1.22: seq=4 ttl=62 time=0.415 ms
^C
--- 10.244.1.22 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.415/0.486/0.647 ms
6、可以看到是可以正常通信的,master上查看路由表信息:
# 查看设备接口
[root@k8s01 ~]# ip r
default via 172.28.95.253 dev eth0
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.28.80.0/20 dev eth0 proto kernel scope link src 172.28.81.7
# 查看路由表
[root@k8s01 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.28.95.253 0.0.0.0 UG 0 0 0 eth0
10.244.0.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0
10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.28.80.0 0.0.0.0 255.255.240.0 U 0 0 0 eth0
7、抓包分析
单只有一个结点时: 发送到10.244.0.0/24网段的数据报文不会经过flannel网卡,而是直接发给本机的cni0接口,发送数据到对应veth接口上。使用tcpdump进行 抓一下包,如下:
[root@k8s01 ~]# tcpdump -i flannel.1 -nn host 10.244.0.12
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
[root@k8s01 ~]# tcpdump -i cni0 -nn host 10.244.0.12
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on cni0, link-type EN10MB (Ethernet), capture size 262144 bytes
18:09:29.380824 IP 10.244.0.1 > 10.244.0.12: ICMP echo request, id 17565, seq 19, length 64
18:09:29.380858 IP 10.244.0.12 > 10.244.0.1: ICMP echo reply, id 17565, seq 19, length 64
18:09:30.380828 IP 10.244.0.1 > 10.244.0.12: ICMP echo request, id 17565, seq 20, length 64
18:09:30.380863 IP 10.244.0.12 > 10.244.0.1: ICMP echo reply, id 17565, seq 20, length 64
^C
12 packets captured
12 packets received by filter
0 packets dropped by kernel
当有多个节点时 发送到10.244.1.0/24的网段的数据报文发给本机的flannel接口,即进入二层隧道,然后对数据报文进行封装(封装VxLAN首部–>UDP首部–>IP首部–>以太网首部),到达目标Node节点后,由目标Node上的flannel.1进行解封装。使用tcpdump进行 抓一下包,如下:
[root@k8s01 ~]# tcpdump -i flannel.1 -nn host 10.244.1.22
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
18:18:44.586179 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 20, seq 20, length 64
18:18:44.586572 IP 10.244.1.22 > 10.244.0.11: ICMP echo reply, id 20, seq 20, length 64
18:18:45.586322 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 20, seq 21, length 64
18:18:45.586688 IP 10.244.1.22 > 10.244.0.11: ICMP echo reply, id 20, seq 21, length 64
^C
20 packets captured
20 packets received by filter
0 packets dropped by kernel
显然,节点内的容器间通信通过 cni0 网桥就能完成,不涉及任何VXLAN报文的封包解包。例如在下面的图例中,Node1的子网为10.244.0.1/24, PodA 10.244.0.11 和 PodB 10.224.0.12通过 cni0 网桥实现互通。
不同node上的pod通信流程如下:
下面重点分析一下跨节点的容器通信过程。假设有两个节点Node1和Node2,其中Node1的PodA要跟Node2的PodB通信,则它们之间的通信过程如下图所示:
大概总结一下整个过程:
flanneld 从 etcd 中可以获取所有节点的子网情况,以此为依据为各节点配置路由,将属于非本节点的子网IP都路由到 flannel.1 处理,本节点的子网路由到 cni0 网桥处理,如果节点信息有变化, flanneld 也会同步的对路由信息做修改。
VXLAN的封包是将二层以太网帧封装到四层UDP报文中的过程
先来看一下vxlan的报文形式
要生成原始的L2帧, flannel.1 需要得知:
内层的源/目的IP地址是已知的,即为PodA/PodB的PodIP,在图例中,分别为10.224.0.11和10.224.1.23。 内层源/目的MAC地址要结合路由表和ARP表来获取。根据路由表得知:
1、下一跳地址是10.224.1.0,关联ARP表,得到下一跳的MAC地址,也就是目的MAC地址:Node2_flannel.1_MAC; 2、报文要从 flannel.1 虚拟网卡发出,因此源MAC地址为 flannel.1 的MAC地址。
要注意的是,这里ARP表的表项并不是通过ARP学习得到的,而是 flanneld 预先为每个节点设置好的,由 flanneld负责维护,没有过期时间。
[root@k8s01 ~]# ip n |grep flannel
10.244.1.0 dev flannel.1 lladdr 66:b0:1e:b3:71:e6 PERMANENT
有了上面的信息,flannel.1 就可以构造出内层的2层以太网帧:
要将原始L2帧封装成VXLAN UDP报文,flannel.1 还需要填充源/目的IP地址。前面提到,VTEP是VXLAN隧道的起点或终点。因此,目的IP地址即为对端VTEP的IP地址,通过FDB表获取。在FDB表中,dest字段表示的即为VXLAN隧道目的端点(对端VTEP)的IP地址,也就是VXLAN UDP报文的目的IP地址。FDB表也是由 flanneld 在每个节点上预设并负责维护的。
FDB表(Forwarding database)用于保存二层设备中MAC地址和端口的关联关系,就像交换机中的MAC地址表一样。在二层设备转发二层以太网帧时,根据FDB表项来找到对应的端口。例如cni0网桥上连接了很多veth pair网卡,当网桥要将以太网帧转发给Pod时,FDB表根据Pod网卡的MAC地址查询FDB表,就能找到其对应的veth网卡,从而实现联通
可以使用 bridge fdb show 查看FDB表:
[root@k8s01 ~]# bridge fdb show | grep flannel.1
66:b0:1e:b3:71:e6 dev flannel.1 dst 172.28.81.9 self permanent
源IP地址信息来自于 flannel.1 网卡设置本身,根据 local 192.168.50.2 可以得知源IP地址为192.168.50.2。
[root@k8s01 ~]# ip -d a show flannel.1
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 66:c0:b5:49:b0:ce brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 1 local 172.28.81.7 dev eth0 srcport 0 0 dstport 8472 nolearning ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 10.244.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::64c0:b5ff:fe49:b0ce/64 scope link
valid_lft forever preferred_lft forever
至此, flannel.1 已经得到了所有完成VXLAN封包所需的信息,最终通过 eth0 发送一个VXLAN UDP报文:
Flannel的VXLAN模式通过静态配置路由表,ARP表和FDB表的信息,结合VXLAN虚拟网卡 flannel.1 ,实现了一个所有Pod同属一个大二层网络的VXLAN网络模型。
VXLAN是Linux内核本身支持的一种网络虚拟化技术,是内核的一个模块,在内核态实现封装解封装,构建出覆盖网络,其实就是一个由各宿主机上的Flannel.1设备组成的虚拟二层网络。
由于VXLAN由于额外的封包解包,导致其性能较差,所以Flannel就有了host-gw模式,即把宿主机当作网关,除了本地路由之外没有额外开销,性能和calico差不多,由于没有叠加来实现报文转发,这样会导致路由表庞大。因为一个节点对应一个网络,也就对应一条路由条目。
host-gw虽然VXLAN网络性能要强很多,但是种方式有个缺陷: 要求各物理节点必须在同一个二层网络中。物理节点必须在同一网段中。这样会使得一个网段中的主机量会非常多,万一发一个广播报文就会产生干扰。在私有云场景下,宿主机不在同一网段是很常见的状态,所以就不能使用host-gw了。
VXLAN还有另外一种功能,VXLAN也支持类似host-gw的玩法,如果两个节点在同一网段时使用host-gw通信,如果不在同一网段中,即 当前pod所在节点与目标pod所在节点中间有路由器,就使用VXLAN这种方式,使用叠加网络。 结合了Host-gw和VXLAN,这就是VXLAN的Direct routing模式
修改kube-flannel.yml文件,将flannel的configmap对象改为:
[root@k8s01 ~]# vim kube-flannel.yml
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true
}
}
[root@k8s01 ~]# kca -f kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged configured
clusterrole.rbac.authorization.k8s.io/flannel unchanged
clusterrolebinding.rbac.authorization.k8s.io/flannel unchanged
serviceaccount/flannel unchanged
configmap/kube-flannel-cfg configured
daemonset.apps/kube-flannel-ds unchanged
# 查看路由信息
[root@k8s01 ~]# ip route
default via 172.28.95.253 dev eth0
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 172.28.81.9 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.28.80.0/20 dev eth0 proto kernel scope link src 172.28.81.7
[root@k8s02 ~]# ip route
default via 172.28.95.253 dev eth0
10.244.0.0/24 via 172.28.81.7 dev eth0
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.28.80.0/20 dev eth0 proto kernel scope link src 172.28.81.9
从上面的结果可以看到,发往10.244.1.0/24和10.244.1.0/24的包都是直接经过eth0网络接口直接发出去的,这就是Directrouting。如果两个节点是跨网段的,则flannel自动降级为VxLAN模式。
此时,在各个集群节点上执行“iptables -nL”命令 可以 看到, iptables filter 表 的 FORWARD 链 上 由其 生成了如下两条转发规则, 它显式放行了10.244.0.0/16
网络进出 的所有报文,用于确保由物理接口接收或发送的目标地址或源地址为10.244.0.0/16
网络的所有报文均能够正常通行。这些是Direct Routing
模式得以实现的必要条件:
ACCEPT all -- 10.244.0.0/16 0.0.0.0/0 /* flanneld forward */
ACCEPT all -- 0.0.0.0/0 10.244.0.0/16 /* flanneld forward */
再在此之前创建的Pod和宿主机上进行ping测试,可以看到在flannel.1接口上已经抓不到包了,在eth0上可以用抓到ICMP的包,如下
[root@k8s01 ~]# tcpdump -i flannel.1 -nn host 10.244.1.22
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
[root@k8s01 ~]# tcpdump -i eth0 -nn host 10.244.1.22
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:19:27.475984 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 59, seq 56, length 64
23:19:28.476118 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 59, seq 57, length 64
23:19:29.476269 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 59, seq 58, length 64
23:19:30.476465 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 59, seq 59, length 64
23:19:31.476611 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 59, seq 60, length 64
23:19:32.476748 IP 10.244.0.11 > 10.244.1.22: ICMP echo request, id 59, seq 61, length 64
^C
6 packets captured
14 packets received by filter
0 packets dropped by kernel
Flannel除了上面2种数据传输的方式以外,还有一种是host-gw的方式,host-gw后端是通过添加必要的路由信息使用节点的二层网络直接发送Pod的通信报文。它的工作方式类似于Directrouting的功能,但是其并不具备VxLan的隧道转发能力。
编辑kube-flannel的配置清单,将ConfigMap资源kube-flannel-cfg的data字段中网络配置进行修改,如下:
[root@k8s01 ~]# vim kube-flannel.yml
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw"
}
}
[root@k8s01 ~]# kca -f kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged configured
clusterrole.rbac.authorization.k8s.io/flannel unchanged
clusterrolebinding.rbac.authorization.k8s.io/flannel unchanged
serviceaccount/flannel unchanged
configmap/kube-flannel-cfg configured
daemonset.apps/kube-flannel-ds unchanged
配置完成后,各节点会生成类似directrouting一样的 路由和iptables规则,用于实现二层转发Pod网络的通信报文,省去了隧道转发模式的额外开销。但是存在的问题点是,对于不在同一个二层网络的报文转发,host-gw是无法实现的。延续上面的例子,进行抓包查看:
master上的Pod:10.244.0.11向node01节点上的Pod:10.244.1.22发送ICMP报文
[root@k8s01 ~]# ip route
default via 172.28.95.253 dev eth0
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 172.28.81.9 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.28.80.0/20 dev eth0 proto kernel scope link src 172.28.81.7
[root@k8s02 ~]# ip route
default via 172.28.95.253 dev eth0
10.244.0.0/24 via 172.28.81.7 dev eth0
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.28.80.0/20 dev eth0 proto kernel scope link src 172.28.81.9
#进行ping包测试
[root@k8s01 ~]# ping 10.244.1.22
#在eth0上进行抓包
[root@k8s01 ~]# tcpdump -i eth0 -nn host 10.244.1.22
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:25:52.985827 IP 172.28.81.7 > 10.244.1.22: ICMP echo request, id 1, seq 71, length 64
23:25:53.985836 IP 172.28.81.7 > 10.244.1.22: ICMP echo request, id 1, seq 72, length 64
23:25:54.985828 IP 172.28.81.7 > 10.244.1.22: ICMP echo request, id 1, seq 73, length 64
23:25:55.985827 IP 172.28.81.7 > 10.244.1.22: ICMP echo request, id 1, seq 74, length 64
23:25:56.985838 IP 172.28.81.7 > 10.244.1.22: ICMP echo request, id 1, seq 75, length 64
23:25:57.985847 IP 172.28.81.7 > 10.244.1.22: ICMP echo request, id 1, seq 76, length 64
^C
6 packets captured
17 packets received by filter
0 packets dropped by kernel
该模式下,报文转发的相关流程如下:
工作模式流程图如下:
在host-gw模式下,由于不涉及VXLAN的封包解包,不再需要flannel.1虚机网卡。 flanneld 负责为各节点设置路由 ,将对应节点Pod子网的下一跳地址指向对应的节点的IP,如图中路由表所示。
由于没有封包解包带来的消耗,host-gw是性能最好的。不过一般在云环境下,都不支持使用host-gw的模式,在私有化部署的场景下,可以考虑
以上就是Flannel网络模型的三种工作模式,但是flannel自身并不具备为Pod网络实现网络策略和网络通信隔离的功能,为此只能借助于Calico联合统一的项目Calnal项目进行构建网络策略的功能。
Calico 是一种容器之间互通的网络方案。在虚拟化平台中,比如 OpenStack、Docker 等都需要实现 workloads 之间互连,但同时也需要对容器做隔离控制,就像在 Internet 中的服务仅开放80端口、公有云的多租户一样,提供隔离和管控机制。而在多数的虚拟化平台实现中,通常都使用二层隔离技术来实现容器的网络,这些二层的技术有一些弊端,比如需要依赖 VLAN、bridge 和隧道等技术,其中 bridge 带来了复杂性,vlan 隔离和 tunnel 隧道则消耗更多的资源并对物理环境有要求,随着网络规模的增大,整体会变得越加复杂。我们尝试把 Host 当作 Internet 中的路由器,同样使用 BGP 同步路由,并使用 iptables 来做安全访问策略,最终设计出了 Calico 方案。
优势
Calico把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以Calico方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。
对于控制平面,它每个节点上会运行两个主要的程序,一个是Felix,它会监听ECTD中心的存储,从它获取事件,比如说用户在这台机器上加了一个IP,或者是分配了一个容器等。接着会在这台机器上创建出一个容器,并将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。另外一部分是一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,你们路由的时候得到这里来。
由于Calico是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率可能是所有方案中最高的,因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。
1、IPIP
从字面来理解,就是把一个IP数据包又套在一个IP包里,即把 IP 层封装到 IP 层的一个 tunnel。它的作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需 IP,而这个 ipip 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来。
2、BGP
BGP(border gateway protocol)是外部路由协议(边界网关路由协议),用来在AS之间传递路由信息是一种增强的距离矢量路由协议(应用场景),基本功能是在自治系统间自动交换无环路的路由信息,通过交换带有自治系统号序列属性的路径可达信息,来构造自治系统的拓扑图,从而消除路由环路并实施用户配置的路由策略。
边界网关协议(Border Gateway Protocol, BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或‘前缀’表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。BGP,通俗的讲就是讲接入到机房的多条线路(如电信、联通、移动等)融合为一体,实现多线单IP,BGP 机房的优点:服务器只需要设置一个IP地址,最佳访问路由是由网络上的骨干路由器根据路由跳数与其它技术指标来确定的,不会占用服务器的任何系统。
实际上,Calico 项目提供的 BGP 网络解决方案,与 Flannel 的 host-gw 模式几乎一样。也就是说,Calico也是基于路由表实现容器数据包转发,但不同于Flannel使用flanneld进程来维护路由信息的做法,而Calico项目使用BGP协议来自动维护整个集群的路由信息
1、下载calico安装文件,修改网段
curl -LO https://docs.projectcalico.org/archive/v3.15/manifests/calico.yaml
将配置文件中CALICO_IPV4POOL_CIDR注释取消,并将值修改为: 10.244.0.0/16
2、安装calico
kca -f calico.yaml
3、查看是否正常
[root@localhost ~]# kubectl get ds -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 1 1 1 1 1 kubernetes.io/os=linux 13m
kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 43m
# 生成对应的tul0网桥和对应veth pair对
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:a4:54:60 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.118/24 brd 192.168.10.255 scope global noprefixroute enp0s3
valid_lft forever preferred_lft forever
inet6 fe80::97f7:6981:54d9:5f3e/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:1b:a1:4b:4e brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: cali788093a9dfd@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
inet 10.244.73.64/32 brd 10.244.73.64 scope global tunl0
valid_lft forever preferred_lft forever
6: cali01dd37f65d2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
7: calida0b2d07b71@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
1、启动一个DomeSet资源,配置文件如下(nginx-des.yaml):
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-daemonset
labels:
app: nginx-daemonset
spec:
selector:
matchLabels:
app: nginx-daemonset
template:
metadata:
labels:
app: nginx-daemonset
spec:
containers:
- name: nginx-daemonset
image: nginx:alpine
2、查看pod运行情况
[root@iZj6cdsbgjsfiq4jznz7ziZ ~]# kubectl get po -n kube-system -o wide|grep nginx
nginx-daemonset-4cdfj 1/1 Running 0 118s 10.244.73.68 k8s01 <none> <none>
nginx-daemonset-djlj9 1/1 Running 0 2m31s 10.244.236.129 k8s02 <none> <none>
3、在master节点上的podA(10.244.73.68)测试与node(10.244.236.129)节点上的podB连通性
[root@iZj6cdsbgjsfiq4jznz7ziZ ~]# kexec -it nginx-daemonset-4cdfj -n kube-system sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # ping 10.244.236.129
PING 10.244.236.129 (10.244.236.129): 56 data bytes
64 bytes from 10.244.236.129: seq=0 ttl=62 time=0.627 ms
64 bytes from 10.244.236.129: seq=1 ttl=62 time=0.431 ms
64 bytes from 10.244.236.129: seq=2 ttl=62 time=0.419 ms
64 bytes from 10.244.236.129: seq=3 ttl=62 time=0.513 ms
64 bytes from 10.244.236.129: seq=4 ttl=62 time=0.455 ms
64 bytes from 10.244.236.129: seq=5 ttl=62 time=0.410 ms
^C
--- 10.244.236.129 ping statistics ---
6 packets transmitted, 6 packets received, 0% packet loss
round-trip min/avg/max = 0.410/0.475/0.627 ms
PodA 访问 PodB 流程如下:
数据包从 PodA 出到达Veth Pair另一端(宿主机上,以cali前缀开头)。
进入IP隧道设备(tunl0),由Linux内核IPIP驱动封装,把源容器ip换成源宿主机ip,目的容器ip换成目的主机ip,这样就封装成 k8s01 到 k8s02 的数据包。
此时包的类型:
原始IP包:
源IP:10.244.73.68
目的IP:10.244.236.129
TCP:
源IP: 172.28.81.11
目的iP:172.28.81.12
数据包经过路由器三层转发到 Node2。
k8s02 收到数据包后,网络协议栈会使用IPIP驱动进行解包,从中拿到原始IP包。
然后根据路由规则,将数据包转发给cali设备,从而到达 PodB
实际流程如下:
1、进入master上的podA容器查看路由表
/ # 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
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1440 qdisc noqueue state UP
link/ether 1e:b1:8f:36:03:43 brd ff:ff:ff:ff:ff:ff
inet 10.244.73.68/32 scope global eth0
valid_lft forever preferred_lft forever
eth0@if8 为在宿主机上创建的虚拟网桥的一端,另一端为 cali23efcac8988
[root@iZj6cdsbgjsfiq4jznz7ziZ ~]# route -n |grep 10.244.73.68
10.244.73.68 0.0.0.0 255.255.255.255 UH 0 0 0 cali23efcac8988
2、去往podB的数据包,由podA经过网桥设备转发到podA宿主机上,宿主机上查询路由表
[root@iZj6cdsbgjsfiq4jznz7ziZ ~]# route -n|grep 10.244.236
10.244.236.128 172.28.81.12 255.255.255.192 UG 0 0 0 tunl0
可以看到 podB (pod-ip 10.244.236.129) 的下一跳是172.28.81.12 ,也就是 podB 所在的宿主机ip地址,网络接口是 tunl0
tunl0 设备是一个ip隧道(ip tunnel)设备,技术原理是IPIP,ip包进入ip隧道后会被linux内核的IPIP驱动接管并重新封装(伪造)成去宿主机网络的ip包,然后发送出去。这样原始ip包经过封装后下一跳地址就是 172.28.81.12
3、数据包到达 podB 的宿主机 172.28.81.12 后,经过解包查找 10.244.236.129 的路由为网桥设备 calid733d03af9b
[root@k8s02 ~]# route -n |grep 10.244.236.129
10.244.236.129 0.0.0.0 255.255.255.255 UH 0 0 0 calid733d03af9b
4、最终 10.244.236.129 经过网桥设备 cali0393db3e4a6 被转发到容器 podB 内部,返回的数据包路径也是一样。
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1440 qdisc noqueue state UP
link/ether 26:19:7d:29:38:d7 brd ff:ff:ff:ff:ff:ff
inet 10.244.236.129/32 scope global eth0
valid_lft forever preferred_lft forever
全互联模式 每一个BGP Speaker都需要和其他BGP Speaker建立BGP连接,这样BGP连接总数就是N^2,如果数量过大会消耗大量连接。如果集群数量超过100台官方不建议使用此种模式
RR模式 中会指定一个或多个BGP Speaker为RouterReflection,它与网络中其他Speaker建立连接,每个Speaker只要与Router Reflection建立BGP就可以获得全网的路由信息。在calico中可以通过Global Peer实现RR模式
1、修改calico配置文件, 配置如下内容
- name: CALICO_IPV4POOL_IPIP
value: Always
- name: CALICO_AUTODETECTION_METHOD
value: interface=eth0
2、重新安装calico服务
kca -f calico.yaml
PodA 访问 PodB 流程如下
其中,这里最核心的 下一跳 路由规则,就是由 Calico 的 Felix 进程负责维护的。这些路由规则信息,则是通过 BGP Client 中 BIRD 组件,使用 BGP 协议来传输。
不难发现,Calico 项目实际上将集群里的所有节点,都当作是边界路由器来处理,它们一起组成了一个全连通的网络,互相之间通过 BGP 协议交换路由规则。这些节点,我们称为 BGP Peer。
而 Flannel host-gw 和 Calico 的唯一不一样的地方就是当数据包下一跳到达node2节点容器时发生变化,并且出数据包也发生变化,知道它是从veth的设备流出,容器里面的数据包到达宿主机上,这个数据包到达k8s02之后,它又根据一个特殊的路由规则,这个会记录目的通信地址的cni网络,然后通过cali设备进去容器,这个就跟网线一样,数据包通过这个网线发到容器中,这也是一个二层的网络互通才能实现
Route Reflector 模式(RR)(路由反射)
Calico 维护的网络在默认是 (Node-to-Node Mesh)全互联模式,Calico集群中的节点之间都会相互建立连接,用于路由交换。但是随着集群规模的扩大,mesh模式将形成一个巨大服务网格,连接数成倍增加。这时就需要使用 Route Reflector(路由器反射)模式解决这个问题。确定一个或多个Calico节点充当路由反射器,让其他节点从这个RR节点获取路由信息。
在BGP中可以通过calicoctl node status看到启动是 node-to-node mesh 网格的形式,这种形式是一个全互联的模式,默认的BGP在k8s的每个节点担任了一个BGP的一个喇叭,一直吆喝着扩散到其他节点,随着集群节点的数量的增加,那么上百台节点就要构建上百台链接,就是全互联的方式,都要来回建立连接来保证网络的互通性,那么增加一个节点就要成倍的增加这种链接保证网络的互通性,这样的话就会使用大量的网络消耗,所以这时就需要使用Route reflector,也就是找几个大的节点,让他们去这个大的节点建立连接,也叫RR,也就是公司的员工没有微信群的时候,找每个人沟通都很麻烦,那么建个群,里面的人都能收到,所以要找节点或着多个节点充当路由反射器,建议至少是2到3个,一个做备用,一个在维护的时候不影响其他的使用。
优势
劣势