Kubernetes / Linux Note / 运维笔记

Kubernetes 网络插件详解 – Flannel篇

Einic Yeo · 2月18日 · 2021年 · ·

Kubernetes本身并没有自己实现容器网络,而是借助CNI标准,通过插件化的方式来集成各种网络插件,实现集群内部网络相互通信。Kubernetes集群设计了Cluster IP、Node IP、Pod IP三种类型的通信IP:Cluster IP是Service对象的IP地址,实现Pod服务的负载均衡,外部网络无法ping通,只在集群内部可以访问使用,是一个虚拟的IP;Node IP是集群节点Node的IP地址,可以是物理机的IP(也可能是虚拟机IP);Pod IP是容器组Pod的IP地址,是在容器组初始化过程由Pause容器申请的IP地址。其中Pod IP是容器网络接口CNI与具体的网络插件来实现IP的申请及数据包的转发。

Flannel是CoreOS开源的,Overlay模式的CNI网络插件,Flannel在每个集群节点上运行一个flanneld的代理守护服务,为每个集群节点(HOST)分配一个子网(SUBNET),同时为节点上的容器组(POD)分配IP,在整个集群节点间构建一个虚拟的网络,实现集群内部跨节点通信。Flannel的数据包在集群节点间转发是由backend实现的,目前,已经支持核心官方推荐的模式有UDP、VXLAN、HOST-GW,以及扩展试用实验的模式有IPIP,AWS VPC、GCE、Ali VPC、Tencent VPC等路由,其中VXLAN模式在实际的生产中使用最多。

核心模式

UDP模式

UDP模式简单来说,就是数据报文在发送实际物理网络之前,通过flanneld进行一层UDP封装,将数据报文作为payload发送给对端,对端收到UDP报文之后,flanneld通过解包得到真正的数据报文后,再转发至最终的服务端,如图一所示。UDP模式的核心点是虚拟网络设备tun/tap该设备一端连接协议栈,另外一端连接用户程序,允许用户程序像读写文件一样进行收发数据包。tun/tap工作原理基本一致,tun模拟是三层网络设备,收发的是IP层数据包;tap模拟是二层网络设备,收发以太网数据帧。

UDP模式,Flannel通过DaemonSet控制器,在每个节点启动一个kube-flannel的Pod,同时会创建两个虚拟网络设备,cni0与flannel0,如图二所示,其中cni0是linux bridge设备,而flannel0则是Tun设备。

在同一个Kubernetes集群中,Pod通信有同节点Pod通信及跨节点Pod通信两种场景,其中同节点Pod通信,由于cni0是类二层转发设备,可以直接实现数据包的转发,所以同节点的Pod间通信,直接通过Linux bridge cni0来实现。跨节点Pod通信则是三层虚拟网络设备Tun,也就是flannel0。当Pod数据包通过路由发送到flannel0上,节点主机上的flanneld进程就会收到IP包,并将IP包封装在一个UDP包,通过物理网络发送给目的宿主机。同理目的主机就会有UDP解包及转发至Pod服务。UDP模式比较简单,但是由于flanneld运行在用户态,而数据的交互和传递则在内核态完成,会经历两次的内核态和用户态数据拷贝,性能损耗巨大,所以UDP模式已经逐渐废弃。如图四所示,是一个完成UDP模式数据包转发过程及路由表。

VXLAN模式

VXLAN (Virtual Extensible LAN)是一种网络虚拟化技术,它使用一种隧道协议将二层以太网帧封装在四层UDP报文中,通过三层网络传输,组成一个虚拟大二层网络。VXLAN使用VTEP(VXLAN Tunnel Endpoint)来进行封包和解包:在发送端,源VTEP将原始报文封装成VXLAN报文,通过UDP发送到对端VTEP;在接收端,VTEP将解开VXLAN报文,将原始的二层数据帧转发给目的的接收方。VTEP可以是独立的网络设备,例如交换机,也可以是部署在服务器上的虚拟设备。例如使用置顶交换机(TOR)作为VTEP时,VXLAN的网络模型如下图:

Flannel默认和推荐的模式就是VXLAN,当使用默认配置安装Flannel时,会为每个节点分配一个24位子网,并在每个节点上创建两张虚机网卡cni0和flannel.1:其中cni0是一个网桥设备,节点上所有的Pod都通过veth pair的形式与cni0相连;而flannel.1则是一个VXLAN类型的设备,充当VTEP的角色实现对VXLAN报文的封包解包。   同节点Pod通信,与UDP模式一样,通过cni0网桥完成,不涉及VXLAN报文的封包解包操作;跨节点Pod通信则是通过静态配置路由表,ARP(address resolution protocol 地址解析协议)表和FDB表(Forwarding database二层设备中MAC地址和端口的关联关系)的信息,通过VXLAN虚拟网卡flannel.1,实现了集群内所有Pod在同一个大二层网络相互通信。还有VXLAN模式支持DirectRouting配置,DirectRouting=true是支持在相同子网情况下数据包直接通过路由转发,与HOST-GW模式相同。

UDP模式和VXLAN模式都是属于隧道方式,也就是在UDP的基础之上,构建虚拟网络,VXLAN模式只是将UDP模式的flanneld进程换成了VTEP设备,通过VTEP设备完成封包和解包的过程;另外一个关键点是VXLAN模式全过程全部在内核态完成,flannel.1充当网桥的角色,进行UDP数据包的转发,所以性能要比UDP模式好很多。

HOST-GW模式

UDP模式和VXLAN模式不同,HOST-GW模式属于路由的方式,无需经过任何封包和解包,纯路由机制实现,因此性能比较高。但是HOST-GW模式只支持宿主机之间二层连接,要求集群中所以节点必须处于同一个网络中。如果不在一个网络中,三层设备路由器缺少Pod 网络的路由规则,无法转发数据包。HOST-GW模式将目的主机当作网关,直接路由原始的数据包。flanneld会在集群节点上创建并维护路由表,当数据包到达集群节点的时,就会根据路由表直接发送到下一跳,也就是同网段的节点IP,直接通过二层网络将数据转发到目的节点上;目的节点再根据路由表规则转发到cni网桥,网桥根据路由规则转发到容器里面。HOST-GW模式通过路由转发实现高性能网络通信,但是这种模式局限性很大,节点之间都要相互有点对点的路由覆盖,每个节点上有n-1个路由,而n个节点共有n(n-1)/2个路由,才能保证flannel的flat网络能力。

扩展模式

IPIP模式

IPIP模式是一种简单隧道方式,通过内核IPIP封装数据包,开销低但只能封装ipv4单播通信,因此无法设置OSPF、RIP或任何其他基于多播的协议。在IPIP模式,会有两个IPIP隧道设备tunl0flannel.ipip,其中tunl0由ipip的内核模块,在modprobe ipip模块的网络命名空间(network namespace)自动创建,是命名空间默认IPIP设备,包含local=any和remote=any属性,flannel.ipip是由flannel创建的一对多ipip设备。当接收到IPIP协议数据包时,内核在local/remote属性中存在精确匹配src/dst的IP地址版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!,就会解包转到内部服务,否则会将数据包转发到tunl0备用设备。同样,还有IPIP模式也支持DirectRouting配置,DirectRouting=true是支持在相同子网情况下数据包直接通过路由转发,与HOST-GW模式相同。

Cloud VPC

Kubernetes容器很多情况下都是部署在公有云之上,容器集群的网络需要云其他产品互联互通,Flannel支持公有云Cloud VPC模式,在AWS VPC,AliVPC,TencentCloud VPC运行容器集群,就可以直接使用VPC的路由表,将容器内部IP实时同步到路由表中,这样容器内部IP与其他云产品相互通信,比如ELB就可以直接配置路由到容器内部,不过有的公有云要路由条数有限制,或者需要额外收费。

容器网络基础

Kubernetes是一个开源容器调度编排引擎,管理大规模容器化应用,采用典型的Master-Worker主从分布式技术架构,由集中式管理节点(Master Node),分布式的工作节点(Worker Node)组成。向下屏蔽底层差异化的分布式基础设施,以应用为中心构建云计算的基础操作系统能力(即云原生操作系统),面向用户提供云原生时代的云计算的新界面。其中,Kubernetes网络至关重要,如果管理节点是控制大脑,运行节点是执行载体,那么网络就是将整个容器集群打通形成一个整体的神经网络;与Docker网络相比,Kubernetes网络最大的特点就是让容器组(Pod)拥有自己的身份证,即独立IP,实现在任何节点上的Pod都可以互相直接通信,而不需要任何的NAT地址转换;在不做限制时,Pod可以访问任何网络,并且拥有独立的网络栈,集群内部可见地址与外部可见地址保持一致。在容器网络的具体实现上,Kubernetes通过开放的CNI标准,以插件化方式引入多种容器网络实现,从而支持各种差异化的场景的需求;当前社区比较常见的网络插件主要有Flannel、Calico、Cilium、OVN等,每个插件有不同的模式,需要按照实际的场景来选择使用。按照POD通信方式,有同主机的容器通信与跨主机的容器通信两大类型。

(一)同主机的容器通信在kubernetes集群的节点上,会创建一个veth(virtual ethernet)虚拟设备,同时将veth一端插入到容器网络的命名空间中,一端连接到主机上的网桥(linux bridge)。这样在同一主机上的POD通过veth实现IP地址相互通信。网桥也会分配一个IP地址,充当从POD到不同节点的出口流量网关。

(二)跨主机的容器通信在不同主机上运行的容器POD通过IP地址相互通信,需要通过网络插件实现,按照依赖底层的技术大致可以分为Overlay模式,路由模式,Underlay模式三大类:

① Overlay模式是在二层或三层网络之上再构建起来一个独立的网络,这个网络通常会有自己独立的IP地址空间、交换或者路由的实现。VXLAN协议是目前最流行的Overlay网络隧道协议之一,显著优势就是灵活,对底层网络没有侵入性。

② 路由模式放弃了跨主机容器在L2的连通性,而专注于通过路由协议提供容器在L3的通信方案;路由模式更易于集成到现在的数据中心的基础设施之上,便捷地连接容器和主机,并在报文过滤和隔离方面有着更好的扩展能力及更精细的控制模型。

③ Underlay模式是借助驱动程序将宿主机的底层网络接口直接暴露给容器使用的一种网络构建技术,较为常见的解决方案有MAC VLAN、IP VLAN和直接路由等。

Flannel网络插件

在Flannel VXLAN模式时,插件会在kubernetes集群的每个节点上创建vxlan设备与路由表。发往不同主机上容器数据包都会通过vxlan设备,封装成UDP数据发往目的地;在目的主机上,封装的UDP数据包会被解压并路由到目标POD。但是具体到POD如何分配IP,以及整个容器生命周期过程中CNI插件与其他组件怎么交互配合,是一个相当复杂的过程,本文通过Flannel插件,以及容器运行时Containerd,详细的说明容器网络运行的全过程。

Pod IP地址分配机制

当节点首次向集群注册时,nodeipam作为选项传递给kube-controller-manager的–controllers参数,控制器就会从集群CIDR(集群网络的IP范围)中为每个节点分配一个专用子网(podCIDR)。由于节点子网podCIDR保证不会重复,所以在为节点POD分配的IP地址也确保了唯一;如果需要更改节点的podCIDR,可以通过取消注册节点再重新注册实现。使用以下命令列出节点的podCIDR:

$ kubectl get no-o json | jq '.spec.podCIDR'
10.244.0.0/24

Pod启动时网络配置过程

当一个POD被调度到节点时,会有很多初始化操作,以下是网络配置相关的配置及初始化过程详细步骤:

① POD被调度到容器集群的某个节点上

② 节点的kubelet通过调用CRI插件来创建POD

③ CRI插件创建POD Sandbox ID与POD网络命名空间

④ CRI插件通过POD网络命名空间和POD Sandbox ID来调用CNI插件

⑤ CNI插件配置POD网络,调用顺序从Flannel CNI插件,Bridge CNI 插件到主机IPAM CNI 插件,最后返回POD IP地址。

⑥ 创建Pause容器,并将其添加第三步创建的POD的网络命名空间

⑦ Kubelet调用CRI插件拉取应用容器镜像

⑧ 容器运行时containerd拉取应用容器镜像

⑨ Kubelet调用CRI插件来启动应用容器

⑩ CRI插件调用容器运行时containerd来启动和配置在pod cgroup和namespaces中的应用容器。

CRI插件与CNI插件的交互

在POD初始化过程中,CRI插件会调用CNI插件来完成对POD网络的配置。CNI网络插件都会在kubernetes节点上安装配置代理,并附带CNI配置,然后CRI插件通过该配置来确定要调用那个CNI插件。CNI配置文件的位置默认值为/etc/cni/net.d/,可以自定义配置。同时在每个节点上都会有CNI插件,CNI插件位置默认值为/opt/cni/bin,也可以自定义配置。在containerd作为容器运行时,CNI配置和版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!CNI插件二进制文件的路径可以在containerd配置的[plugins.”io.containerd.grpc.v1.cri”.cni]中指定。

对于Flannel插件,Flanneld作为Flannel守护进程,通常安装在 kubernetes集群的节点上,而install-cni作为init容器,在每个节点上创建 CNI配置文件-/etc/cni/net.d/10-flannel.conflist。Flanneld创建vxlan设备,从API-SERVER获取并监听POD网络元数据。在创建POD时,为整个集群中的所有POD分配路由,通过路由实现POD IP地址相互通信。CRI插件与CNI插件之间的交互如下:

① Kubelet通过CRI插件调用容器运行时

② CRI插件通过CNI配置文件调用CNI插件,为POD创建网络命名空间,网络命名空间在/var/run/netns/文件下

③ Flannel CNI插件配置和调用Bridge CNI plugin

④ Bridge CNI插件在主机上创建cniO网桥,并创建了veth对,一端插入容器网络命名空间,另一端连接到cniO网桥。然后调用已配置的版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!IPAM 插件。

⑤ host-local IPAM插件返回容器的IP地址和网关(cniO桥作为容器的网关),将IP地址分配给POD,桥接插件将网关IP地址分配给cniO桥接。所有分配的IP地址都存储在本地磁盘上的/var/lib/cni/networks/cni0/目录下。

CNI插件间的交互

CNI插件配置POD网络,调用顺序从Flannel CNI插件,Bridge CNI 插件到主机IPAM CNI 插件,最后返回POD IP地址。详情如下:

① Flannel CNI插件:Containerd CRI插件使用CNI配置文件- /etc/cni/net.d/10-flannel.conflist调用Flannel CNI插件:

$ cat /etc/cni/net.d/10-flannel.conflist{  "name": "cni0",  "plugins": [    {      "type": "flannel",      "delegate": {       "ipMasq": false,        "hairpinMode": true,        "isDefaultGateway": true      }    }  ]}

当Flanneld启动时,会从API-SERVER获取podCIDR和其他与网络相关的详细信息,并存储在 -/run/flannel/subnet.env文件中:


FLANNEL_NETWORK=10.244.0.0/16 
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450 
FLANNEL_IPMASQ=false

Flannel CNI插件使用/run/flannel/subnet.env中的信息来配置和调用网桥CNI插件。
② Bridge CNI插件:Flannel CNI插件调用Bridge CNI插件,配置如下:

{
  "name": "cni0",
  "type": "bridge",
  "mtu": 1450,
  "ipMasq": false,
  "isGateway": true,
  "ipam": {
    "type": "host-local",
    "subnet": "10.244.0.0/24"
  }
}

首次调用Bridge CNI插件时,会创建一个linux网桥,并在配置文件中指定“名称”:“cni0”。然后为每一个POD创建veth对,veth对一端位于容器的网络命名空间中,另一端连接到主机网络的网桥上,使用 Bridge CNI插件,主机上的所有容器都连接到主机网络版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!的网桥上。

配置veth对后,Bridge插件调用主机本地IPAM CNI插件,可以在CNI config CRI插件中配置使用具体IPAM 插件。
③ 主机本地 IPAM CNI 插件:Bridge CNI插件使用以下配置调用主机本地 IPAM CNI 插件:


{
  "name": "cni0",
  "ipam": {
    "type": "host-local",
    "subnet": "10.244.0.0/24",
    "dataDir": "/var/lib/cni/networks"
  }
}

Host-local IPAM(IP地址管理)插件从子网返回容器的IP地址,并将分配的IP本地存储在主机上dataDir下指定的目录下- /var/lib/cni/networks/。/var/lib/cni/networks/文件包含分配 IP 的容器 ID。调用时,主机本地IPAM 插件返回以下结果:


{
  "ip4": {
    "ip": "10.244.4.2",
    "gateway": "10.244.4.3"
  },
  "dns": {}
}

总结

总的来说,Kubernetes网络比较复杂,主要是涉及到容器运行时CRI,容器网络标准CNI,以及容器网络插件三个方面配合及协调,同时在灵活易用及性能优化上持续演进。Flannel作为使用最广网络插件,主要具备两大优势:首先从UDP,VXLAN,HOST-GW到IPIP,不仅是场景覆盖与性能的权衡演进,还有对底层操作核心能力的快速支持,社区一直保持活跃。其次开放与云平台的集成,容器大部分运行环境都是在云平台之版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!上,与云平台的集成直接使用底层云能力实现网络能力,提供开箱即用且高效网络方案,将云的能力最大化,始终贯彻云原生的理念。

模式底层网络要求实现模式封包/解包overlay网络转发效率
Flannel UDP三层互通overlay用户态三层
Flannel VXLAN三层互通overlay内核态二层
Flannel host-gw二层互通路由三层
IPIP模式三层互通overlay内核态三层
Cloud VPC三层互通路由三层
0 条回应