Kubernetes 虚拟网络详解(CNI实现:Flannel、Calico)

Kubernetes 容器调度平台提供大规模、自动化的部署容器的实现方式,提供跨节点跨集群的虚拟网络的CNI解决方案、提供分布式存储方案、提供动态的资源规划的解决方案,是以往容器的一种高级部署实现的方案,在K8S-1.24版本之前底层是通过docker来实现,高版本的运行已经脱离docker的依赖转而通过CRI(容器运行时接口)来适配各种不同的容器引擎来实现底层容器的创建及维护工作。

Docker网络

        在讨论Kubernetes网络之前,让我们先来看一下Docker网络。Docker采用插件化的网络模式,默认提供bridge、host、none、overlay、maclan和Network plugins这几种网络模式,运行容器时可以通过–network参数设置具体使用那一种模式。
    bridge:这是Docker默认的网络驱动,此模式会为每一个容器分配Network Namespace和设置IP等,并将容器连接到一个虚拟网桥上。如果未指定网络驱动,这默认使用此驱动。
    host:此网络驱动直接使用宿主机的网络。
    none:此驱动不构造网络环境。采用了none 网络驱动,那么就只能使用loopback网络设备,容器只能使用127.0.0.1的本机网络。
    overlay:此网络驱动可以使多个Docker daemons连接在一起,并能够使用swarm服务之间进行通讯。也可以使用overlay网络进行swarm服务和容器之间、容器之间进行通讯,
    macvlan:此网络允许为容器指定一个MAC地址,允许容器作为网络中的物理设备,这样Docker daemon就可以通过MAC地址进行访问的路由。对于希望直接连接网络网络的遗留应用,这种网络驱动有时可能是最好的选择。
    Network plugins:可以安装和使用第三方的网络插件。可以在Docker Store或第三方供应商处获取这些插件:flannel等。

        在默认情况,Docker使用bridge网络模式,我们先以bridge模式对Docker的网络进行说明。
        安装Docker时,创建一个名为docke0虚拟网桥,虚拟网桥使用“10.0.0.0 -10.255.255.255 “、”172.16.0.0-172.31.255.255″和“192.168.0.0-192.168.255.255”这三个私有网络的地址范围。

        运行容器时,在宿主机上创建虚拟网卡veth pair设备,veth pair设备是成对出现的,从而组成一个数据通道,数据从一个设备进入,就会从另一个设备出来。将veth pair设备的一端放在新创建的容器中,命名为eth0;另一端放在宿主机的docker0中,以veth为前缀的名字命名。通过 brctl show 命令查看放在docker0中的veth pair设备
        外部访问:bridge的docker0是虚拟出来的网桥,因此无法被外部的网络访问。因此需要在运行容器时通过-p和-P参数对将容器的端口映射到宿主机的端口。实际上Docker是采用 NAT的 方式,将容器内部的服务监听端口与宿主机的某一个端口port 进行绑定,使得宿主机外部可以将网络报文发送至容器。

1)通过-P参数,将容器的端口映射到宿主机的随机端口:

2)通过-p参数,将容器的端口映射到宿主机的制定端口:

docker run -p {hostPort}:{containerPort} {images}

可以修改docker的默认网段:

#第一步 删除原有配置
​    sudo service docker stop
​    sudo ip link set dev docker0 down
​    sudo brctl delbr docker0
​    sudo iptables -t nat -F POSTROUTING

#第二步 创建新的网桥
​    sudo brctl addbr docker0
​    sudo ip addr add 172.17.10.1/24 dev docker0
​    sudo ip link set dev docker0 up

#第三步 配置Docker的文件
#注意: 这里是 增加下面的配置
vi /etc/docker/daemon.json

##追加的即可
cat /etc/docker/daemon.json  
{"registry-mirrors": ["http://224ac393.m.daocloud.io"],
​            "bip": "172.17.10.1/24"
}
$ systemctl  restart  docker

Kubernetes网络划分

1.容器与容器之间的网络 :

        POD有一个唯一的IP地址,POD内的容器共享IP地址和网络命名空间
2.POD与POD之间的网络

        POD间的网络涉及到CNI网络插件的使用:flannel\calico...
3.POD与Service之间的网络

        kube-proxy与endpoint(详情去看k8s的组件介绍)
4.互联网与Service之间的网络
        Service网络层:提供一个虚拟IP地址并维护一组pod的网络访问路由规则:clusterIP\nodeport\loadbalaner

Kubernetes CNI 网络模型

CNI的全称是Container Network Interface,它为容器提供了一种基于插件结构的标准化网络解决方案。以往,容器的网络层是和具体的底层网络环境高度相关的,不同的网络服务提供商有不同的实现。CNI从网络服务里抽象出了一套标准接口,从而屏蔽了上层网络和底层网络提供商的网络实现之间的差异。并且,通过插件结构,它让容器在网络层的具体实现变得可插拔了,所以非常灵活。

CNI 提供了一种应用容器的插件化网络解决方案,定义对容器网络进行操作和配置的规范,通过插件的形式对 CNI 接口进行实现。
  CNI 仅关注在创建容器时分配网络资源与在销毁容器时删除网络资源。
  对容器网络的设置和操作都通过插件 (Plugin) 进行具体实现,CNI 插件包括两种类型: 
  1.CNI Plugin 
  2.IPAM (IP Address Management) Plugin
  CNI Plugin 负责为容器配置网络资源, IPAM Plugin 负责对容器的 IP 地址进行分配和管理。 IPAM Plugin 作为 CNI Plugin的一部分,与 CNI Plugin 一起工作。

  • CNI插件的控制流程

CNI 设计的基本思路是:容器运行时创建网络命令空间 (network namepsace) 后,然后由 CNI 插件负责网络配置,最后启动容器内的应用。
    - kubelet 在启动容器之前,先启用 Pause 容器。
    - Pause 容器启动之前创建网络 namespace。
    - 如果 Kubelet 配置了 CNI,会调用对应的 CNI 插件
    - CNI 插件执行网络配置操作,如创建虚拟网卡、加入网络空间等。
    - CNI 调用 ipam 分配地址。
    - 启动 Pod 内其他容器,并共享 Pause 容器内网络空间。

  • CNI网络插件对比

CNI网络插件优点缺点是否支持网络策略
Flannel部署简单,性能优秀网络层延迟高
Calico性能最好,支持容器内BGP协议,支持网络策略配置复杂
Weave Net功能强大,跨平台支持性能低下, 容易出现网络死锁
Canal结合了Flannel和Calico两种插件的优点,支持多种网络模式,可以满足不同的需求部署和配置较为繁琐

CNI 插件网络方案

CNI插件三种网络实现模式:

  • overlay (基于隧道)模式是基于隧道技术实现的,整个容器网络和主机网络独立,容器之间跨主机通信时将整个容器网络封装到底层网络中,然后到达目标机器后再解封装传递到目标容器。不依赖与底层网络的实现。实现的插件有flannel(UDP、vxlan)、calico(IPIP)等等

  • 三层路由模式(基于路由)中容器和主机也属于不通的网段,他们容器互通主要是基于路由表打通,无需在主机之间建立隧道封包。但是限制条件必须依赖大二层同个局域网内。实现的插件有flannel(host-gw)、calico(BGP)等等。这种方案优势是传输率较高,不需要封包、解包, 但 BGP 等协议在很多数据中心内部支持,设置较为麻烦。

  • underlay网络是底层网络,负责互联互通。 容器网络和主机网络依然分属不同的网段,但是彼此处于同一层网络,处于相同的地位。整个网络三层互通,没有大二层的限制,但是需要强依赖底层网络的实现支持.实现的插件有calico(BGP)等等

  • Network Policy网络策略

Network Policy 的主要功能是对 Pod 或者 Namespace 间的网络通信进行限制和准入控制, 设置方式为将目标对象的 label 为查询条件,设置允许访问或禁止访问的客户端 Pod 表。 目前查询条件可以作用于 Pod 和 Namespace 级别。
  为了使用 Network Policy,Kubernetes 引入了一个新的资源对象 NetworkPolicy,供用户设置 Pod 之间的网络访问策略。 但这个资源对象配置仅仅是策略规则,还需要一个策略控制器(Policy Controller)进行策略规则的具体实现。策略控制器由第三方网络组件提供,目前   Calico、Cilium、kube-router、Romana、Weave Net 等开源项目均支持网络策略的实现。

Flannel

Kubernetes系统上Pod网络的实现依赖于第三方插件,而Flannel是由CoreOS主推的目前比较主流的容器网络解决方案,CNI插件有两种功能:网络配置和网络策略,由于flannel比较简单,并不支持网络策略,flannel项目自身只是一个框架,真正提供网络功能的是它的后端实现,目前,Flannel支持三种不同后端实现,分别是:

  • UDP:使用设备flannel.0进行封包解包,不是内核原生支持,上下文切换较大,性能非常差

  • VXLAN:使用flannel.1进行封包解包,内核原生支持,性能较强

  • host-gw:无需flannel.1这样的中间设备,直接宿主机当作子网的下一跳地址,性能最强

    host-gw的性能损失大约在10%左右,而其他所有基于VXLAN“隧道”机制 的网络方案,性能损失在20%~30%左右

UDP是Flannel项目最早支持的一种方式,是性能最差的方式,目前已被废弃。用的最多的是VXLANhost-gw模式的部署。

udp模式

udp 是 Flannel 最早支持的模式、在这个模式中主要有两个主件:flanneld 、flannel0 。
    flanneld 进程负责监听 etcd 上面的网络变化,以及用来收发包,
    flannel0 则是一个三层的 tun 虚拟网络设备,用作在操作系统内核和用户应用程序之间传递 ip 包。
    1.cni0数据包根据 ip 路由会先交给 flannel0 设备,
    2.然后 flannel0 就会把这个 ip 包,交给 flanneld 进程,flanneld 进程是一个 udp 进程,负责处理 flannel0 发送过来的数据包。
    3.flanneld 进程会监听 etcd 的网络信息,然后根据目的 ip 的地址匹配到对应的子网,从 etcd 中找到这个子网对应的宿主机 node 的 ip 地址,然后将这个数据包直接封装在 udp 包里面,然后发送给 node 2。
    4.由于每台宿主机上的 flanneld 都监听着一个 8285 端口,所以 node 2 机器上 flanneld 进程会从 8285 端口获取到传过来的数据,解析出封装在里面的发给源 ip 地址。
    5.flanneld 会直接把这个 ip 包发送给它所管理的 tun 设备,即 flannel0 设备。然后网络栈会将这个数据包根据路由发送到 cni0 网桥,
    6.cni0 网桥会扮演二层交换机的角色,将数据包发送给正确的端口,进而通过 veth pair 设备进入到容器里。

上面所讲的 Flannel udp 模式现在已经废弃,原因就是因为它经过三次用户态与内核态之间的数据拷贝。容器发送数据包经过 cni0 网桥进入内核态一次;数据包由 flannel0 设备进入到 flanneld 进程又一次;第三次是 flanneld 进行 udp 封包之后重新进入内核态,将 UDP 包通过宿主机的 eth0 发出去。

VXLan模式

VXLAN,即(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。
        VXLAN是内核的一个模块,在内核态实现封装解封装,从而通过“隧道”机制,构建出覆盖网络(Overlay Network),其实就是一个由各宿主机上的Flannel.1设备组成的虚拟二层网络。
VXLAN的设计思想是:
        在现有的三层网络之上,“覆盖”一层虚拟的、由内核VXLAN模块负责维护的二层网络,使得连接在这个VXLAN二层网络上的“主机”(虚拟机或容器都可以),可以像在同一个局域网(LAN)里那样自由通信。
        VXLAN会在宿主机上设置一个特殊的网络设备作为“隧道”的两端,叫VTEP:(虚拟隧道端点)

        VTEP:(虚拟隧道端点)
        flanel.1设备,就是VXLAN的VTEP,即有IP地址,也有MAC地址
    
(内部数据帧)VTEP设备封装:目的容器IP地址+目的VTEP设备的MAC地址
(外部数据帧)linux封装:目的主机MAC地址 + 目的主机IP地址 + VXLAN Header + 内部数据帧

VXLAN Header里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。
在Flannel中,VNI的默认值是1,这也是为什么宿主机的VTEP设备都叫flannel.1的原因


        IP地址是从FDB的转发数据库获取,是由flanneld进程维护
        MAC地址是从ARP表获取,是由flanneld进程维护
    
        VXLAN 在通信的时候,VTEP 在进行通信前会通过查询转发表 FDB 来确定目标 VTEP 地址,转发表 FDB 用于保存远端虚拟机/容器的 MAC 地址,远端 VTEP IP,以及 VNI 的映射关系,这个映射关系在 k8s 上 Flannel 会通过 flanneld 进程来自动更新 FDB(查询转发表) 表项。

host-gw模式

·        host-gw模式通信十分简单,它是通过 ip 路由直连的方式进行通信,flanneld 负责为各节点设置路由 ,将对应节点Pod子网的下一跳地址指向对应的节点的 ip :

        通过把主机当作网关实现跨节点网络通信的。因此通信双方的宿主机要求能够直接路由,必须在同一个网络,这个限制使得host-gw模式无法适用于集群规模较大且需要对节点进行网段划分的场景。        

        host-gw的另外一个限制则是随着集群中节点规模的增大,flanneld维护主机上成千上万条路由表的动态更新也是一个不小的压力。

    flanneld的唯一作用就是负责主机上路由表的动态,但因为只能修改主机的路由,所以各个主机必须二层网络互通。

host-gw 小结:
    1.纯三层的网络方案;
    2.无需创建虚拟设备,只需要添加相应路由规则即可;
    3.免除了额外的虚拟设备的封包和解封包过程;
    4.需要集群宿主机之间二层连通;
    5.相较UDP和VXLAN,性能最好;
    
            host-gw虽然VXLAN网络性能要强很多,但是种方式有个缺陷:**要求各物理节点必须在同一个二层网络中**。物理节点必须在同一网段中。这样会使得一个网段中的主机量会非常多,万一发一个广播报文就会产生干扰。在私有云场景下,宿主机不在同一网段是很常见的状态,所以就不能使用host-gw了。

Flannel总结

对比三种网络,udp 主要是利用 tun 设备来模拟一个虚拟网络进行通信;vxlan 模式主要是利用 vxlan 实现一个三层的覆盖网络,利用 flannel1 这个 vtep 设备来进行封拆包,然后进行路由转发实现通信;而 host-gw 网络则更为直接,直接改变二层网络的路由信息,实现数据包的转发,从而省去中间层,通信效率更高。

Flannel 会在每一个宿主机上运行名为 flanneld 代理,其负责为宿主机预先分配一个唯一的 IP 地址段,并将每个容器的 IP 地址映射到这个 IP 地址段中。
    Flannel 需要依赖 etcd 或者其他分布式键值存储系统来存储网络配置信息、
    数据包则通过 VXLAN、UDP 或 host-gw 这些类型的后端机制进行转发。
    flannel只实现简单的网络通信,不支持网络ACL。(网络策略)


Flannel主要包括flannel0/1 的网桥、flanneld 的服务进程、etcd
Flannel 首先创建了一个名为 flannel0/1  的网桥,而且这个网桥的一端连接 docker0 网桥,另一端连接一个叫作 flanneld 的服务进程。

  flanneld 进程并不简单,它上连 etcd,利用 etcd 来管理可分配的 IP 地址段资源 ,同时监控 etcd 中每个 Pod 的实际地址,并在内存中建立了一个 Pod 节点路由表;它下连 docker0 和物理网络,使用内存中 Pod 节点路由表,将 docker0 发给它的数据包包装起来,利用物理网络的连接将数据包投递到目标 flanneld 上,从而完成 Pod 到 Pod 之间的直接地址通信。
  
  Flannel 之间底层通信协议的可选技术包括 UDP、VxLan、AWS VPC 等多种方式 。通过源 flanneld 封包、目标 flanneld 解包, docker0 最终收到的就是原始数据,对容器应用来说是透明的,应用感觉不到中间 Flannel 的存在。
  Flannel 完美地实现了对 Kubernetes 网络的支持,但是它引入了多个网络组件,在网络通信时需要转到 flannel0 网络接口,再转到用户态的 flanneld 程序,到对端后还需要走这个过程的反过程,所以也会引入一些网络的时延损耗。
  另外, Flannel 模型默认采用了 UDP 作为底层传输协议, UDP 本身是非可靠协议,虽然两端的 TCP 实现了可靠传输,但在大流量高并发的应用场泉下还需要反复测试,确保没有问题。
  注意:Flannel 使用的 etcd 是另外安装的,不是直接使用 Kubernetes 中的 etcd。

Calico

Calico简介

Calico是属于纯3层的网络模型,每个容器都通过IP直接通信,中间通过路由转发找到对方。容器所在的节点类似于传统的路由器,提供了路由查找的功能。每个容器所在的主机节点扮演了虚拟路由器 (vRouter)的功能,vRouter必须有某种方法,能够知道整个集群的路由信息。

之前提到的FlannelHost Gateway模式 (opens new window)方案是不能跨二层网络,是因为它只能修改主机路由,Calico把改路由表的做法换成了标准的BGP路由协议。相当于在每个节点上模拟出一个额外的路由器,由于采用的是标准协议,Calico模拟路由器的路由表信息可以被传播到网络的其他路由设备中,这样就实现了在三层网络上的高速跨节点网络。

BGP(Border Gateway Protocol)是一种用于在互联网中交换路由信息的协议。它是一种自治系统(AS)之间的路由协议,用于在不同的自治系统之间交换路由信息。BGP协议的主要作用是将路由信息从一个自治系统传递到另一个自治系统,以便实现互联网的全球路由。

但现实中的网络并不一定支持BGP路由,在这种情况下可以使用[IPIP隧道模式来传输数据。

FelixW

Felix是一个守护程序,作为agent运行在托管容器或虚拟机的Calico节点上。Felix负责刷新主机路由和ACL规则等,以便为该主机上的Endpoint正常运行提供所需的网络连接和管理。进出容器、虚拟机和物理主机的所有流量都会遍历Calico,利用Linux内核原生的路由和iptables生成的规则。

BGP Client

Calico在每个运行Felix服务的节点上都部署一个BGP Client(BGP客户端)。BGP客户端的作用是读取Felix编写到内核中的路由信息,由BGP客户端对这些路由信息进行分发。当Felix将路由插入Linux内核时,BGP客户端将接收它们,并将它们分发到集群中的其他工作节点。

Node-to-Node Mesh

该模式为默认模式,在BGP下,集群中的每一个节点的BGP Client都需要和其他所有节点的BGP Client进行通信来交换路由。 但随着节点数量增加,连接数会以N^2规模增加,给集群网络带来巨大的压力。

BGP Route Reflector

Calico会指定几个节点负责专门跟其他所有节点进行连接并交换路由信息,从而学习到全局的路由信息。而其他节点也只需要跟这几个节点进行通信来获取到整个集群的规则信息。

calico两种网络方式

①IPIP

从字面意思理解,就是把一个IP包套在另一个IP包中,也就是将IP层封装到IP层的一个tunnel中。

它的作用基本上相当于一个基于IP层的网桥。普通的网桥是MAC层,用不到IP,而IPIP方式则是用两端路由做一个tunnel,将两个本来不通的网络通过点对点连接起来。

IPIP技术的源代码可以在内核net/ipv4/ipip.c中找到。

其中有个设备tunl0,它是一个IP隧道(IP tunnel)设备。

在上边这个例子中,IP包进入IP tunnel之后,就会被Linux内核的IPIP驱动接管。IPIP驱动会将这个IP包直接封装在一个宿主机网络的IP包

②BGP

BGP(Border Gateway Protocol,边界网关协议)是互联网上一个核心的去中心化自治路由协议

它通过维护IP路由表前缀表来实现自治系统(AS)之间的可达性,属于矢量路由协议

BGP不用传统内部网关协议(IGP)的指标,而是用基于路径、网络策略、规则集决定路由。因此它更适合被称为矢量性协议,而非路由协议

BGP会将接入机房的多条线路(电信、移动、联通等)融合为一体,实现多线单IP——服务器只需要一个IP地址,最佳访问路由的确认,是由网络上骨干router根据跳数等技术指标确定的,不占用服务器的任何系统

BGP网络相比IPIP,最大的区别在于没有隧道设备tunl0。前边介绍过IPIP网络中Pod之间的流量发给tunl0,之后tunl0发给对端设备。而BGP网络中,Pod之间的流量直接从网卡发送到目的地,减少了tunl0这个环节。

 外部流量访问集群

从集群外访问集群有多种方式,比如loadbalancer,Ingress,nodeport,nodeportloadbalancerservice的两个基本类型,是将service直接对外暴露的方式,ingress则是提供了七层负载均衡,其基本原理将外部流量转发到内部的service,再转发到后端endpoints,在平时的使用中,我们可以依据具体的业务需求选用不同的方式。这里主要介绍nodeport和ingress方式。

Nodeport

通过将 Service 的类型设置为 NodePort,就可以在 Cluster 中的主机上通过一个指定端口暴露服务。注意通过 Cluster 中每台主机上的该指定端口都可以访问到该服务,发送到该主机端口的请求会被 Kubernetes 路由到提供服务的 Pod 上。采用这种服务类型,可以在 Kubernetes cluster 网络外通过主机 IP:端口的方式访问到服务。

Ingress

Ingress 是推荐在生产环境使用的方式,它起到了七层负载均衡器和 Http 方向代理的作用,可以根据不同的 url 把入口流量分发到不同的后端Service。外部客户端只看到 http://foo.bar.com 这个服务器,屏蔽了内部多个 Service 的实现方式。采用这种方式,简化了客户端的访问,并增加了后端实现和部署的灵活性,可以在不影响客户端的情况下对后端的服务部署进行调整。

服务对外暴露方式

  1. 利用POD hostNetwork配置选项对外暴露(单个服务)

    • 将pod的端口在物理机上开通,不支持负载均衡有端口冲突风险

  2. 利用Service的NodePort选项对外暴露(较少服务)

    • 访问任何节点都以路由到Pod,默认支持负载均衡策略(随机)

    • 每一个Service都要绑定NodePort,大量对外暴露的服务将出现大量的端口占用

    • Service维护一个虚拟的IP地址绑定一组POD,设置NodePort端口是在每个工作着的主机开辟端口实现服务的对外转发,POD的发现由Service管理

  3. 利用LoadBalancer负载均衡器(大规模共有云服务)

    • 使用Service绑定云服务商负载均衡器IP入口

    • 在Service中type设置为LoadBalancer并设置LoadBalancerIP,所有请求转发工作都有负载均衡器完成

  4. 使用Ingress-Controller构建应用入口(私有云服务)

    • 是k8s中内置的组件,按照既定好的路由规则进行转发,提供一个IP或域名暴露一个端口,在内部根据自定义的路由规则(附加的前缀)实现路由转发

    • Ingress是在Service之上进行的流量管理,通过脚本的方式去配置路由的规则,根据请求host\路径等信息将请求转发到不同的Service之中

    • 在本地使用域名要配置相应的hosts或者DNS解析

常见的术语介绍:

  1. 第2层网络:OSI(Open Systems Interconnections,开放系统互连)网络模型的“数据链路”层。第2层网络会处理网络上两个相邻节点之间的帧传递。第2层网络的一个值得注意的示例是以太网,其中MAC表示为子层。
  2. 第3层网络:OSI网络模型的“网络”层。第3层网络的主要关注点,是在第2层连接之上的主机之间路由数据包。IPv4、IPv6和ICMP是第3层网络协议的示例。
  3. VXLAN:代表“虚拟可扩展LAN”。首先,VXLAN用于通过在UDP数据报中封装第2层以太网帧来帮助实现大型云部署。VXLAN虚拟化与VLAN类似,但提供更大的灵活性和功能(VLAN仅限于4096个网络ID)。VXLAN是一种封装和覆盖协议,可在现有网络上运行。
  4. Overlay网络:Overlay网络是建立在现有网络之上的虚拟逻辑网络。Overlay网络通常用于在现有网络之上提供有用的抽象,并分离和保护不同的逻辑网络。
  5. 封装:封装是指在附加层中封装网络数据包以提供其他上下文和信息的过程。在overlay网络中,封装被用于从虚拟网络转换到底层地址空间,从而能路由到不同的位置(数据包可以被解封装,并继续到其目的地)。
  6. 网状网络:网状网络(Mesh network)是指每个节点连接到许多其他节点以协作路由、并实现更大连接的网络。网状网络允许通过多个路径进行路由,从而提供更可靠的网络。网状网格的缺点是每个附加节点都会增加大量开销。
  7. BGP:代表“边界网关协议”,用于管理边缘路由器之间数据包的路由方式。BGP通过考虑可用路径,路由规则和特定网络策略,帮助弄清楚如何将数据包从一个网络发送到另一个网络。BGP有时被用作CNI插件中的路由机制,而不是封装的覆盖网络。

Network namespace

网络命名空间(Network namespace)是 Linux Kernel 提供的用于实现网络虚拟化的核心,它能创建多个隔离的网络空间,该网络空间内的防火墙、网卡、路由表、邻居表、协议栈与外部独立,不管是虚拟机还是容器,当运行在独立的命名空间时,就像是一台单独的物理主机。

由于不同的命名空间之间相互隔离,所以同一个宿主机之内的命名空间并不能直接通信,如果想与外界(其他 Network namespace、或者宿主机)进行通信,就需要在命名空间里面,插入虚拟网卡(Veth),连接到虚拟交换机(Bridge),就像配置一个物理环境中的局域网,配置完连接信息之后,它们之间就可以正常通信了。

虚拟网卡(Veth)

虚拟网卡\虚拟以太网(Veth)

目前主流的虚拟网卡有 tun/tap 和 veth 两种,在时间上 tun/tap 出现的更早。tun/tap 不是一个设备,而是两个相对独立的虚拟网络设备,其中 tap 模拟了以太网设备,操作的是数据帧,工作在 L3,tun 则模拟了网络层设备,操作的是 IP 报文。

使用 tun/tap 设备的目的是实现把来自于协议栈的数据包先交由某个打开 /dev/net/tun 字符设备的用户进程处理后,再把数据包重新发回到链路中。 我们可以把 tun/tap 理解为一端连着网络协议栈,另一端连着用户态程序。tun/tap 设备可以将 TCP/IP 协议栈处理好的网络包发送给任何一个使用 tun/tap 驱动的进程,只要协议栈中的数据包能被用户态程序截获并加工处理,就能实现例如数据压缩、流量加密、透明代理等功能。

tun/tap 设备通常用作 overlay 网络传输,如图示例,应用程序通过 tun 发送数据包,tun 设备如果发现另一读被 VPN 程序打开,便会通过字符设备发送给 VPN,VPN 收到数据包,重新修改成新报文,然后作为报文体,再封装到另一个发送给 B 地址的新报文中,这种将一个数据包封装到另一个数据包的处理方式被称为 “隧道”,隧道技术是构建虚拟逻辑网络的经典做法。OpenVPN、Vtun、Flannel 等都是基于 tun/tap 实现隧道封装的,在后续章节讲到的 VXLAN 网卡也是一种 tun 设备。

使用 tun/tap 设备传输数据需要经过两次协议栈,会有多次的封包解包,一定的性能损耗,这也是大家所说 Flannel UDP 模式性能较低的原因。

veth 是另一种主流的虚拟网卡方案,在 Linux Kernel 2.6 版本支持网络命名空间的同时,也提供了专门的虚拟 Veth(Virtual Ethernet,虚拟以太网)设备,用来让两个隔离的网络命名空间可以互相通信。

严格来说,veth 一对设备,因而也常被称作 veth pair。简单理解 veth 就是一根带两个 Ethernet 网卡的`网线`,从一头发数据,另一头收数据,如果 veth-1 和 veth-2 是一对 veth 设备,veth-1 发送的数据会由 veth-2 收到,反之亦然。

因为 veth 这个特性,它常常充当着一个桥梁,连接着宿主机内的虚拟网络,典型的例子像两个隔离的网络命名空间之间的连接、Bridge(Linux 网桥) 和 OVS (Open vSwitch)之间的连接等,通过这种方式,从而构建出复杂的虚拟网络拓扑架构。

我们在 Kubernetes 集群中的宿主机总能看到一堆 veth 开头的网卡设备信息,这些就是为不同 Pod 之间通信而创建的虚拟网卡,在 Kubernetes 宿主机中查看网卡设备。

$ ip addr
7: veth9c0be5b3@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
   link/ether e2:7c:c8:36:d7:14 brd ff:ff:ff:ff:ff:ff link-netnsid 2
   inet6 fe80::e07c:c8ff:fe36:d714/64 scope link
      valid_lft forever preferred_lft forever

网桥

        网桥用于不同网络中主机之间的相互通信,二层的虚拟网络设备,将若干个网络接口连接起来使其能相互间转发报文。
网桥读取报文中的目标MAC地址并与自己记录的MAC地址表结合来决定报文转发的目标网络接口,当不知道怎么转发时,广播报文。
网桥的MAC地址表会设置超时时间,默认5min,若收到对应端口MAC地址回发的包,则重置超市时间,否则,MAC地址失效,进行广播处理。
交换机只是一个二层设备,接收到的报文要么转发,要么丢弃。而网桥接收的报文除了转发和丢弃外,还可能被送到网络协议栈的上层(网络层),从而被自己消化,所以网桥可以被看作二层设备或三层设备。
Linux内核通过网桥设备绑定多个以太网接口设备,从而将它们桥接起来。网桥设备有IP地址。
我们在部署 Docker 或者 Kubernetes 时,宿主机内的 cni0、docker0 就是它们创建的 Linux 网桥设备。

VXLAN(虚拟可扩展局域网)

VLAN(Virtual Local Area Network,虚拟局域网)
二层网络当设备非常多广播又非常频繁的时候,很容易形成广播风暴。 VLAN 通过划分广播域,在报头增加 VLAN tag,让所有广播只针对相同的 VLAN tag 的设备生效,这样就缩小了广播域,也提高了安全性和可管理性。
就是 VLAN tag 的设计容量仅限于4096个网络ID,二层范围一般较小且固定,也无法支持虚拟机大范围的动态迁移。

VXLAN(虚拟可扩展局域网),它是 Linux 内核本身就支持的一种网络虚似化技术。

 VXLAN 本质上是一种隧道封装技术,将 L2 的以太网帧(Ethernet frames)封装成 L4 的 UDP 数据报,然后在 L3 的网络中传输,效果就像 L2 的以太网帧在一个广播域中传输一样,不再受数据中心传输的限制。
封装同时加入了自己定义的 VXLAN Header。在 VXLAN Header 里直接就有 24 Bits 的 VLAN ID,可以存储 1677 万个不同的取值,VXLAN 让二层网络得以在三层范围内进行扩展,不再受数据中心间传输的限制。

其实就是将内层的数据报文再包了一层,然后通过一个叫 VTEP 的进程负责解包和封包。VXLAN header 里面还有一个 VNI 标志,它的作用主要是用来标记数据包是不是属于当前租户的,用来做网络隔离使用。

VXLAN 在通信的时候,VTEP 在进行通信前会通过查询转发表 FDB 来确定目标 VTEP 地址,转发表 FDB 用于保存远端虚拟机/容器的 MAC 地址,远端 VTEP IP,以及 VNI 的映射关系,这个映射关系在 k8s 上 Flannel 会通过 flanneld 进程来自动更新 FDB(查询转发表) 表项。

比如 node1 的容器要和 node2 的容器通信,那么会经过如下:

- 发送端:在 node1 中发起 `ping 172.20.1.2` ,`ICMP` 报文经过 `cni0` 网桥后交由 `flannel.1` 设备处理。 `flannel.1` 设备是 VXLAN 的 VTEP 设备,负责 VXLAN 封包解包。 因此,在发送端,`flannel.1` 将原始L2报文封装成 VXLAN UDP 报文,然后从 `eth0` 发送;
- 接收端:node2 收到 UDP 报文,发现是一个 VXLAN 类型报文,交由 `flannel.1` 进行解包。根据解包后得到的原始报文中的目的 ip,将原始报文经由 `cni0` 网桥发送给相应容器;

VXLAN 对网络基础设施的要求很低,不需要专门的硬件提供特别支持,只要三层可达的网络就可以部署 VXLAN。VXLAN 每个边缘入口都部署了一个 VTEP(VXLAN Tunnel Endpoints,VXLAN 隧道端点),VTEP 是 VXLAN 隧道的起点和终点,VXLAN 对用户原始数据帧的封装和解封装均在 VTEP 上进行,VTEP 既可以是一台独立的网络设备,也可以是在服务器中的虚拟交换机。源服务器发出的原始数据帧,在 VTEP 上被封装成 VXLAN 格式的报文,并在 IP 网络中传递到另外一个 VTEP 上,并经过解封转还原出原始的数据帧,最后转发给目的服务器。

由于VXLAN由于额外的封包解包,导致其性能较差,所以Flannel就有了host-gw模式,即把宿主机当作网关,除了本地路由之外没有额外开销,性能和calico差不多,由于没有叠加来实现报文转发,这样会导致路由表庞大。因为一个节点对应一个网络,也就对应一条路由条目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值