POD的通信:
1.同一Pod中容器间通信,共享同一网络命名空间,使用locahost地址+容器端口进行通信
2.同一Node中Pod间通信,让veth对的一端链接到root网络命名空间(宿主机的),另一端链接到Pod的网络命名空间,使用同一docker0网桥,使用docker0分配的ip+端口进行通信
3.不同Node中 Pod间通信,使用第三方插件如Flannel,Calico进行通信。
docker0/cni是虚拟网桥,作用于同一物理子网中,查询通过转发表(mac表)mac地址进行通信,是二层网络设备,如二层交换机;flannel0是三层网络设备,跨物理子网,通过查询路由表找到其他Node,如路由器
Kuber-proxy与Service
CIDR(Classless Inter-Domain Routing,无类域间路由选择)消除了传统的A类、B类和C类地址以及划分子网的概念,因而可以更加有效地分配IPv4的地址空间。它可以将好几个IP网络结合在一起,使用一种无类别的域际路由选择算法,使它们合并成一条路由从而较少路由表中的路由条目减轻Internet路由器的负担。k8s集群中,每个node节点都会被分配一个CIDR块,(把网络前缀都相同的连续地址组成的地址组称为CIDR地址块)用来给node上的pod分配IP地址。
Kuber-proxy是网络代理和负载均衡,负责Service的实现,管理Sevice的Endpoints,默认使用iptables的方式实现。
Service为一组Pods提供了负载均衡,实现了动态网络,提供了一个固定的clusterIP,通过这个clusterIP找到后端的Endpoint而无视后端Endpoint的变化。
Endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个Service对应的所有pod的IP地址+端口。Service配置selector,Endpoint Controller才会自动创建对应的Endpoint对象;否则,不会生成Endpoint对象。
Kube-proxy监视APIserver的更改,因为集群中所有Service(iptables)更改都会发送到APIserver上,所以每台Kube-proxy监视APIserver,当对Service或Pod的ClusterIP进行修改时,Kube-proxy就会在本地更新,以便正确发送给Endpoint即后端pod.
在Pod通信过程中经过iptables时,会将源ip修改为Service的clusterIP,目标ip不变:
1.数据包从pod1所在eth0离开,通过veth对的另一端veth0传给网桥cbr0,网桥找不到service的ip对应的mac,交给了默认路由,到达了root命名空间的eth0
2.root命名空间的eth0接受数据包之前会经过iptables进行过滤,iptables接受数据包后使用kube-proxy在node上配置的规则响应service,然后数据包的目的ip重写为service后端指定的pod的ip了
3.收到包的pod会回应数据包到源pod,源ip是发送方ip,目标IP是接收方,数据包进行回复时经过iptables,iptables使用内核机制conntrack记住它之前做的选择,又将数据包源ip重新为service的ip,目标ip不变,然后原路返回至pod1的eth0
CNI(Container NetworkInterface)意为容器网络接口,它是一种标准的设计,为了让用户在容器创建或销毁时都能够更容易地配置容器网络。这是K8s中提供的一种通用网络标准规范,因为k8s本身不提供网络解决方案。目前比较知名的网络解决方案有:flannel、calico、canel、kube-router
Overlay 和 Underlay
在分布式pod中,K8S上的所有pod默认会从同一平面网络得到全局一个唯一IP地址和一个虚拟网络接口,无论是否都处于一个namespace,各个pod之间都可使用各自的IP地址直接进行通信。为了满足分布式pod必须位于同一个平面网络内,Netplugin目前常用的实现方案有:Overlay Network ,Underlay Network两类。
容器虚拟化网络方案有两种:基于隧道和基于路由:
基于隧道网络方案(overlay)典型为flannel vxlan和calico ipip模式:对物理网络没有特别要求,只要宿主机IP层互通就可以(3层网络),需要封包解包,CPU开销大。flannel路由表下还有cni转发表是两层转发,所以路由表比较小。
基于路由网络方案(underlay)典型为flannel host-gw和calico bgp模式:需要在同一个物理子网中(2层网络),不需要封包解包,但是对于每一个服务器的每一个容器中都要添加一个解析,路由表庞大。不需要再转一层CNI转发表。一般使用的直接路由flannel host-gw和calico bgp模式是纯三层网络方案。
Underlay
Underlay网络就是传统IT基础设施网络,由交换机和路由器等设备组成,借助以太网协议、路由协议和VLAN协议等驱动,将网络设备链接成物理网络拓扑,负责网络之间的数据包传输。它还是Overlay网络的底层网络,为Overlay网络提供数据通信服务。容器网络中的Underlay网络是指借助驱动程序将宿主机的底层网络接口直接暴露给容器使用的一种网络构建技术,较为常见的解决方案有MAC VLAN、IP VLAN和直接路由等。underlay可以是二层也可以是三层,二层的典型例子是以太网Ethernet,三层典型的例子是互联网Internet。二层技术是vlan,三层技术是ospf,bgp等协议组成。
在k8s中,underlay中比较典型的例子是通过将宿主机作为路由器设备,Pod的网络则通过学习路由条目从而实现跨节点通讯。典型为flannel host-gw和calico bgp模式.。
直接路由“直接路由”模型放弃了跨主机容器在L2的连通性,而专注于通过路由协议提供容器在L3的通信方案。这种解决方案因为更易于集成到现在的数据中心的基础设施之上,便捷地连接容器和主机,并在报文过滤和隔离方面有着更好的扩展能力及更精细的控制模型,因而成为容器化网络较为流行的解决方案之一。一个常用的直接路由解决方案,每个主机上的各容器在二层通过网桥连通,网关指向当前主机上的网桥接口地址。跨主机的容器间通信,需要依据主机上的路由表指示完成报文路由,因此每个主机的物理接口地址都有可能成为另一个主机路由报文中的“下一跳”,这就要求各主机的物理接口必须位于同一个L2网络中。于是,在较大规模的主机集群中,问题的关键便转向如何更好地为每个主机维护路由表信息。常见的解决方案有:
①Flannel host-gw使用存储总线etcd和工作在每个节点上的flanneld进程动态维护路由;
②Calico使用BGP(Border Gateway Protocol)协议在主机集群中自动分发和学习路由信息。与Flannel不同的是,Calico并不会为容器在主机上使用网桥,而是仅为每个容器生成一对veth设备,留在主机上的那一端会在主机上生成目标地址,作为当前容器的路由条目,
flannel host-gw模式中每个Node需要在同一个二层网络中,并将各个Node作为一个路由器,跨节点通讯通过路由表方式进行,这样方式下将网络模拟成一个underlay network。因为是通过路由方式,集群的CIDR至少要配置16,这样可以保证跨节点 的Node作为一层网络,同节点的Pod作为一个网络。如果不是这种情况,路由表处于相同的网络中,会存在网络不可达。
calico bgp BGP(Border Gateway Protocol)是去中心化自治路由协议。它是通过维护IP路由表或前缀表来实现AS(Autonomous System)自治系统之间的可访问性,属于向量路由协议。flannel使用flanned进程来维护路由信息,而Calico提供了BGP网络解决方案,它包含了多个守护进程,其中Bird进程是一个BGP客户端与路由反射器,BGP客户端负责从Felix中获取路由并分发给其他的BGP Peer,而反射器在BGP中起到了优化的作用。在同一个IBGP中,BGP客户端仅需要和一个RR相连,这样减少了AS内部维护的大量的bgp链接。通常情况下,RR是真实的路由设备,而Bird作为bgp客户端工作。
对于一个K8S集群来说,容器数量是很多的,为了避免各个容器间网络的冲突,一个常见的解决方案是给每个宿主机分配同一个网络中的不同子网,再让宿主机根据自有子网向其内容器分配ip。而主机间的网络通信只能经由主机上可以对外通信的网口进行,跨主机在数据链路层直接连接虚拟网桥难以实现,必须借助主机间通信隧道进行数据帧的转发。这种于某个通信网络上构建出另一个逻辑通信网络通常被称为overlay网络。隧道转发的本质是将容器双方的报文分别封装成各自宿主机的报文,通过宿主机之间的隧道完成数据交换,这种虚拟网络的基本要求就是宿主机只需要支持隧道转发协议即可。对于底层网络没有特殊要求。VXLAN协议是目前最流行的Overlay网络隧道协议之一,它也是由IETF定义的NVO3(Network Virtualization over Layer 3)标准技术之一,采用L2 over L4(MAC-in-UDP)的报文封装模式,将二层报文用三层协议进行封装,可实现二层网络在三层范围内进行扩展,将“二层域”突破规模限制形成“大二层域”。
同一大二层域就类似于传统网络中VLAN(虚拟局域网)的概念,只不过在VXLAN网络中,它被称作Bridge-Domain,以下简称为BD。类似于不同的VLAN需要通过VLAN ID进行区分,各BD要通过VNI加以标识。但是,为了确保VXLAN机制通信过程的正确性,涉及VXLAN通信的IP报文一律不能分片,这就要求物理网络的链路层实现中必须提供足够大的MTU值(最大传输单元,通信协议某一层上面能传输的最大字节数),或修改其MTU值以保证VXLAN报文的顺利传输。不过,降低默认MTU值,以及额外的头部开销,必然会影响到报文传输性能。VXLAN的显著的优势之一是对底层网络没有侵入性,管理员只需要在原有网络之上添加一些额外设备即可构建出虚拟的逻辑网络来。这个额外添加的设备称为VTEP(VXLAN Tunnel Endpoints),它工作于VXLAN网络的边缘,负责相关协议报文的封包和解包等操作,从作用来说相当于VXLAN隧道的出入口设备。VTEP代表着一类支持VXLAN协议的交换机,而支持VXLAN协议的操作系统也可将一台主机模拟为VTEP,Linux内核自3.7版本开始通过vxlan内核模块原生支持此协议。于是,各主机上由虚拟网桥构建的LAN便可借助vxlan内核模块模拟的VTEP设备与其他主机上的VTEP设备进行对接,形成隧道网络。同一个二层域内的各VTEP之间都需要建立VXLAN隧道,因此跨主机的容器间直接进行二层通信的VXLAN隧道是各VTEP之间的点对点隧道。VTEP是VXLAN隧道的守门员,可以理解为交换机。在同一个BD的两个VTEP(同一个BD内可以有多个VTEP)通过建立隧道实现相互通信。对于flannel来说,这个VTEP设备就是各节点生成的flannel.1网络接口,其中1就是BD的标识VNI,只要flannel网络接口的VNI一样,就属于同一个BD
对于Flannel来说,这个VTEP设备就是各节点上生成flannel.1网络接口,其中的“1”是VXLAN中的BD标识VNI,因而同一Kubernetes集群上所有节点的VTEP设备属于VNI为1的同一个BD。类似VLAN的工作机制,相同VXLAN VNI在不同VTEP之间的通信要借助二层网关来完成,而不同VXLAN之间,或者VXLAN同非VXLAN之间的通信则需经由三层网关实现。VXLAN支持使用集中式和分布式两种形式的网关:前者支持流量的集中管理,配置和维护较为简单,但转发效率不高,且容易出现瓶颈和网关可用性问题;后者以各节点为二层或三层网关,消除了瓶颈。然而,VXLAN网络中的容器在首次通信之前,源VTEP又如何得知目标服务器在哪一个VTEP,并选择正确的路径传输通信报文呢?常见的解决思路一般有两种:多播和控制中心。多播是指同一个BD内的各VTEP加入同一个多播域中,通过多播报文查询目标容器所在的目标VTEP。而控制中心则在某个共享的存储服务上保存所有容器子网及相关VTEP的映射信息,各主机上运行着相关的守护进程,并通过与控制中心的通信获取相关的映射信息。Flannel默认的VXLAN后端采用的是后一种方式,它把网络配置信息存储在etcd系统上。Linux内核自3.7版本开始支持vxlan模块,此前的内核版本可以使用UDP、IPIP或GRE隧道技术。事实上,考虑到当今公有云底层网络的功能限制,Overlay网络反倒是一种最为可行的容器网络解决方案,仅那些更注重网络性能的场景才会选择Underlay网络。
Underlay 网络性能优于 Overlay 网络。
Overlay 网络利用隧道技术,将数据包封装到 UDP 中进行传输。因为涉及数据包的封装和解封,存在额外的 CPU和网络开销。虽然几乎所有 Overlay 网络方案底层都采用 Linux kernel 的 vxlan 模块,这样可以尽量减少开销,但这个开销与 Underlay 网络相比还是存在的。所以 Macvlan、Flannel host-gw、Calico 的性能会优于 Docker overlay、Flannel vxlan 和 Weave。
Overlay 较 Underlay 可以支持更多的二层网段,能更好地利用已有网络,以及有避免物理交换机 MAC 表耗尽等优势,所以在方案选型的时候需要综合考虑。
VXLAN(Virtual eXtensible Local Area Network)是一种隧道技术,能在三层网络的基础上建立二层以太网网络隧道,从而实现跨地域的二层互连。主要原理是引入一个UDP格式的外层隧道作为数据链路层,而原有数据报文内容作为隧道净荷加以传输。由于外层采用了UDP作为传输手段,净荷数据可以轻松地在二三层网络中传送。其在三层网络之上,构建出了一个虚拟的大二层网络,VXLAN通过采用MAC in UDP封装来延伸二层网络,将以太报文封装在IP报文之上,通过路由在网络中传输,只要虚拟机路由可达,就可以将其规划到同一个大二层网络中,无需关注虚拟机的MAC地址。
网络插件
Flannel设计目的就是为集群中所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。Flannel会创建一个新的虚拟网卡flannel0接收docker网桥的数据,通过维护路由表,对接收到的数据进行封包和转发(vxlan)。Fannel规定宿主机下各个Pod属于同一个子网,不同宿主机下的Pod属于不同的子网,Flannel会在每一个宿主机上运行名为flanneld代理,其负责为宿主机预先分配一个子网,并为Pod分配IP地址。Flannel使用Kubernetes或etcd来存储网络配置、分配的子网和主机公共IP等信息。数据包则通过VXLAN、UDP或host-gw这些类型的后端机制进行转发。
flannel原理:
不同主机间pod通信,数据从容器发出后,在宿主机的cni0虚拟网卡的转发表中找不到目的地址,转发到flannel0虚拟网卡,将数据封装在udp中根据flannel的路由表转发到目的节点的flannel服务,数据到达目的节点后被解包然后进入目的节点的flannel0虚拟网卡,再被转发到目的主机的cnio虚拟网卡。最后由cni0到达目的容器。
Calico是一个基于BGP的纯三层网络方案,其会为每个容器(pod)分配一个可路由的IP,在通信时不需要解包和拆包,因此网络性能损耗小,易于排查和水平扩展。Calico网络功能强大,可以与istio集成。Calico IPIP模式与Vxlan类似,也是通过网络隧道技术实现的,与Vxlan的差别就是,VXLAN本质上本质上是一个UDP包,而IPIP则将包封装在本身的报文包上。它其实是利用了Linux 的tun/tap设备,对IP层的报文再加了一层IP层的封装实现的一种overlay模式。因为IPIP模式比BGP模式多了一层封包与拆包,所以性能会有所损耗。既然如此,为什么不直接使用BGP模式就行了呢?因为BGP模式是需要通过路由广播交换容器网络的路由信息,而路由广播只能在局域网中进行,在BGP模式下,如果kubernetes集群中的工作节点不在同一个子网,则跨子网的工作节点上的POD无法正常通信。
calico架构图如下
Calico网络模型主要工作组件:
Felix:Calico Agent,运行在每一台 Host 的 agent 进程,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等,保证跨主机容器的网络互通。
etcd:Calico的后端存储,主要负责网络元数据一致性,确保Calico网络状态的准确性,可以与kubernetes共用;
BGP Client(BIRD):Calico 为每一台 Host 部署一个 BGP Client,使用 BIRD 实现,BIRD 是一个单独的持续发展的项目,实现了众多动态路由协议比如 BGP、OSPF、RIP 等。负责将Felix 在各Node上的设置通过BGP协议广播到Calico网络,从而实现网络互通。
BGP Route Reflector:在大型网络规模中,如果仅仅使用 BGP client 形成 mesh 全网互联的方案就会导致规模限制,因为所有节点之间俩俩互联,需要 N^2 个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数。
IPIP模式,是一种将各Node的路由之间做一个tunnel,再把网络连接起来的模式:
就是将一个IP数据包套在另一个IP包里,使用到了Linux提供的隧道技术。可以理解为一个基于IP层的网桥,将两个本不通的网络通过点对点连接起来。