Write by lyc at 2022-5-27
一、容器网络模型回顾 1.Docker容器网络模型 图例
2.Docker网络模型涉及的名词 网络的命名空间:
Linux 在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命名空间中,彼此间无法通信。Docker 就利用这一特性,实现不同容器间的网络隔离。
容器只是宿主机上的一个进程,容器的隔离效果远不如虚拟机的隔离性,容器只是隔离了一部分的资源。“网络命名空间”的目的是为了让这个容器具备独立的网络协议栈,有自己独立的 ip,mac,端口。
什么是 veth设备对:
Veth 设备对的引入是为了实现在不同网络命名空间的通信。
Veth 设备对是容器与宿主机通信的桥梁,类似一跟网线(点到点),用于容器与宿主机网络命名空间的连接(打破网络命名空间隔离)。
当数据包到达宿主机的命名空间、网络协议栈,就可以利用宿主机 NAT 网络地址转换,将数据包发送到宿主机以外的网络(互联网)。
什么是 Iptables/Netfilter:
Iptables/Netfilter 在 linux 下是一个配置防火墙规则的工具,利用 linux 内核 Netfilter 实现的 ip包过滤。
Docker 使用 Netfilter 实现容器网络转发。
外部访问容器:DNAT 目的网络地址转换(改目标IP)
容器访问外部:SNAT 源网络地址转换(改源IP)
什么是 docker0(网桥):
网桥是一个二层网络设备,通过网桥可以将 Linux 支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信。
docker0
是容器的网桥,是虚拟的二层交换机。所有的容器接入到这个“交换机”上,因此同一台宿主机上的容器相互之间通信走的是二层网络,docker0
为容器转发数据包并且实现 APR 协议。
路由:
Linux 系统包含一个完整的路由功能,当IP层在处理数据发送或转发的时候,会使用路由表来决定发往哪里。
3.查看 veth 设备对 1 yum install bridge-utils -y
创建一个 busybox 容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ docker run -it busybox /bin/sh / eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:656 (656.0 B) TX bytes:0 (0.0 B) 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)
宿主机查看 veth 设备
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 $ ifconfig docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:caff:fed4:7ddb prefixlen 64 scopeid 0x20<link> ether 02:42:ca:d4:7d:db txqueuelen 0 (Ethernet) RX packets 11 bytes 682 (682.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 16 bytes 1534 (1.4 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.100.159 netmask 255.255.255.0 broadcast 192.168.100.255 inet6 fe80::20c:29ff:fe45:6a76 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:45:6a:76 txqueuelen 1000 (Ethernet) RX packets 3519981 bytes 730409886 (696.5 MiB) RX errors 0 dropped 12 overruns 0 frame 0 TX packets 3267636 bytes 379473606 (361.8 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 22 bytes 1279 (1.2 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 22 bytes 1279 (1.2 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 veth4e175a5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::78f1:4cff:fee4:8aad prefixlen 64 scopeid 0x20<link> ether 7a:f1:4c:e4:8a:ad txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8 bytes 656 (656.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 $ brctl show docker0 bridge name bridge id STP enabled interfaces docker0 8000.0242cad47ddb no veth4e175a5
4.K8S 网络模型:Pod 网络
Pod 是 K8s 最小调度单元,一个 Pod 由一个容器或多个容器组成,当有多个容器时,怎么都用一个Pod IP?
k8s 会在每个 Pod 里先启动一个 infra container 小容器,然后让其他的容器连接进来这个网络命名空间,然后其他容器看到的网络视图就完全一样了。即网络设备、IP地址、Mac地址等。在 Pod 的 IP 地址就是 infra container 的 IP 地址。
在 Kubernetes 中,每一个 Pod 都有一个真实的 IP 地址,并且每一个 Pod 都可以使用此 IP 地址与 其他 Pod 通信。 Pod之间通信会有两种情况:
两个Pod在同一个Node上
两个Pod在不同Node上
第一种情况:两个Pod在同一个Node上
同节点Pod之间通信道理与Docker网络一样的,如下图:
对 Pod1 来说,eth0 通过虚拟以太网设备(veth0)连接到 root namespace;
网桥 cbr0 中为 veth0 配置了一个网段。一旦数据包到达网桥,网桥使用ARP 协议解析出其正确的目标网段 veth1;
网桥 cbr0 将数据包发送到 veth1;
数据包到达 veth1 时,被直接转发到 Pod2 的 network namespace 中的 eth0 网络设备。
第二种情况:两个Pod在不同Node上
相比同节点Pod通信,这里源 Pod 发出的数据包需要传递到目标节点,但是源 Pod 并不知道目标 Pod 在哪个节点上?
因此,为了实现容器跨主机通信需求,就需要部署网络组件,这些网络组件都必须满足如下要求:(每个node有独立的网段)
一个Pod一个IP
所有的 Pod 可以与任何其他 Pod 直接通信
所有节点可以与所有 Pod 直接通信
Pod 内部获取到的 IP 地址与其他 Pod 或节点与其通信时的 IP 地址是同一个
5.K8S 网络模型: CNI(容器网络接口)
CNI GitHub
CNI
(Container Network Interface,容器网络接口):是一个容器网络规范,Kubernetes 网络采用的就是这个 CNI
规范,负责初始化 infra container
容器的网络设备。
CNI
二进制程序默认路径:/opt/cni/bin/
1 2 3 4 5 6 7 8 9 10 11 $ ls -l /opt/cni/bin/ total 142504 -rwxr-xr-x 1 root root 4159518 May 25 16:08 bandwidth -rwxr-xr-x 1 root root 41521152 May 25 16:08 calico -rwxr-xr-x 1 root root 41521152 May 25 16:08 calico-ipam -rwxr-xr-x 1 root root 3069556 May 25 16:08 flannel -rwxr-xr-x 1 root root 3614480 May 25 16:08 host-local -rwxr-xr-x 1 root root 41521152 May 25 16:08 install -rwxr-xr-x 1 root root 3209463 May 25 16:08 loopback -rwxr-xr-x 1 root root 3939867 May 25 16:08 portmap -rwxr-xr-x 1 root root 3356587 May 25 16:08 tuning
以 Flannel
网络组件为例,当部署 Flanneld
后,会在每台宿主机上生成它对应的 CNI
配置文件(它其实是一个 ConfigMap
),从而告诉 Kubernetes 要使用 Flannel
作为容器网络方案。
CNI
配置文件默认路径:/etc/cni/net.d
1 2 3 4 $ ls -l /etc/cni/net.d/ total 8 -rw-r--r-- 1 root root 662 May 25 16:08 10-calico.conflist -rw------- 1 root root 3063 May 25 16:08 calico-kubeconfig
当 kubelet
组件需要创建 Pod
的时候,先调用 dockershim
它先创建一个 infra container
容器。然后调用 CNI
插件为 infra container
容器配置网络。
这两个路径可在 kubelet
启动参数中定义:
1 2 3 4 5 $ vim kubelet.conf --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin
二、K8S 网络组件:Flannel
flannel Github
Flannel
是 CoreOS 维护的一个网络组件,Flannel
为每个 Pod
提供全局唯一的 IP,Flannel
使用 ETCD
来存储 Pod
子网与 Node IP 之间的关系。 Flanneld
守护进程在每台主机上运行,并负责维护 ETCD
信息和路由数据包。
Flannel 支持多种工作模式:
UDP
:最早支持的一种方式,由于性能最差,目前已经弃用。
VXLAN
:Overlay Network 方案,源数据包封装在另一种网络包里面进行路由转发和通信
Host-GW
:Flannel 通过在各个节点上的 Agent 进程,将容器网络的路由信息写到主机的路由表上,这样一来所有 的主机都有整个容器网络的路由数据了。
Directrouting
:同时支持 VXLAN 和 Host-GW 工作模式
公有云 VPC
:ALIYUN,AWS
1.Flannel VXLAN 模式 VXLAN 介绍 VXLAN,即 Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种网络虚似化技术。VXLAN 可以完全在内核态实现上述封装和解封装的工作,从而通过与前面相似的“隧道”机制,构建出覆盖网络(Overlay Network)。
VXLAN的覆盖网络设计思想:在现有的三层网络之上,覆盖一层二层网络,使得连接在这个VXLAN二层网络上的主机之间,可以像在同一局域网里通信。
为了能够在二层网络上打通“隧道”,VXLAN 会在宿主机上设置一个特殊的网络设备作为“隧道” 的两端。这个设备就叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端点)。
VTEP设备进行封装和解封装的对象是二层数据帧,这个工作是在Linux内核中完成的。 Flannel工作逻辑图如下:
VXLAN 工作流程 如果Pod 1访问Pod 2,源地址10.244.1.10,目的地址10.244.2.10 ,数据包传输流程如下:
从container到cni0(容器路由):容器根据路由表,将数据包发送下一跳10.244.0.1,从eth0网卡出。可以使用ip route命令 查看路由表
从cni0到flannel.1(主机路由):数据包进入到宿主机虚拟网卡cni0,根据路由表转发到flannel.1虚拟网卡。
flannel.1封装内部数据帧:flannel.1收到IP包后,由于flannel.1工作在二层网络,二层网络又需要目的MAC地址,因此要想封 装完整的二层数据帧,这个目标容器宿主机上flannel.1的MAC地址从哪获取到呢?其实在flanneld进程启动后,就会自动添加其他 节点ARP记录,可以通过ip neigh show dev flannel.1命令查看。
flannel.1二次封装:知道了目的MAC地址,Linux内核就可以进行二层封包了。但是,对于宿主机网络来说这个二层帧并不能在 宿主机二层网络里传输。所以接下来,还要把这个数据帧进一步封装成为宿主机网络的一个普通数据帧,好让它载着内部数据帧, 通过宿主机的eth0网卡进行传输。
flannel.1发起UDP连接:flannel.1准备向目标容器宿主机flannel.1发送UDP连接,但这时还不知道目标宿主机是谁,也就是说 这个UDP包该发给哪台宿主机呢? flanneld进程也维护着一个叫做FDB的转发数据库,可以通过bridge fdb show dev flannel.1命令查看,里面记录了目标容器宿主 机flannel.1的MAC对应的宿主机IP,也就是UDP要发往的目的地。接下来就是一个正常的宿主机于宿主机之间的传输了。
数据包到达目的宿主机:数据包从Node1的eth0网卡发出去,Node2接收到数据包,解封装发现是VXLAN数据包,把它交给 flannel.1设备。flannel.1设备则会进一步拆包,取出原始IP包(源容器IP和目标容器IP),通过cni0网桥二层转发给容器。
vxlan模式部署前有两处可能需要调整
Network:指定Pod IP分配的网段
与 kube-controller-manager.conf
配置的保持一样。
Backend:指定工作模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ vim kube-flannel.yml .... net-conf.json: | { "Network" : "10.244.0.0/16" , "Backend" : { "Type" : "vxlan" } } $ vim kube-controller-manager.conf .... --allocate-node-cidrs=true --cluster-cidr=10.244.0.0/16
K8S 集群更换网络组件准备 K8S 集群只能存在一个网络组件,更换网络组件将中断 K8S 集群中的所有业务 ,先卸载 calico
:
1 kubectl delete -f calico.yaml
清理 calico
遗留的数据:
关闭 calico
虚拟网卡:tunl0@NONE
删除路由:10.244.0.0/16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ ip tunnel del tunl0 delete tunnel "tunl0" failed: Operation not permitted $ ip link set tunl0 down $ ip route show default via 10.19.0.1 dev eth0 10.19.0.0/16 dev eth0 proto kernel scope link src 10.19.188.6 10.244.159.130 dev cali8bb3c199d9d scope link 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 linkdown $ ip route delete 10.244.159.130 dev cali8bb3c199d9d scope link
更换网络组件为 flannel-v0.17.0 通过查看官方文档 Deploying flannel manually 可知,部署前需要在指定路径先准备好 /opt/bin/flanneld
1 2 3 4 5 6 7 8 9 10 cd /usr/local /srcwget https://github.com/flannel-io/flannel/releases/download/v0.17.0/flannel-v0.17.0-linux-amd64.tar.gz mkdir -p /opt/bin/ tar xvf flannel-v0.17.0-linux-amd64.tar.gz -C /opt/bin/ cd /opt/kubernetes/cfgwget https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml kubectl apply -f kube-flannel.yml
重建原有 Pod,flannel 部署完原来的Pod都要删除一下,让他们基于现有的网络组件重新获取 Pod IP
2.HOST-GW 模式 host-gw
模式相比 vxlan
简单了许多,直接添加路由,将目的主机当做网关,直接路由原始封包。
1 2 3 4 5 6 7 net-conf.json: | { "Network" : "10.244.0.0/16" , "Backend" : { "Type" : "host-gw" }
当你设置 flannel
使用 host-gw
模式,flanneld
会在宿主机上创建节点的路由表:
1 2 3 4 5 6 default via 192.168.31.1 dev ens33 proto static metric 100 10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 10.244.1.0/24 via 192.168.31.63 dev ens33 10.244.2.0/24 via 192.168.31.61 dev ens33 192.168.31.0/24 dev ens33 proto kernel scope link src 192.168.31.62 metric 100
目的 IP 地址属于 10.244.1.0/24 网段的 IP 包,应该经过本机的 eth0 设备发出去(即:dev eth0);并且,它下一跳地址是192.168.31.63(即:via 192.168.31.63)。
一旦配置了下一跳地址,那么接下来,当 IP 包从网络层进入链路层封装成帧的时候,eth0 设备就会使用下一跳地址对应的 MAC 地址,作为该数据帧的目的 MAC 地址。
而 Node 2 的内核网络栈从二层数据帧里拿到 IP 包后,会“看到”这个 IP 包的目的 IP 地址是 10.244.1.20,即 container-2 的IP 地址。这时候,根据 Node 2 上的路由表,该目的地址会匹配到第二条路由规则(也就是 10.244.1.0 对应的路由规则),从而进入 cni0 网桥,进而进入到 container-2 当中。
可见,数据包是封装成帧发送出去的,会使用路由表的下一跳来设置目的MAC地址,会经过二层网络到达目的节点,因此,host-gw模式必须要求集群宿主机之间二层连通。
Flannel 两种模式小结 VXLAN 特点:
先进行二层帧封装,再通过宿主机网络封装,解封装也一样,所以增加性能开销
对宿主机网络要求低,只要三层网络可达
Host-GW 特点:
直接路由转发,性能损失很小
对宿主机网络要求二层可达
三、K8S 主流网络方案之 Calico
Calico GitHub Calico Document
1.Calico 介绍 Calico
是一个纯三层的数据中心网络方案,Calico
支持广泛的平台,包括 Kubernetes
、OpenStack
等。
Calico
在每一个计算节点利用 Linux Kernel
实现了一个高效的虚拟路由器(vRouter
)来负责数据转发,而每个 vRouter
通过 BGP
协议负责把自己上运行的 workload
的路由信息向整个 Calico
网络内传播。
此外,Calico
项目还实现了 Kubernetes
网络策略,提供 ACL
功能。
实际上,Calico
项目提供的网络解决方案,与 Flannel
的 host-gw
模式几乎一样。也就是说, Calico
也是基于路由表实现容器数据包转发,但不同于 Flannel
使用 flanneld
进程来维护路由信息的做法,而 Calico
项目使用 BGP
协议来自动维护整个集群的路由信息。
BGP
英文全称是 Border Gateway Protocol
,即边界网关协议,它是一种自治系统间的动态路由发现协议,与其他 BGP
系统交换网络可达信息。
BGP 介绍:
在互联网中,一个自治系统(AS)是一个有权自主地决定在本系统中应采用何种路由协议的小型单位。这个网络单位可以是一个简单的网络也可以是一个由一个或多个普通的网络管理员来控制的网络群体,它是一个单独的可管理的网络单元(例如一所大学,一个企业或者一个公司个体)。一个自治系统有时也被称为是一个路由选择域(routing domain)。一个自治系统将会分配一个全局的唯一的16位号码,有时我们把这个号码叫做自治系统号(ASN)。
在正常情况下,自治系统之间不会有任何来往。如果两个自治系统里的主机,要通过 IP 地址直接进行通信,我们就必须使用路由器把这两个自治系统连接起来。BGP协议就是让他们互联的一种方式。
Calico 主要由三个部分组成:
Felix
:以 DaemonSet
方式部署,运行在每一个 Node 节点上,主要负责维护宿主机上路由规则以及 ACL 规则。
BGP Client
(BIRD):主要负责把 Felix
写入 Kernel 的路由信息分发到集群 Calico
网络。
Etcd
:分布式键值存储,保存 Calico
的策略和网络配置状态。
calicoctl
:命令行管理 Calico
。
2.清理 Flannel 残留数据(可选) 再所有节点删除 flannel
残留数据
1 2 3 4 5 6 7 8 9 kubectl delete -f kube-flannel.yml ip link delete cni0 ip link delete flannel.1 ip route delete 10.244.159.130 .....
3.部署 Calico-v3.20.0 Calico 存储有两种方式:
数据直接存储进 etcd
。(性能更强,适合 node 节点数量大于 50 台) 1 https://docs.projectcalico.org/v3.9/manifests/calico-etcd.yaml
数据存储在 Kubernetes API Datastore 服务中,即间接的通过 K8S API 将数据存进 etcd
中。 1 2 https://docs.projectcalico.org/manifests/calico.yaml
使用存储方式1:calico-etcd.yaml
数据存储在 etcd
中还需要修改 yaml:
配置连接 etcd
地址,如果使用 https
,还需要配置证书。( ConfigMap
和 Secret
位置)
根据实际网络规划修改 Pod CIDR
(CALICO_IPV4POOL_CIDR)
使用存储方式2:calico.yaml 1 2 $ cd /opt/kubernetes/cfg $ wget https://docs.projectcalico.org/manifests/calico.yaml
通过查看镜像版本,可以看到,我这里使用的版本是 Calico-v3.20.0
1 2 3 4 5 6 $ grep "image:" calico.yaml image: docker.io/calico/cni:v3.20.0 image: docker.io/calico/cni:v3.20.0 image: docker.io/calico/pod2daemon-flexvol:v3.20.0 image: docker.io/calico/node:v3.20.0 image: docker.io/calico/kube-controllers:v3.20.0
下载完后还需要修改里面定义 Pod
网段 CALICO_IPV4POOL_CIDR
,要与前面 kube-controller-manager.conf
配置文件指定的 cluster-cidr
网段一样。
1 2 3 4 5 6 7 $ vim calico.yaml 3685 - name: CALICO_IPV4POOL_CIDR 3686 value: "10.244.0.0/16" $ kubectl apply -f calico.yaml
4.管理工具:calicoctl-v3.20.0
2021.12.16 起 calicoctl GitHub 整合到 calico GitHub
calicoctl
工具用于管理 calico
,可通过命令行读取、创建、更新和删除 Calico
的存储对象。
calicoctl
在使用过程中,需要从配置文件中读取 Calico
对象存储地址等信息。默认配置文件路 径:/etc/calico/calicoctl.cfg
二进制安装 calicoctl 在 master1 节点上安装使用即可。
1 2 3 4 5 6 7 8 9 10 11 cd /usr/local /srcwget https://github.com/projectcalico/calicoctl/releases/download/v3.20.0/calicoctl-linux-amd64 mv calicoctl-linux-amd64 /usr/bin/calicoctl chmod +x /usr/bin/calicoctl $ calicoctl version Client Version: v3.20.0 Git commit: 38b00edd Cluster Version: v3.20.0 Cluster Type: k8s,bgp,kdd
方式1:直连 etcd 该方式对应 存储方式1:calico-etcd.yaml
1 2 3 4 5 6 7 8 9 10 11 $ mkdir -p /etc/calico $ vim /etc/calico/calicoctl.cfg apiVersion: projectcalico.org/v3 kind: CalicoAPIConfig metadata: spec: atastoreType: "etcdv3" etcdEndpoints: "https://192.168.31.61:2379,https://192.168.31.62:2379,https://192.168.31.63:2379" etcdKeyFile: "/opt/etcd/ssl/server-key.pem" etcdCertFile: "/opt/etcd/ssl/server.pem" etcdCACertFile: "/opt/etcd/ssl/ca.pem"
方式2:基于 kubeconfig,间接通过 K8S API 来连接 etcd 该方式对应 存储方式2:calico.yaml
1 2 3 4 5 6 7 8 $ mkdir -p /etc/calico $ vim /etc/calico/calicoctl.cfg apiVersion: projectcalico.org/v3 kind: CalicoAPIConfig metadata: spec: datastoreType: "kubernetes" kubeconfig: "/root/.kube/config"
calicoctl 管理命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ calicoctl node status Calico process is running. IPv4 BGP status +---------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+-------------------+-------+----------+-------------+ | 10.19.142.207 | node-to-node mesh | up | 09:27:34 | Established | | 10.19.154.40 | node-to-node mesh | up | 09:27:34 | Established | +---------------+-------------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. $ calicoctl get nodes NAME k8s-master1 k8s-node1 k8s-node2 $ calicoctl get ippool -o wide NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR default-ipv4-ippool 10.244.0.0/16 true Always Never false all()
5.Calico 工作模式
IPIP
:Overlay Network 方案,源数据包封装在宿主机网络包里进行转发和通信。(默认)
BGP
:基于路由转发,每个节点通过 BGP
协议同步路由表,写到宿主机。 (CALICO_IPV4POOL_IPIP
的值设置 Never
)
CrossSubnet
:同时支持 BGP
和 IPIP
,即根据子网选择转发方式。
工作模式1:IPIP IPIP模式:采用 Linux IPIP 隧道技术实现的数据包封装与转发。
IP 隧道(IP tunneling)是将一个IP报文封装在另一个IP报文的技术,Linux系统内核实现的
IP隧道技术主要有三种:IPIP、GRE、SIT。
1 2 3 4 5 $ vim calico.yaml - name: CALICO_IPV4POOL_IPIP value: "Always"
Pod 1 访问 Pod 2 大致流程如下:
数据包(原始数据包)从容器出去到达宿主机,宿主机根据路由表发送到tunl0设备(IP隧道设备)
Linux内核IPIP驱动将原始数据包封装在宿主机网络的IP包中(新的IP包目的地之是原IP包的下一跳地址,即192.168.31.63)。
数据包根据宿主机网络到达Node2;
Node2收到数据包后,使用IPIP驱动进行解包,从中拿到原始数据包;
然后根据路由规则,根据路由规则将数据包转发给cali设备,从而到达容器2。
工作模式2:BGP 从 IPIP 切换到 BGP 工作模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ calicoctl get ippool -o yaml > ipip.yaml $ cp ipip.yaml bgp.yaml $ vim bgp.yaml apiVersion: projectcalico.org/v3 items: - apiVersion: projectcalico.org/v3 kind: IPPool metadata: creationTimestamp: "2022-05-27T09:27:30Z" name: default-ipv4-ippool resourceVersion: "253506" uid: 883aaa73-632d-4bae-b018-2c8aaf3ca186 spec: blockSize: 26 cidr: 10.244.0.0/16 ipipMode: Never natOutgoing: true nodeSelector: all() vxlanMode: Never kind: IPPoolList metadata: resourceVersion: "261536" $ calicoctl apply -f bgp.yaml
查看 tunl0 网卡变化,tunl0 网卡的IP 不存在了。 BGP 模式下不需要使用 tunl0 网桥。
1 2 3 4 $ ip ad 7: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1432 qdisc noqueue state UNKNOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0
查看节点路由表的变化:路由不再经过 tunl0 隧道转发,在 BGP 模式下基于路由表转发。
1 2 3 4 5 6 7 8 9 $ ip route show default via 10.19.0.1 dev eth0 10.19.0.0/16 dev eth0 proto kernel scope link src 10.19.188.6 10.244.36.64/26 via 10.19.142.207 dev eth0 proto bird blackhole 10.244.159.128/26 proto bird 10.244.159.129 dev calie6e099bc81b scope link 10.244.169.128/26 via 10.19.154.40 dev eth0 proto bird 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 linkdown
Pod 1 访问 Pod 2 大致流程如下:
数据包从容器出去到达宿主机;(直接通过宿主机路由表)
宿主机根据路由规则,将数据包转发给下一跳(网关);
到达Node2,根据路由规则将数据包转发给cali设备,从而到达容器2。
每一个 Pod 在 K8S 节点上都会有一条完整的路由,每一个 K8S 节点都会维护一份完整的路由表,因此在 BGP 工作模式下,宿主机的路由表会非常庞大。
相比较 Flannel GW 模式每个节点维护的是某一个 Pod 网段的路由,Calico BGP 工作模式的要复杂的多。
6.Route Reflector 模式(RR 路由反射 BGP工作模式优化) Node-to-Node Mesh 全互联模式(默认) Calico
维护的网络在默认是 Node-to-Node Mesh
全互联模式,Calico
集群中的节点之间都会相互建立连接,用于路由交换。全互联的目的是节点相互之间交换路由表。
但是随着集群规模的扩大,Mesh 模式将形成一个巨大服务网格,连接数成倍增加,就会产生性能问题。这时就需要使用 Route Reflector
(路由器反射)模式解决这个问题。
查看 Node-to-Node Mesh
全互联模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ calicoctl node status Calico process is running. IPv4 BGP status +---------------+-------------------+-------+------------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+-------------------+-------+------------+-------------+ | 10.19.142.207 | node-to-node mesh | up | 2022-05-27 | Established | | 10.19.154.40 | node-to-node mesh | up | 2022-05-27 | Established | +---------------+-------------------+-------+------------+-------------+ IPv6 BGP status No IPv6 peers found. $ netstat -ant|grep -i ESTABLISHED|grep 179 tcp 0 0 10.19.188.6:6443 10.19.142.207:11790 ESTABLISHED tcp 0 0 10.19.188.6:51790 10.19.154.40:2379 ESTABLISHED tcp 0 0 10.19.188.6:16937 10.19.142.207:179 ESTABLISHED tcp 0 0 10.19.188.6:22775 10.19.154.40:179 ESTABLISHED tcp 0 0 10.19.188.6:51796 10.19.154.40:2379 ESTABLISHED
Route Reflector 模式(RR 路由反射)
确定一个或多个 Calico
节点充当路由反射器,集中分发路由,让其他节点从这个 RR 节点获取路由信息。
切换模式为 RR 模式 先查看 bgpconfig 和 ASN
1 2 3 4 5 6 7 8 9 10 $ calicoctl get bgpconfig NAME LOGSEVERITY MESHENABLED ASNUMBER $ calicoctl get nodes -o wide NAME ASN IPV4 IPV6 k8s-master1 (64512) 10.19.188.6/16 k8s-node1 (64512) 10.19.142.207/16 k8s-node2 (64512) 10.19.154.40/16
关闭 BGP Node-to-Node Mesh
全互联模式,就等于启用了 RR
反射模式。
这个模式切换的过程会断开节点网络,期间终止业务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ vim bgp-rr.yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info nodeToNodeMeshEnabled: false asNumber: 64512 $ calicoctl apply -f bgp-rr.yaml Successfully applied 1 'BGPConfiguration' resource(s) $ calicoctl get bgpconfig NAME LOGSEVERITY MESHENABLED ASNUMBER default Info false 64512
挑选指定节点作为 RR 路由反射器,打上标签,推荐使用两个以上节点。
1 2 3 4 5 $ kubectl label node k8s-node1 route-reflector=true node/k8s-node1 labeled $ kubectl label node k8s-node2 route-reflector=true node/k8s-node2 labeled
为 RR 路由器反射器节点配置上 routeReflectorClusterID
,这是多台 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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 $ calicoctl get node k8s-node1 -o yaml > rr-node1.yaml $ vim rr-node1.yaml apiVersion: projectcalico.org/v3 kind: Node metadata: annotations: projectcalico.org/kube-labels: '{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"k8s-node1","kubernetes.io/os":"linux","route-reflector":"true"}' creationTimestamp: "2022-05-25T08:07:43Z" labels: beta.kubernetes.io/arch: amd64 beta.kubernetes.io/os: linux kubernetes.io/arch: amd64 kubernetes.io/hostname: k8s-node1 kubernetes.io/os: linux route-reflector: "true" name: k8s-node1 uid: 02d6decc-d77c-4cdc-abaf-afe44909d380 spec: addresses: - address: 10.19.142.207/16 type : CalicoNodeIP - address: 10.19.142.207 type : InternalIP bgp: ipv4Address: 10.19.142.207/16 routeReflectorClusterID: 244.0.0.1 orchRefs: - nodeName: k8s-node1 orchestrator: k8s status: podCIDRs: - 10.244.1.0/24 $ calicoctl get node k8s-node2 -o yaml > rr-node2.yaml $ vim rr-node2.yaml apiVersion: projectcalico.org/v3 kind: Node metadata: annotations: projectcalico.org/kube-labels: '{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"k8s-node2","kubernetes.io/os":"linux"}' creationTimestamp: "2022-05-25T08:07:41Z" labels: beta.kubernetes.io/arch: amd64 beta.kubernetes.io/os: linux kubernetes.io/arch: amd64 kubernetes.io/hostname: k8s-node2 kubernetes.io/os: linux name: k8s-node2 uid: ec221faa-498e-4edc-b384-411b38eb1c4f spec: addresses: - address: 10.19.154.40/16 type : CalicoNodeIP - address: 10.19.154.40 type : InternalIP bgp: ipv4Address: 10.19.154.40/16 routeReflectorClusterID: 244.0.0.2 orchRefs: - nodeName: k8s-node2 orchestrator: k8s status: podCIDRs: - 10.244.0.0/24 $ calicoctl apply -f rr-node1.yaml $ calicoctl apply -f rr-node2.yaml
部署 bgppeer 路由放射器,节点存在标签的将作为 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 $ vim bgppeer.yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: peer-with-route-reflectors spec: nodeSelector: all() peerSelector: route-reflector == 'true' $ calicoctl apply -f bgppeer.yaml Successfully applied 1 'BGPPeer' resource(s) $ calicoctl node status Calico process is running. IPv4 BGP status +---------------+---------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+---------------+-------+----------+-------------+ | 10.19.142.207 | node specific | up | 06:05:33 | Established | | 10.19.154.40 | node specific | up | 06:07:48 | Established | +---------------+---------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found.
到这里中断的网络会恢复正常。
网络组件选型
网络组件
覆盖网络
主机路由
网络策略
场景
Flannel
VXLAN
HostGW
N
Calico
IPIP
GBP
Y
网络组件选型考虑那些因素:
追求网络性能?calico
需要设置网络策略?calico
云主机不支持BGP协议?flannel
集群规模50台左右?flannel
如果没有专门网络工程工程师?flannel
目前主流 Calico
四、办公网络与 K8S 网络互通方案 网络需求:
办公网络与Pod网络不通。在微服务架构下,开发人员希望在办公电脑能直接连接K8s中注册中心调试;
办公网络与Service网络不通。在测试环境运行的mysql、redis等需要通过nodeport暴露,维护成本大;
现有虚拟机业务访问K8s上的业务。解决方案:打通办公网络与K8s网络
方案1:专门准备一台 Node 负责转发来自办公网络
办公环境下 kubernetes 网络互通方案 k8s-办公内网与k8s内网互通 打通Kubernetes内网与局域网的N种方法
挑选 dev-k8s-node1 作为路由转发节点
1 2 3 4 5 6 7 $ echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p iptables -t nat -I POSTROUTING -s 192.168.100.0/24 -d 10.244.0.0/16 -j MASQUERADE iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -d 10.0.0.0/24 -j MASQUERADE
同网段其他要访问 dev-k8s 集群的,将路由下一跳指向 dev-k8s-node1
1 2 3 # 192.168.100.158 route add -net 10.244.0.0/16 gw 192.168.100.192 dev eth0 route add -net 10.0.0.0/24 gw 192.168.100.192 dev eth0
但是我经过如上配置,只能访问到 dev-k8s-node1 节点上的 Pod,无法访问到其他节点的 Pod。经过 tcpdump 抓包发现经过 SNAT 的数据包并没有发送到 dev-k8s-node2 主机上,数据包在 dev-k8s-node1 就被丢弃了?
该问题暂未得到解决,不知道是不是因为我使用的是 Calico + BGP + 2node rr 反射而不行。是不是得用 flannel 才能实现?
1 iptables 办公网络 SNAT -> flannel.1
退而求其次,我只能手动将各个节点对应的子网加到 192.168.100.158 的路由表当作。该方法虽然愚笨姑且解决了问题,打通了外网 Prometheus 到内网 K8S 集群的监控。
1 2 3 4 route add -net 10.244.232.0 netmask 255.255.255.192 gw 192.168.100.190 dev eth0 route add -net 10.244.46.192 netmask 255.255.255.192 gw 192.168.100.191 dev eth0 route add -net 10.244.23.192 netmask 255.255.255.192 gw 192.168.100.192 dev eth0 route add -net 10.244.23.128 netmask 255.255.255.192 gw 192.168.100.193 dev eth0
方案2:两方上层路由器使用 BGP 做路由交换