文章目录
- 前言
- 一、了解overlay、underlay容器网络
- 二、网络通信
-
- 1.分类
- 2.网络虚拟设备对
-
- [2.1、什么是网络虚拟设备对veth pair?](#2.1、什么是网络虚拟设备对veth pair?)
- 2.2、如何查看容器的网卡与主机的哪个veth设备对是成对的关系?
- 3、vxlan和vtep
- 三、Flannel网络模式剖析
-
- 0、flannel的作用
- 1、flannel三种网络模式
- 2、通过UDP网络模式剖析奠定后续基础
-
- 2.1、跨主机容器通信示例
- 2.2、当container-1发起对container-2的访问请求时,数据包的通信过程如下
- [2.3、路由规则和 TUN 设备](#2.3、路由规则和 TUN 设备)
- [2.4、子网概念和 Etcd 存储](#2.4、子网概念和 Etcd 存储)
- [2.5、UDP 封装和解封装过程](#2.5、UDP 封装和解封装过程)
- [2.6、UDP 模式的性能问题分析](#2.6、UDP 模式的性能问题分析)
- 3、VXLAN网络模式剖析
-
- 3.1、二层网络封装过程
- [3.2、ARP 记录和 MAC 地址获取](#3.2、ARP 记录和 MAC 地址获取)
- 3.3、内部数据帧和外部数据帧
- [3.4、FDB 转发数据库](#3.4、FDB 转发数据库)
- 4、VXLAN模式下完整的网络通信过程
- 四、UDP模式和VXLAN模式对比总结
- 五、Flannel部署
- 总结
前言
在传统的单机环境下,容器之间的网络通信相对简单,通常通过Docker默认的网桥模式就可以实现。然而,将容器部署到分布式的集群环境中时,跨主机的容器网络通信就成为了一个亟待解决的问题。因为在Docker的默认配置下,不同宿主机上的容器是无法直接通过IP地址进行互相访问的。这是因为每个宿主机上的Docker网络都是独立的,它们各自管理着自己的私有网段。这种隔离虽然在安全性方面有所裨益,但也阻碍了容器之间的直接通信。
因此为了解决这个"跨主通信"的难题,容器社区提出了多种网络方案。这些方案的核心目标是在不同宿主机上的容器之间建立一个虚拟的网络,使得它们能够像在同一个局域网内一样进行通信。这就是所谓的"覆盖网络"(Overlay Network)技术。
一、了解overlay、underlay容器网络
underlay容器网络
yaml
代表承载容器的虚拟机或者物理机网络环境能够识别、转发容器ip。开源网络插件方案如Flannel的host-gw模式、calico的bgp模式。
容器网络可以不通过隧道封装,依托于网络插件组件功能(增加路由表)和网络要求(k8s管理的节点在同一子网,不跨三层网络)
overlay容器网络
yaml
代表承载容器的虚拟机或者物理机网络环境能够识别、转发容器ip。需要通过每个虚拟机上的封包、解包处理后在转发给容器。
开源网络插件方案如Flannel的vxlan模式、calico的ipip模式.
容器网络通过节点隧道封装后基于承载网络转发,只要求kubernetes管理的节点三层网络可达
二、网络通信
1.分类
yaml
(1)容器间通信:
同一个pod内的多个容器间的通信,通过lo即可实现;
(2)pod之间的通信:
同一节点的pod之间通过cni网桥转发数据包。
不同节点的pod之间的通信需要网络插件支持。
(3)pod和service通信:
通过iptables或ipvs实现通信,ipvs取代不了iptables,因为ipvs只能做负载均衡,而做不了nat转换。
(4)pod和外网通信:
iptables的MASQUERADE。
(5)Service与集群外部客户端的通信:
(ingress、nodeport、loadbalancer...)
2.网络虚拟设备对
2.1、什么是网络虚拟设备对veth pair?
yaml
veth是虚拟以太网卡的缩写。veth设备总是成对的,因此称为veth pair.一端发送数据,另一端接收数据,常被用于跨网络空间的通信。
但如果仅有veth pair设备,容器是无法访问外部网络的,因为从容器发出的数据包实际上进入了veth pair设备对的协议栈,如果容器需要访问网络还需要
使用网桥bridge等技术将veth pair设备接收的数据包通过某种方式转发出去。以下是veth pair的基本工作原理
2.2、如何查看容器的网卡与主机的哪个veth设备对是成对的关系?
yaml
方法一
在容器查看该文件中的值 cat /sys/class/net/eth0/iflink
在主机查看 /sys/class/net下面的所有的子目录ifindex的值和容器里查出来的值进行对比,一样的话就是一个设备对
方法二
在容器执行 ip link show eth0,如下所示
116: eth0@if117: xxxx
从这个可以看出116是eth0接口的index,117是和它成对的veth的index
在主机上执行 ip link show | grep 117即可查看设备对
3、vxlan和vtep
3.1、vtep
yaml
VTEP设备是Flannel VXLAN模式的核心组件,它通过在物理网络之上构建虚拟覆盖网络,实现了高效、灵活的容器跨主机通信。
为了能够在二层网络上打通"隧道",VXLAN 会在宿主机上设置一个特殊的网络设备作为"隧道"的两端。
这个设备就叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端点)
作用
yaml
VTEP设备在每个宿主机上都会被创建,通常命名为flannel.1。这个设备既有IP地址,也有MAC地址,可以被视为一个虚拟的网络接口。
VTEP设备的主要作用:
是执行VXLAN封装和解封装操作,使得容器可以在不同宿主机之间进行通信.
注意事项:
它进行封装和解封装的对象,是二层数据帧(Ethernet frame),而且这个工作的执行流程,
全部是在内核里完成的(因为 VXLAN 本身就是 Linux 内核中的一个模块)
当在k8s宿主机上查看网络设备时,可以看到flannel.1设备的存在
shell
[root@k8s-master~]# ip addr show flannel.1
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN
link/ether c6:58:f3:29:cb:d8 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 brd 10.244.1.0 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::c458:f3ff:fe29:cbd8/64 scope link
valid_lft forever preferred_lft forever
从上述输出中,我们可以看到flannel.1设备的MAC地址和IP地址。这些信息在VXLAN网络中用于标识不同的VTEP设备
优点
yaml
VTEP设备的引入使得Flannel能够在现有的三层网络上构建一个虚拟的二层网络。
这种方式不仅保证了容器网络的隔离性,还提供了良好的可扩展性。
与UDP模式相比,VXLAN模式的VTEP设备能够在内核态完成封装和解封装操作,大大减少了用户态和内核态之间的切换,从而提高了网络性能。
此外,VTEP设备还支持多播和广播,这使得ARP请求和DHCP等基于广播的协议能够在VXLAN网络中正常工作。这一特性进一步增强了VXLAN网络的灵活性和兼容性
3.2、vxlan相关概念
vxlan作用
yaml
VXLAN (Virtual Extensible LAN) 是一种覆盖网络技术,全称是虚拟可扩展的局域网。
1、它主要用于解决传统 VLAN 技术的一些限制,如 VLAN ID 数量限制(只有4096个)。
VXLAN 可以支持高达1600万个虚拟网络,极大地扩展了网络的规模和灵活性。
2、提供封装与隧道技术
VXLAN 通过封装原始的以太网帧(Layer 2)到 UDP 数据包(Layer 3)中来工作。
这意味着它可以跨越不同的网络和子网,实现跨网络边界的通信。
3、提供VXLAN 网络标识符 (VNI)
每个 VXLAN 网络都有一个唯一的标识符,称为 VNI(VXLAN Network Identifier),它提供了地址隔离
确保各个 VXLAN 网络之间的数据包不会互相干扰
4、提供VTEP(VXLAN Tunnel Endpoint)
VTEP 是 VXLAN 架构中的端点设备,负责封装和解封装数据包,且这个工作的执行流程,全部是在内核里完成的。
每个通过 VXLAN 通信的网络设备都有一个或多个 VTEP
vxlan通信流程---啰嗦版
yaml
1、每台设备上已经有了VTEP设备和相应的路由规则,源容器发出请求后,数据包包含了源容器ip和目的容器ip,
这个原始数据包首先会通过虚拟网卡对出现在docker0网桥上,然后根据路由规则到达VTEP设备,
且要发往的网关地址为目的VTEP设备的ip,然后VTEP会从ARP表里获取到对应的MAC地址,并进行二层封包,得到一个二层数据帧,
我们把它称为"内部数据帧"。
yaml
2、 接下来,Linux 内核会在"内部数据帧"前面,加上一个特殊的 VXLAN 头,用来表示这实际上是一个 VXLAN 要使用的数据帧,
而这个 VXLAN 头里有一个重要的标志叫作 VNI,它是 VTEP 设备识别某个数据帧是不是应该归自己处理的重要标识,
而在 Flannel 中,VNI 的默认值是 1
yaml
3、然后,Linux 内核会从FDB数据库里读取到对应的目的宿主机ip,把"内部数据帧"和目的宿主机ip封装进一个 UDP 包里,
我们把它称为"外部数据帧",这个"外部数据帧"里包含了源节点ip和目的节点ip,
然后Linux内核会在这个"外部数据帧"前面加上目的宿主机的MAC地址,这样,封包工作就宣告完成了。
yaml
4、接下来,源宿主机上的 flannel.1 设备就可以把这个数据帧从源宿主机的 eth0 网卡发出去,
数据帧会经过宿主机网络来到目的宿主机的 eth0 网卡。这时候,目的宿主机的内核网络栈会发现这个数据帧里有 VXLAN Header,
并且 VNI=1。所以 Linux 内核会对它进行拆包,拿到里面的内部数据帧,然后根据 VNI 的值,把它交给 flannel.1 设备。
而 flannel.1 设备则会进一步拆包,取出"原始 IP 包",
然后根据路由规则,将它交给docker0网桥,经过虚拟网卡对就进入到了目的容器里。
vxlan通信流程---精简版
yaml
1、首先,当一个容器发送数据包到另一个宿主机上的容器时,这个数据包会被路由到本机的VTEP设备。
2、然后,VTEP设备会对这个原始数据包进行VXLAN封装。
封装过程中,VTEP会在原始数据包外层添加VXLAN头部、UDP头部、IP头部和以太网头部。
这个过程将原始的二层数据帧封装成一个可以在物理网络中传输的UDP数据包。
3、接下来,封装后的数据包会通过物理网络发送到目标宿主机。
4、在目标宿主机上,该主机的VTEP设备会接收到这个封装后的数据包,并进行解封装操作。
解封装过程会移除外层的头部信息,还原出原始的数据包。
5、最后,解封装后的原始数据包会被转发到目标容器。
上述vxlan通信过程可以使用下方图进行一个描述
三、Flannel网络模式剖析
0、flannel的作用
yaml
1、协助k8s,给每一个Node上的Docker容器分配互相不冲突的IP地址。
2、建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。
覆盖网络是建立在另一个网络之上并由其基础设施支持的虚拟网络。
覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础设施分离,在将封装的数据包转发到端点后,将其解封装。
3、创建一个新的虚拟网卡Flannel0接收docker网桥的数据,通过维护路由表,对接受到的数据进行封包和转发。
4、etcd保证了所有node上flanneld所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化
1、flannel三种网络模式
udp网络模式
yaml
UDP 模式是 Flannel 最早支持的一种实现方式。
它通过在用户态将容器间通信的数据包封装在 UDP 包中进行传输。
虽然实现简单直观,但由于涉及频繁的用户态与内核态切换,以及数据的多次拷贝,导致性能较差。因此,UDP 模式目前已被弃用。
host-gw网络模式
yaml
host-gw(host gateway)模式是一种直接利用宿主机作为网关的实现方式。
它通过在各个宿主机上添加路由规则,将目标容器所在宿主机的 IP 地址设置为网关,从而实现容器跨主机通信。
这种模式性能较高,但要求所有宿主机都在同一个二层网络内
【工作原理】
当容器需要与其他宿主机上的容器通信时,数据包会先发送到本机的网桥设备。
然后,根据路由表,数据包会被转发到目标容器所在的宿主机。
目标宿主机接收到数据包后,再将其转发给目标容器。
【优点】
性能高:由于不需要额外的封装和解封装过程,host-gw模式的性能接近于原生网络。
配置简单:不需要额外的网络设备或协议支持。
【缺点】
要求所有宿主机在同一个二层网络:这限制了其在跨数据中心或云环境中的应用。
路由表膨胀:随着节点数量的增加,每个节点上的路由表会变得很大。
vxlan网络模式
yaml
VXLAN(Virtual Extensible LAN)是 Linux 内核原生支持的网络虚拟化技术。
它通过在现有的三层网络上覆盖一层虚拟的二层网络,实现了跨主机的容器通信。
VXLAN 模式能够在内核态完成封装和解封装工作,因此性能较好,逐渐成为了主流的容器网络方案。
【工作原理】
当容器需要与其他宿主机上的容器通信时,数据包会被路由到本机的VTEP设备
然后,VTEP设备会对这个原始数据包在内核进行VXLAN封装,将原始的二层数据帧封装成一个可以在物理网络中传输的UDP数据包
封装后的数据包会通过物理网络发送到目标宿主机。在目标宿主机上,该主机的VTEP设备会接收到这个封装后的数据包,并进行解封装操作。
解封装过程会移除外层的头部信息,还原出原始的数据包。
【优点】
性能高:在内核态完成封装和解封装工作
覆盖和底层网络是完全独立的,因此如果底层网络拓扑发生变化,覆盖网络不会受到影响
2、通过UDP网络模式剖析奠定后续基础
2.1、跨主机容器通信示例
yaml
为了理解Flannel的UDP模式工作原理,来看一个具体的跨主机容器通信示例。
假设有两台宿主机:Node 1和Node 2,其中:
在Node 1上,我们有一个名为container-1的容器,其IP地址为10.244.0.13。对应的docker0网桥地址是10.244.0.1/24。
在Node 2上,我们有另一个名为container-2的容器,其IP地址为10.244.1.14。对应的docker0网桥地址是10.244.1.1/24。
需求:
现在让container-1能够访问container-2。
2.2、当container-1发起对container-2的访问请求时,数据包的通信过程如下
yaml
1、container-1发出一个IP包,源地址为10.244.0.13,目的地址为10.244.1.14。
2、这个IP包首先到达Node 1的docker0网桥。由于目的地址10.244.1.14不在docker0网桥的网段内,所以这个IP包会被转发给默认路由。
3、根据Node 1上的路由规则,这个IP包会被送往一个名为flannel0的设备。flannel0是一个TUN设备,它在三层网络上工作。
4、flannel0设备会将这个IP包交给用户态的flanneld进程处理。
5、flanneld进程通过查询Etcd,得知目的IP地址10.244.1.14所在的子网对应的宿主机是Node 2,其IP地址为11.101.1.3。
6、flanneld将原始的IP包封装在一个UDP包里,然后发送给Node 2的8285端口(flanneld默认监听的端口)。
7、当这个UDP包到达Node 2后,会被Node 2上的flanneld进程接收和解包,还原出原始的IP包。
8、Node 2的flanneld将还原出的IP包交给本机的flannel0设备。
9、flannel0设备将IP包转发给docker0网桥。
10、docker0网桥根据目的IP地址,将包转发给container-2。
yaml
对上述通信流程的总结:
通过这个过程,Flannel在不同宿主机上的两个容器之间建立了一个"隧道",使得它们可以直接使用IP地址进行通信,
而无需关心容器和宿主机的具体分布情况。
这种UDP封装的方式虽然实现了跨主机的容器通信,但由于涉及多次用户态与内核态的切换,以及数据的多次拷贝,因此性能较差。
这也是为什么UDP模式后来被VXLAN等更高效的模式所取代的原因。
2.3、路由规则和 TUN 设备
在上述通信过程中反复提到了路由规则和TUN设备,在此对二者进行详细说明
yaml
在Flannel的UDP模式中,路由规则和TUN设备扮演着关键角色,它们共同构建了容器跨主机通信的基础架构。
首先,查看Flannel在宿主机上创建的路由规则。
以Node 2为例,如上图所示
凡是发往 10.244.2.0/24 网段的 IP 包,都需要经过 flannel.1 设备发出,并且它最后被发往的网关地址是:10.244.2.0。
10.244.2.0 正是 Node 2 上的 VTEP 设备(也就是 flannel.1 设备)的 IP 地址,如上图所示
当容器发出的数据包到达宿主机时,Linux内核会根据这些路由规则决定数据包的下一步去向。
对于跨主机通信的情况,数据包会被路由到flannel0设备。
注意事项:
1、flannel0是一个TUN设备,它是Linux内核提供的一种虚拟网络设备
2、每台宿主机上的 flanneld 进程会负责维护相关的路由规则
3、TUN设备的特殊之处在于它工作在网络层(第三层),主要用于处理IP数据包
4、当一个IP数据包被发送到TUN设备时,它不会被转发到物理网络,而是被递交给创建该设备的用户空间程序
flannel0(TUN)设备与用户空间的flanneld进程工作流程
yaml
1、当一个数据包被路由到flannel0设备时,linux内核将这个数据包从内核空间传递到用户态空间的flanned进程中
2、flanneld进程接收到这个IP数据包后,会根据数据包的目的IP地址查询Etcd,确定目标容器所在的宿主机
3、然后,flanneld将原始的IP数据包封装在一个UDP包中,并将这个UDP包发送到目标宿主机
4、在目标宿主机上,flanneld进程接收到UDP包后,解封装出原始的IP数据包,并将其写入本机的flannel0设备
5、目标宿主机的Linux内核接收到这个IP数据包,然后根据本机的路由规则将其转发到正确的容器
总结
yaml
这种设计允许Flannel在不修改Linux内核的情况下,实现了跨主机的容器通信。
TUN设备(flannel0)充当了用户空间和内核空间之间的桥梁,使得flanneld可以接管容器间通信的路由过程。
然而,这种设计也带来了性能上的开销。
每个数据包都需要经过三次用户空间和内核空间的切换,以及多次数据拷贝,这inevitably会影响网络性能。
分别是以下三次用户态和内核态的切换:
1)第一次,用户态的容器进程发出的 IP 包经过 docker0 网桥进入内核态;
2)第二次,IP 包根据路由表进入 TUN(flannel0)设备,从而回到用户态的 flanneld 进程;
3)第三次,flanneld 进行 UDP 封包之后重新进入内核态,将 UDP 包通过宿主机的 eth0 发出去。
这也是为什么Flannel的UDP模式虽然实现简单,但在实际生产环境中较少使用的原因
2.4、子网概念和 Etcd 存储
子网概念
yaml
在Flannel的网络架构中,子网(Subnet)是一个核心概念。
每台宿主机都被分配一个独立的子网,该子网内的所有容器都属于这个子网。这种设计使得容器网络的管理变得更加简单和高效。
在上述例子中,Node 1的子网是10.244.1.0/24,而Node 2的子网是10.244.2.0/24。
这意味着Node 1上的所有容器都会获得10.244.1.0/24网段内的IP地址,而Node 2上的所有容器则会获得10.244.2.0/24网段内的IP地址。
etcd存储
yaml
Flannel使用Etcd作为分布式存储系统来保存这些子网信息。
1、Etcd是一个高可用的键值存储系统,它在Flannel的网络方案中扮演着重要角色。
2、Flannel将子网与宿主机的对应关系存储在Etcd中,这样集群中的所有节点都可以获取到这些信息
3、Flannel的flanneld进程会定期从Etcd中读取这些信息,并据此更新本机的路由表和ARP表。
当一个容器需要与另一个宿主机上的容器通信时,flanneld就可以根据这些信息来确定目标容器所在的宿主机,并正确地封装和转发数据包
yaml
基于Etcd的子网管理方式有几个显著的优点:
动态性: 当新的宿主机加入集群或现有宿主机离开集群时,子网分配信息可以动态更新。
一致性: 所有节点都可以从Etcd获取到一致的网络拓扑信息,确保了整个集群网络配置的一致性。
可扩展性: 这种方式可以轻松支持大规模集群,因为Etcd本身就是为高可用和大规模设计的。
灵活性: 通过修改Etcd中的数据,可以灵活地调整网络配置,而无需重启整个集群。
然而这种设计也带来了一些挑战。最主要的是,Etcd成为了整个网络的一个关键依赖。
如果Etcd集群出现问题,可能会影响到整个容器网络的正常运行。
因此,在生产环境中,通常需要部署高可用的Etcd集群,并定期进行备份。
总的来说,Flannel通过Etcd存储子网信息的方式,巧妙地解决了容器跨主机通信时的寻址问题。
这为实现高效的容器网络奠定了基础,也为后续的VXLAN等更高效的网络模式提供了必要的支持
2.5、UDP 封装和解封装过程
yaml
在Flannel的UDP模式中,封装和解封装过程是实现容器跨主机通信的核心。这个过程涉及到数据包在不同网络层之间的转换,
以及在宿主机之间的传输。理解UDP模式的封装和解封装过程对于理解容器网络的工作原理非常有帮助。
它为我们展示了容器网络方案需要解决的核心问题,以及解决这些问题的基本思路。
这为理解更高效的网络方案,如VXLAN模式,奠定了基础。让我们详细探讨UDP模式中,封装和解封装过程的过程。
拆解UDP封装过程
yaml
首先,当源容器(例如container-1)发送一个数据包到目标容器(例如container-2)时,这个原始的IP数据包会经过docker0网桥,
然后根据路由规则被转发到flannel0设备。flannel0作为一个TUN设备,会将这个数据包从内核空间传递到用户空间的flanneld进程。
flanneld进程接收到这个原始IP数据包后,会进行UDP封装。
封装过程包括以下步骤:
1、flanneld首先检查数据包的目的IP地址,确定目标容器所在的宿主机。
2、flanneld然后创建一个新的UDP数据包。这个UDP数据包的源IP地址是当前宿主机的IP地址,目的IP地址是目标容器所在宿主机的IP地址。
UDP端口通常是8285。
3、原始的IP数据包被放入这个UDP数据包的负载部分。
4、flanneld将封装好的UDP数据包交给宿主机的网络栈,由宿主机的网络栈负责将这个UDP包发送出去。
拆解UDP解封装过程
yaml
当封装好的UDP包到达目标宿主机后,解封装过程开始。
这个过程基本上是封装过程的逆操作:
1、目标宿主机的网络栈接收到UDP包,发现目的端口是8285,于是将这个包交给监听在8285端口的flanneld进程。
2、flanneld进程接收到UDP包后,从UDP包的负载中提取出原始的IP数据包。
3、flanneld将提取出的原始IP包写入本机的flannel0设备。
4、Linux内核接收到这个IP包,根据路由规则将其转发给docker0网桥。
5、docker0网桥根据IP包的目的地址,将包转发给目标容器。
2.6、UDP 模式的性能问题分析
yaml
UDP模式的主要性能问题来自于频繁的用户态和内核态切换.这种频繁的状态切换会带来显著的性能开销。
即:
1)第一次,用户态的容器进程发出的 IP 包经过 docker0 网桥进入内核态;
2)第二次,IP 包根据路由表进入 TUN(flannel0)设备,从而回到用户态的 flanneld 进程;
3)第三次,flanneld 进行 UDP 封包之后重新进入内核态,将 UDP 包通过宿主机的 eth0 发出去。
从上述流程图中我们可以清楚地看到,一个简单的数据包发送过程涉及多次用户态和内核态的切换,以及多次数据拷贝
yaml
UDP网络模式性能问题在实际应用中的表现可能如下:
1、较高的网络延迟: 由于每个数据包都需要经过多次处理和状态切换,网络延迟会显著增加。
2、CPU使用率升高: 频繁的状态切换和数据拷贝会消耗大量的CPU资源。
3、吞吐量受限: 由于单个flanneld进程需要处理所有流量,在高并发情况下可能会成为瓶颈。
4、内存带宽压力: 多次数据拷贝会增加内存带宽的使用。
5、网络效率降低: UDP封装增加了数据包大小,降低了网络的有效载荷比例。
正是由于这些性能问题,UDP模式在实际生产环境中很少被使用。
相比之下,VXLAN模式通过在内核态实现封装和解封装,大大减少了用户态和内核态的切换,同时也减少了数据拷贝的次数,因此能够提供更好的性能
UDP网络模式分析完毕,接着在此基础上引出vxlan网络模式
3、VXLAN网络模式剖析
在上述中已经对vtep、vxlan、封包有个基本了解,因此分析vxlan模式时从二层网络封装过程开始着手
3.1、二层网络封装过程
yaml
VXLAN的二层网络封装过程,不仅展示了如何在现有的IP网络上构建虚拟的二层网络,还为我们提供了一个优化网络性能的思路。
在Flannel的VXLAN模式中,二层网络封装过程是实现容器跨主机通信的核心机制。
这个过程涉及到将原始的二层数据帧封装到一个新的UDP数据包中,以便在三层网络上传输。
封装过程
yaml
首先,当源容器发送一个数据包到目标容器时,这个原始的二层数据帧会经过docker0网桥,然后根据路由规则被转发到flannel.1设备。
flannel.1是一个VXLAN设备,也被称为VTEP(VXLAN Tunnel End Point)
VTEP设备接收到这个原始的二层数据帧后,会进行VXLAN封装。
这个封装过程的关键在于,它将原始的二层数据帧封装在一个UDP包中,使得这个数据帧可以在三层网络(IP网络)中传输。
这就好像在现有的IP网络之上,创建了一个虚拟的二层网络。
封装过程包括以下步骤:
1、VTEP设备首先检查数据帧的目的MAC地址,确定目标容器所在的宿主机。
这个信息是通过ARP表和FDB(Forwarding Database)获得的。
2、VTEP设备然后创建一个新的UDP数据包。
这个UDP数据包的源IP地址是当前宿主机的IP地址,目的IP地址是目标容器所在宿主机的IP地址。UDP端口通常是4789(VXLAN的标准端口)。
3、在UDP数据包的负载部分,VTEP设备会添加一个VXLAN头。
这个VXLAN头包含一个24位的VNI(VXLAN Network Identifier),用于标识不同的VXLAN网络。
4、原始的二层数据帧被放在VXLAN头之后,构成UDP数据包的完整负载。
5、VTEP设备将封装好的UDP数据包交给宿主机的网络栈,由宿主机的网络栈负责将这个UDP包发送出去
优点
yaml
它允许不同宿主机上的容器直接使用二层地址(MAC地址)进行通信,就好像它们在同一个局域网内。
封装过程完全在内核态完成,避免了频繁的用户态和内核态切换,提高了性能。
VXLAN封装增加的开销相对较小,对网络性能的影响较小。
这种方式可以很好地与现有的网络基础设施集成,不需要对底层网络做特殊配置。
然而,这种封装方式也带来了一些挑战。它增加了网络包的大小,可能会导致MTU(Maximum Transmission Unit)问题。
此外,VXLAN封装也增加了一定的处理复杂度,可能会对网络延迟产生轻微影响
3.2、ARP 记录和 MAC 地址获取
yaml
在Flannel的VXLAN模式中,ARP记录和MAC地址的获取是实现容器跨主机通信的关键环节。
这个过程涉及到如何在虚拟的二层网络中定位和识别不同的VTEP设备。
首先,我们需要理解在VXLAN网络中,每个VTEP设备(即flannel.1设备)都有自己的IP地址和MAC地址。
这些地址信息是VXLAN网络内部使用的,与宿主机的实际网络地址是不同的。
当一个VTEP设备需要向另一个VTEP设备发送数据时,它需要知道目标VTEP设备的MAC地址。
在传统的二层网络中,我们通常使用ARP(地址解析协议)来获取IP地址对应的MAC地址。
然而,在Flannel的VXLAN网络中,这个过程被稍微简化了。Flannel采用了一种更直接的方式来维护ARP记录。
当一个新的节点加入Flannel网络时,该节点上的flanneld进程会将自己VTEP设备的IP地址和MAC地址信息广播到整个集群。
集群中的其他节点接收到这个信息后,会在本地创建相应的ARP记录。这个过程是自动完成的,无需实际的ARP请求和响应
可以通过以下命令查看本地的ARP记录
yaml
这里的每一行都代表一个ARP记录,其中10.244.1.0和10.244.2.0是VTEP设备的IP地址,而后面的MAC地址则是对应的VTEP设备的MAC地址。
值得注意的是,这些ARP记录被标记为PERMANENT,这意味着它们是静态的,不会因为超时而被删除。
这种设计可以减少网络中的ARP流量,提高网络效率。
当一个VTEP设备需要向另一个VTEP设备发送数据时,它会首先查找本地的ARP表。
如果找到了目标IP对应的MAC地址,它就可以直接构造二层数据帧,将原始的IP包封装其中。
如果出现了ARP表中没有记录的情况(这种情况在正常运行的集群中应该很少发生),Flannel会尝试重新获取ARP信息。
这可能涉及到与etcd的通信,或者触发一次集群范围的ARP更新。
这种ARP记录和MAC地址获取的机制有几个明显的优点
yaml
1、减少了网络中的ARP请求和响应流量,提高了网络效率。
2、加快了数据包的封装过程,因为不需要在发送每个数据包时都进行ARP解析。
3、简化了网络故障排查,因为ARP记录是静态的,易于查看和理解。
然而这种机制也带来了一些挑战:
1、如果集群中的节点频繁变动,可能需要更频繁地更新ARP记录。
2、如果ARP记录出现错误,可能会导致网络通信问题。
3.3、内部数据帧和外部数据帧
在Flannel的VXLAN模式中,内部数据帧和外部数据帧是两个关键概念,它们共同构成了VXLAN封装的核心
内部数据帧组成
yaml
内部数据帧是指原始的、由容器发出的数据帧。
当容器需要与其他宿主机上的容器通信时,它会生成一个普通的以太网数据帧。
这个数据帧包含了源容器的MAC地址、目标容器的MAC地址,以及IP层的信息。
然而,这个数据帧无法直接在宿主机网络中传输,因为宿主机网络并不知道如何处理容器的MAC地址
外部数据帧组成
yaml
外部数据帧的结构比内部数据帧要复杂得多。
从外到内,它包括以下几个部分:
1、外部以太网头: 包含源宿主机和目标宿主机的MAC地址。
2、外部IP头: 包含源宿主机和目标宿主机的IP地址。
3、外部UDP头: VXLAN使用UDP作为传输协议,默认端口是4789。
4、VXLAN头: 包含一个24位的VXLAN网络标识符(VNI)。
5、内部数据帧: 原始的、由容器发出的完整以太网帧。
这种封装和解封装的过程使得容器网络可以跨越物理网络的限制,实现跨主机通信。
内部数据帧保持了容器网络的独立性,而外部数据帧则利用了现有的物理网络基础设施。
当外部数据帧到达目标宿主机后,会经历一个解封装的过程。目标宿主机会依次剥离外部以太网头、IP头、UDP头和VXLAN头,最终得到原始的内部数据帧。
然后,这个内部数据帧会被转发到目标容器
3.4、FDB 转发数据库
yaml
在VXLAN模式中,FDB(Forwarding Database,转发数据库)是一个关键组件,它在Flannel网络中扮演着类似于二层交换机的角色。
FDB主要负责维护VTEP设备的MAC地址与对应宿主机IP地址之间的映射关系,这对于VXLAN网络中的数据包转发至关重要。
3.4.1、工作原理
yaml
FDB的工作原理类似于传统以太网交换机中的MAC地址表。
当一个VTEP设备需要向另一个VTEP设备发送数据时,它会查询FDB以获取目标VTEP的MAC地址对应的宿主机IP地址。
这个过程确保了VXLAN封装的数据包能够正确地发送到目标宿主机。
FDB记录由flanneld进程负责维护。当一个新的节点加入Flannel网络时,flanneld会自动更新所有节点的FDB记录。可以通过bridge fdb命令来查看FDB的内容
上述fdb记录的含义是:MAC地址为xxxx的VTEP设备位于IP地址为10.xxx的宿主机上。permanent标志表示这是一个静态记录,不会自动过期
3.4.2、FDB工作流程
yaml
1、当容器1需要向容器2发送数据时,数据包首先到达容器1所在宿主机的VTEP设备。
2、VTEP设备会查询FDB,获取目标容器所在VTEP设备的MAC地址对应的宿主机IP地址。
3、然后,VTEP设备会使用这个IP地址作为外层IP头的目的地址,将原始数据帧封装在VXLAN包中发送出去。
3.4.3、优缺点
yaml
优点:
FDB的使用大大提高了VXLAN网络的效率。通过维护VTEP设备的MAC地址与宿主机IP地址的映射关系,
FDB避免了在VXLAN网络中进行广播查找的需求,从而减少了网络开销,提高了转发效率
缺点:
1、在大规模集群中,FDB表可能会变得相当大,这可能会对内存使用和查找性能产生影响。
2、当节点频繁加入或离开集群时,FDB表的更新也可能成为一个潜在的性能瓶颈。
4、VXLAN模式下完整的网络通信过程
背景: 跨节点的 容器A(IP:10.244.0.2)需要向容器B(IP:10.244.1.3)发送数据时封包过程
yaml
1、容器A生成原始IP包,源地址为10.244.1.2,目的地址为10.244.2.3。
2、这个IP包通过容器的虚拟网卡发送出去,到达宿主机的docker0网桥。
3、docker0网桥根据路由规则,将这个包转发给flannel.1设备(VTEP设备)。
4、flannel.1设备接收到这个IP包后,开始VXLAN封装过程。它首先需要知道目的容器所在的VTEP设备的MAC地址。为此,它会查询本地的ARP表。
5、获得目的VTEP的MAC地址后,flannel.1设备会构造一个内部以太网帧,将原始IP包封装其中。
6、接下来,flannel.1设备需要知道目的VTEP设备所在的宿主机IP地址。它会查询FDB(转发数据库)来获取这个信息。
7、得到目的宿主机的IP地址后,flannel.1设备会构造一个VXLAN头,其中包含VNI(VXLAN Network Identifier)等信息。
8、然后它会将内部以太网帧和VXLAN头封装在一个UDP包中。UDP的目的端口通常是4789。
9、最后这个UDP包会被进一步封装在一个IP包中,源地址是本机IP,目的地址是目标宿主机IP。这个IP包再被封装在一个外部以太网帧中。
10、这个完整封装的外部数据帧通过宿主机的物理网卡发送出去。
跨节点的 容器A(IP:10.244.0.2)需要向容器B(IP:10.244.1.3)发送数据时解封包过程
yaml
当封装后的数据帧到达目标宿主机时,会经历以下步骤:
1、目标宿主机的物理网卡接收到数据帧,发现是一个UDP包,目的端口是4789。
2、Linux内核识别出这是一个VXLAN包,将其转交给对应的VTEP设备(flannel.1)处理。
3、flannel.1设备接收到这个VXLAN包,首先会解析VXLAN头,检查VNI是否匹配。
4、确认VNI匹配后,flannel.1设备会解封装出内部以太网帧。
5、flannel.1设备查看内部以太网帧的目的MAC地址,确认是否是发给自己的。
6、如果是发给自己的,flannel.1设备会进一步解封装出原始的IP包。
7、flannel.1设备将这个IP包转发给docker0网桥。
8、docker0网桥根据IP包的目的地址,将其转发给对应的容器。
9、目标容器B接收到这个IP包,完成整个通信过程。
vxlan模式下网络数据流向面试说法
yaml
1. pod1向pod2发送ping,查找pod1路由表,运用veth-pair技术把数据包发送到cni0(10.244.0.1)
2. cni0查找node1路由,把数据包转发到flannel.1
3. flannel.1虚拟网卡设备对,收到报文后将按照vtep的配置进行封包,首先通过etcd得知pod2属于Node2,并得到node2ip地址,然后通过节点A中的转发表得到节点B对应的vtep的MAC地址,且这个MAC地址不是通过组播学习的,而是通过Api server处watch Node发现的,根据flannel1.1设备创建时的设置参数(VNI、local、IP、PORT)进行封包。
4. 通过node1与node2直接的网络连接,vxlan包到达node2的eth1接口
5. 通过UDP端口8472传输VXLAN数据包给vtep设备flannel.1进行解包
6. 解封装后的IP包匹配node2中的路由表,内核将IP包转发给cni0
7. cni0网桥再把数据包转发给pod2
四、UDP模式和VXLAN模式对比总结
对比 | udp模式 | vxlan模式 |
---|---|---|
性能对比 | UDP模式的主要性能瓶颈来自于频繁的用户态和内核态切换,以及多次数据拷贝。每个数据包都需要经过用户态的flanneld进程处理,这不可避免地引入了额外的延迟和CPU开销。特别是在高吞吐量的场景下,这种开销会变得相当显著 | VXLAN模式的大部分操作都在内核态完成。数据包的封装和解封装过程由Linux内核直接处理,避免了频繁的状态切换和数据拷贝。这使得VXLAN模式能够提供更低的延迟和更高的吞吐量 |
可扩展性 | UDP模式中,所有的网络流量都需要经过用户态的flanneld进程处理。这意味着flanneld进程可能成为性能瓶颈,特别是在大规模集群中。随着节点数量的增加,这个问题会变得更加明显。 | VXLAN模式则不存在这个问题。由于数据包的处理都在内核态完成,它能够更好地利用现代多核处理器的能力。此外,VXLAN的设计本身就考虑了大规模网络的需求,它支持高达16777216个虚拟网络,远远超过了大多数实际场景的需求 |
网络隔离 | UDP模式的网络隔离能力相对有限。虽然它也可以通过配置实现一定程度的网络隔离,但灵活性和可扩展性都不如VXLAN模式 | VXLAN模式提供了更好的支持。VXLAN使用VNI(VXLAN Network Identifier)来标识不同的虚拟网络,这使得在同一物理网络上可以轻松创建多个相互隔离的虚拟网络 |
兼容性 | UDP模式的优势在于它的简单性。它只需要普通的IP网络就可以工作,不需要特殊的网络设备支持 | VXLAN模式则需要网络设备对VXLAN协议的支持 |
五、Flannel部署
1、官方部署文档(高版本0.26.1)
yaml
https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
2、自身环境现使用(低版本0.13)
k8s集群是1.18版本的
yaml
---kube-flannel-ds.yml
apiVersion: v1
items:
- apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: flannel
tier: node
name: kube-flannel-ds
namespace: kube-system
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
app: flannel
tier: node
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
containers:
- args:
- --ip-masq
- --kube-subnet-mgr
command:
- /opt/bin/flanneld
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: quay.io/coreos/flannel:v0.13.1-rc1
imagePullPolicy: IfNotPresent
name: kube-flannel
resources:
limits:
cpu: 200m
memory: 400Mi
requests:
cpu: 100m
memory: 200Mi
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
privileged: false
volumeMounts:
- mountPath: /run/flannel
name: run
- mountPath: /etc/kube-flannel/
name: flannel-cfg
dnsPolicy: ClusterFirst
hostNetwork: true
initContainers:
- args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
command:
- cp
image: quay.io/coreos/flannel:v0.13.1-rc1
imagePullPolicy: IfNotPresent
name: install-cni
volumeMounts:
- mountPath: /etc/cni/net.d
name: cni
- mountPath: /etc/kube-flannel/
name: flannel-cfg
priorityClassName: system-node-critical
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: flannel
serviceAccountName: flannel
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoSchedule
operator: Exists
volumes:
- hostPath:
path: /run/flannel
type: ""
name: run
- hostPath:
path: /etc/cni/net.d
type: ""
name: cni
- configMap:
defaultMode: 420
name: kube-flannel-cfg
name: flannel-cfg
updateStrategy:
rollingUpdate:
maxUnavailable: 1
type: RollingUpdate
yaml
---kube-flannel-cfg
apiVersion: v1
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
kind: ConfigMap
metadata:
labels:
app: flannel
tier: node
name: kube-flannel-cfg
namespace: kube-system
yaml
---clusterrole-flannel
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
rules:
- apiGroups:
- extensions
resourceNames:
- psp.flannel.unprivileged
resources:
- podsecuritypolicies
verbs:
- use
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
yaml
---ClusterRoleBinding-flannel
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-flannel
yaml
--- serviceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-flannel
secrets:
- name: flannel-token-hclt9
yaml
---PodSecurityPolicy-flannel
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
allowPrivilegeEscalation: false
allowedCapabilities:
- NET_ADMIN
- NET_RAW
allowedHostPaths:
- pathPrefix: /etc/cni/net.d
- pathPrefix: /etc/kube-flannel
- pathPrefix: /run/flannel
defaultAllowPrivilegeEscalation: false
fsGroup:
rule: RunAsAny
hostNetwork: true
hostPorts:
- max: 65535
min: 0
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- configMap
- secret
- emptyDir
- hostPath
总结
本文主要分析了 Flannel 这一流行的容器网络方案的 UDP 和 VXLAN 两种主要工作模式的原理和实现。从容器网络通信的挑战出发,介绍了 Flannel 的设计思路和核心概念,包括子网分配、封包解包过程、ARP 记录管理等。通过对比 UDP 和 VXLAN 模式的性能和复杂度,理解了 VXLAN 模式在实际生产环境中更受欢迎的原因以及UDP没落的原因