一、Flannel
1.1 简介
Flannel由CoreOS研发,使用”虚拟网桥和veth设备”的方式为Pod创建虚拟网络接口,通过可配置的后端(backend)定义Pod间的通信网络。它支持基于VXLAN和UDP的Overlay网络,以及基于三层路由的Underlay网络。
对于每一个容器而言,在加入网络时,在每个节点创建一个虚拟交换机,并为每一个Pod/容器创建一对虚拟网卡(veth pair),一端插入容器的网络名称空间,另一端插入节点(宿主机)的网桥。而Pod如何接入物理网络,则取决于后端的定义。
Flannel用于解决Kubernetes集群中各节点Pod之间的通信问题,它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的IP;让所有的容器认为它们都在同一个直连的网络,底层通过UDP/VxLAN/Host-GW等数据转发方式(后端)进行报文的封装和转发。
在IP地址分配方面,它将预留的一个专用网络(默认为10.244.0.0/16)划分为多个子网作为每个节点的IP地址范围(podCIDR),使得每个Pod拥有一个独立的IP地址。具体由IPAM插件host-local进行IP地址分配,并将子网分配信息保存于etcd。
Flannel在每个节点上运行一个名为flanneld的二进制代理程序,它负责从预留的网络中按照指定或者默认的掩码长度为当前节点申请分配一个子网,并将网络配置、已分配的子网和辅助数据(比如主机的公网IP等)存储在Kubernetes API或独立的etcd中。
1.2 Flannel支持的后端
Flannel使用称为后端(backend)的容器网络机制转发跨节点的Pod报文。Flannel目前支持的主流backend如下:
- vxlan
使用Linux内核中的vxlan模块封装隧道报文,以叠加(overlay)网络模型支持跨节点的Pod间的互联互通。同时支持直接路由模式,在此模式下,位于同一个二层网络内的节点之上的Pod间通信可通过路由模式直接发送,而跨二层网络的节点(两个节点不在同一网段,要经过路由器)之上的Pod间通信仍要使用VXLAN隧道协议转发(即兼具vxlan和host-gw两种后端的功能)。使用直接路由模式可以节省一部分隧道网络的开销。下图为VXLAN后端使用直接路由模式的示意图。
vxlan模型中,flanneld监听udp协议的8472端口用于接收和发送封装的数据包。
- host-gw
类似于VXLAN后端的直接路由模式,但不支持跨二层网络的节点,因此这种模式要求各节点处于同一个二层网络中,不太适用于规模较大的环境,但转发性能较好。下图为host-gw后端的示意图。
- udp
用常规UDP报文封装完成隧道转发,性能比vxlan和host-gw相比较差,仅在不支持vxlan和host-gw时使用;UDP后端模式中,flanneld监听UDP 8285端口发送报文。
1.3 VXLAN后端
Flannel的配置文件默认使用VXLAN后端,相关配置定义在kube-flannel名称空间下configmap/kube-flannel-cfg资源对象中。相关配置如下:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
相关的键说明如下:
- Network:Flannel全局使用的子网,即pod cidr的值
- SubnetLen:子网分割的长度,在全局子网掩码小于24时(eg:16),默认为24
- SubnetMin:分配给节点使用的起始子网,默认为切割完成后的第一个子网
- SubnetMax:分给给节点使用的最大子网,默认为切割完成后的最后一个子网
- Backend:Flannel使用的后端,以及后端的配置
这里可以通过抓包进行分析。这里让两个Pod进行通信,其中src-ip为10.244.1.35(nginx Pod1),dst-ip为10.244.2.31(nginx Pod2),二者对应的Node节点的IP分别为192.168.131.14(K8S-Node1)和192.168.131.15(K8S-Node2)。
用kubectl exec命令进入nginx Pod1,执行while true;do curl 10.244.2.31;sleep 1;done命令,之后在Node1节点的物理接口抓包。
tcpdump -i ens33 -nn -X udp port 8472
可以看到,报文在外部会封装两层首部。外层为192.168.131.15.47961 > 192.168.131.14.8472(物理IP,包含隧道封装),内层为10.244.2.31.80 > 10.244.1.35.44014(Pod IP)。事实上,任何报文只要进入了flannel.1接口即相当于进入了隧道接口,之后报文会发到对端的隧道接口。如果是本机以内的Pod之间的通信(eg:两个Pod均属于10.244.3.0子网段),则无需做任何复杂的转发,直接通过cni0接口(本地的虚拟交换机接口)发送。反之,如果是跨节点的Pod间通信,则需要用到flannel.1这个隧道接口来对外通信。
此外,flannel还会在运行的节点上生成一个环境变量文件,默认是/run/flannel/subnet.env,其包含本节点使用的子网、mtu等信息。
cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16 #全局网段
FLANNEL_SUBNET=10.244.3.1/24 #本节点的子网
FLANNEL_MTU=1450 #容器接口mtu值
FLANNEL_IPMASQ=true #地址映射
1.4 直接路由
如果是VXLAN添加直接路由模式,则修改kube-flannel名称空间下configmap/kube-flannel-cfg资源对象,为VXLAN后端添加Directrouting: true键值对就可以开启DirectRouting模式(见下图)。
以K8S-Node2为例,启用Directrouting模式后,节点上的路由变成如下图
可以看到,Pod子网的下一跳地址由对端flannel.1接口地址变成了宿主机物理接口的地址,本地用于发出报文的接口由flannel.1变成了本地的物理接口(eg:ens33)。
显然,这种模式无法满足跨二层网络节点上Pod的通信需求,因为到达Pod子网的下一跳地址无法指向另一个二层网络中的节点地址。所以节点上依然保留与VXLAN隧道相关的flannel.1接口用于跨二层网络节点上Pod的通信需求。
1.5 host-gw后端
如果想修改后端类型,则修改kube-flannel名称空间下configmap/kube-flannel-cfg资源对象,将Backend的Type字段修改为指定类型(eg:host-gw)。
当修改为host-gw后端后,使用tcpdump -i ens33 -nn -X tcp port 80命令再次抓包,就会发现报文内已经没有了隧道封装。说明此时直接路由功能已经在发挥作用了。
此时查看路由信息,发现Pod的子网对应的网关正好是Node节点物理网卡的IP地址,同时也不再需要flannel.1接口(因为没有了隧道封装的开销)。比如,位于10.244.1.0网段的某个Pod和其它网段的某个Pod通信通过192.168.131.14(即本机物理接口ens33的地址)直接把对端的物理网卡当作下一跳路由,完全通过路由的方式完成报文转发。
当节点处于不同的二层网络时,host-gw后端就不能实现Pod间的通信(由于不具备隧道转发的能力)。相对来说,VXLAN的DirectRouting模式兼具VXLAN后端和host-gw后端的优势,既能保证传输性能,又可以跨二层网络转发Pod报文。
此外,Flannel并不支持为Pod网络添加网络策略以控制Pod间通信的能力,它只能借助额外的支持网络策略的插件实现此功能,Calico网络插件就是为此目的而设立的。
二、Calico
2.1 简介
Calico是一个三层的虚拟网络解决方案,它将每个节点都当做虚拟路由器(vRouter),把每个节点上的Pod都当做是”节点路由器”后的一个终端设备并为其分配一个IP地址。各节点路由器通过BGP协议(Border Gateway Protocol,边界路由协议)学习生成路由规则从而实现不同节点上的Pod间的互联互通。
与Flannel相比,Calico的一个显著优势是对网络策略的支持,它允许用户定义访问控制规则以管控进出Pod的数据报文,从而为Pod间的通信施加安全策略。
实际上,Calico提供的网络解决方案与Flannel的host-gw模式几乎一样,也是基于路由表实现容器的数据包转发。但不同于Flannel使用flanneld进程来维护路由信息的做法,Calico使用BGP协议来维护集群的路由信息。
Calico在每一个计算节点通过Linux内核实现了一个高效的vRouter(虚拟路由器,简单的理解为将节点内核视为路由器使用)进行报文转发,而每个vRouter通过BGP协议负责把自身所属的节点上运行的Pod资源的IP地址信息基于节点的agent程序(Felix)直接由vRouter生成路由规则向整个Calico网络内传播。
BGP是互联网上一个核心的去中心化自治路由协议,它通过维护IP路由表或”前缀”表来实现自治系统(AS,Autonomous System)之间的可达性,通常作为大规模数据中心维护不同的自治系统之间路由信息的矢量路由协议。Linux内核原生支持BGP,因而可以将Linux主机配置成为边界网关。
Calico把Kubernetes集群环境中的每个节点上Pod组成的网络视为一个自治系统(AS),而每个节点就相当于自治系统的边界网关。各节点之间通过BGP协议交换路由信息并生成路由规则。但并非所有的网络环境都能支持BGP,而且BGP路由模型要求所有节点位于同一个二层网络中,所以Calico还支持基于IPIP或VXLAN的Overlay网络模型(IPIP和VXLAN二者为互斥关系,只能用其一)。
与Flannel在VXLAN后端使用直接路由(DirectRouting)的网络模型类似,Calico也支持混合使用路由和叠加(Overlay)网络模型,BGP路由模型用于二层网络通信,IPIP或VXLAN用于跨子网(Cross Subnet)的节点间报文转发。
2.2 Calico架构
Calico主要由Felix、Orchestrator Plugin(编排系统插件)、etcd、BIRD和BGP Router Reflector(BGP路由反射器)等组件组成。
- Felix:Calico的核心组件,运行在每个节点上。主要负责完成接口管理、路由规则、ACL规划和状态报告这几个核心任务,从而为各端点(VM或Container)生成连接机制。
- 接口管理:负责创建网络接口,将接口信息配置到内核,以确保内核能够处理各端点的流量,尤其是要确保能用节点自身的MAC来响应当前工作节点上每个工作负载MAC地址的ARP请求,以及为Felix管理的接口打开转发功能。另外,接口管理还要监控各接口的变动以确保规则能够得到正确应用。
- 路由规划:负责为当前工作节点上的各端点在内核FIB(Forwarding Information Base,转发信息库)中生成路由信息,以保证到达当前节点的报文可以正确转发给端点。
- ACL规划:负责在Linux内核中生成ACL规则,以实现仅放行端点间的合规流量,并确保流量不能绕过Calico的安全措施。
- 状态报告:Felix负责提供关于网络健康状况的相关数据,尤其是报告由Felix管理的节点上出现的错误和问题。这些报告和数据会存储在etcd中供其它组件或管理员使用。
- Orchestrator Plugin:编排系统插件的主要功能是将Calico整合进所在的编排系统(eg:Kubernetes、Openstack等云原生平台)中。可以通过各自的API来配置Calico网络实现无缝集成。Eg:Kubernetes的cni网络插件。
- etcd:用于持久存储Calico相关配置数据(eg:使用的子网、网络策略等)的存储管理系统。
- BIRD:Calico在每个运行着Felix的节点上同时运行一个名为BIRD的守护进程,它是BGP协议的客户端,负责将Felix生成的路由信息载入内核并通告到整个网络中,同时从其它节点学习到新的路由信息。学习到的路由信息由Felix生成路由表。
- BGP路由反射器:可选组件。Calico的BGP路由模型默认采用节点网格模式(node-to-node mesh),随着节点数量的增加,节点之间的连接数量会快速增长,给集群网络带来较大的压力。因此,一般建议大规模集群使用BGP路由反射器模式进行路由学习,BGP的点到点通信也就转换为与中心节点的单路通信模型。另外,基于冗余考虑,生产环境应该配置多个BGP路由反射器。对于Calico来说,BGP客户端程序除了作为客户端使用,也可以配置为路由反射器。
2.3 IPIP隧道网络
wget https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
相关配置说明如下:
- name: CALICO_IPV4POOL_IPIP
value: "Always"
设置在IPv4类型的地址池上是否启用IPIP这个隧道网络。支持如下可用值
- Always:总是使用。不论节点是否处在同一个二层网络中均使用IPIP隧道。
- Cross-Subnet:跨子网流量。当节点不在同一个二层网络时其流量才使用隧道封装,否则启用BGP路由网络。
- Never:从不使用IPIP隧道网络。
- name: CALICO_IPV4POOL_VXLAN
value: "Never"
是否启用VXLAN这个隧道网络。支持的可用值及其意义与IPIP类似。不过要注意的是,IPIP与VXLAN只能二者用其一。这里用了IPIP就不用VXLAN了。
这里先用IPIP模式(CALICO_IPV4POOL_IPIP:Always)进行抓包分析。先创建若干Pod做测试用。
kubectl create deploy demoapp --image ikubernetes/demoapp:v1.0 --replicas 3
工作在IPIP模式的Calico会在每个节点上创建一个tunl0接口作为隧道出入口来封装IPIP隧道报文。Calico会为每一个Pod资源创建一对veth设备,其中一端作为Pod的网络接口,另一端(名称以cali为前缀,后跟随机字串)留置在节点的网络名称空间,不使用虚拟网桥。
IPIP隧道网络仍需借助BGP维护节点间的可达性。部署完成后,Calico会通过BGP协议在每个节点上生成到达Kubernetes集群中其它各节点的Pod子网路由信息。以K8S-Node1为例,使用IPIP模式后,节点的路由显示如下图。以去往Node2节点的Pod(192.168.12.0网段)为例说明,要到达192.168.12.0网段需要通过本机的tunl0接口去送给192.168.131.15(这是K8S-Node2节点IP地址)。这些是由各节点上的BIRD以点对点的方式(node-to-node mesh)向网络中的其它节点进行通告并学习其它节点的通告而得。
对于每个Pod,Calico都会在节点上为其生成一个专用路由条目,用于确保以Pod IP为目标的报文可以通过节点上的calixxx接口送达,这是因为Calico没有像Flannel一样使用虚拟网桥进行报文转发导致的。
这里进入Node1节点所在的Pod,去与Node2节点所在的Pod进行通信,其中src-ip为192.168.113.2(demoapp Pod1),dst-ip为192.168.12.2(demoapp Pod2),二者对应的Node节点的IP分别为192.168.131.14和192.168.131.15。另外在Node1节点执行tcpdump -i ens33 -nn host 192.168.131.15命令进行抓包。
可以看到,这是IPIP报文,里面封装了两层IP,外层为192.168.131.14 > 192.168.131.15(节点IP),内层为192.168.113.2.36822 > 192.168.12.2.80(Pod IP)。
2.4 BGP
可以将IPIP的值设为Cross-Subnet(CALICO_IPV4POOL_IPIP:Cross-Subnet),当节点处在同一个子网时通过BGP进行路由学习生成路由信息。
等到calico清单文件apply成功后,再检查路由信息,发现节点将同一网络内其它节点相关的路由条目经由IPIP模式的tunl0接口传输变为节点的物理接口(eg:ens33)传输,这就与Flannel的host-gw后端的工作逻辑非常相似了。如下路由信息截取自K8S-Node1主机上。
与之前的测试方式类似,只不过这次抓包要针对Pod的IP来抓。下面命令在Node1节点执行。其中,192.168.12.5为demoapp Pod2的IP,在Node2节点上。
tcpdump -i ens33 -nn host 192.168.12.5
此时,就直接显示源Pod(192.168.113.4)到目标Pod(192.168.12.5)的通信,没有了外层节点IP的封装。
标签:插件,Kubernetes,网络,Calico,BGP,主流,Pod,节点,路由 From: https://blog.51cto.com/u_15796303/6942008