OS + Docker network calico / weave / flannel
s
calico,weave,flannel等开源网络组件
Docker Weave Network
https://www.cnblogs.com/xiangsikai/p/9900250.html
Weave Network:属于第三方网络项目。
Weave在Docker主机之间实现Overlay网络,使用业界标准VXLAN封装,基于UDP传输,也可以加密传输。
Weave Net创建一个连接多个Docker主机的虚拟网络,类似于一个以太网交换机,所有的容器都连接到这上面,互相通信。
Weave Net由多个peer组成,Weave路由器运行不同Docker主机上,是一个用户空间的进程;每个peer都有一个名称,重启保持不变。它们通过TCP连接彼此,建立后交换拓扑信息。
Weave Net可以在具有编号拓扑的部分连接的网络中路由数据包。
例如:在下面网络中,peer1直接连接2和3,但是如果1需要发送数据包到4和5,则必须先将其发送到peer3。
Weave Net中的”fast data path”使用Linux内核的OpenvSwich datapath模块。该模块使Weave Net路由器能够告知内核如何处理数据包。
OpenvSwich datapath和VXLAN功能在Linux内核版本3.12+才支持,如果内核不支持,则Weave Net使用”user mode”数据包路径。Weave Net会自动选择两台主机之间最快的路径传输数据,提供近原生吞吐量和延迟。
特点:
# IP地址管理(IPAM) Weave自动为容器分配唯一的IP地址。可通过weave ps查看 # 命名和发现 命名的容器自动会注册到Weave DNS中,并可以通过容器名称访问。 注:weave自己维护了一个微型的dns服务器。可以实现主机名通信。 # 负载均衡 允许注册多个相同名称的容器,Weave DNS随机为每个请求返回地址,提供基本的负载均衡功能。 注:如果访问容器名相同,则会自动轮询访问该容器,实现负载均衡。 # 手动指定IP地址 docker run –it –e WEAVE_CIDR=10.32.0.100/24 busybox # 动态拓扑 可以在不停止或重新配置剩余Docker主机的情况下添加主机到Weave网络中或从Weave网络中删除 # 容错 weave peer不断交换拓扑信息,监视和建立与其他peer的网络连接。如果有主机或网络出现故障,Weave会绕过这个主机,保证两边容器可以继续通信,当恢复时,恢复完全连接
Docker Weave 工作原理
- 网卡设备
- Container eth0:eth0是容器主机的默认网络,主要提供容器访问外网所提供的服务,走的默认docker网络架构,只不过他创建了docker_gwbridge这个网桥。
- docker_gwbridge:docker_gwbridge是容器所创建的网桥它替代了docker0的服务。
- Contailner ethwe:它是veth pair虚拟设备对,与其他容器通信的网络虚拟网卡。
- vethwe-bridge:是ethwe设备对创建的weave网桥。网桥内分配的具体的IP与网关。
- weave:weave网桥,通过route路由表找到目标,通过端口将数据包转发到对端端口节点。
- eth0:真机网卡与外界网卡连接得真机网卡,它用来转发,容器VXLAN与NAT两种网卡类型的数据包到指定的对端节点。
- 注:weave会将相邻的节点互相学习,通过route路由表进行相互通信,并通过单独的端口发送数据。类似于静态路由。
Contailner ethwe 发送数据包到对端容器通信
1、ethwe 会将数据包发送给vethwe-bridge网桥。
2、vethwe-bridge接收到数据包后由weave去处理这个数据,通过UDP6783数据端口依照weave的路由表转发到下一路由节点。
3、如果该节点就是目的地,本地weave会把信息转发到内核的TCP协议站,再转发到目的节点。
Calico 网络原理
https://www.cnblogs.com/jokerjason/p/13206594.html
一、Calico基本介绍
Calico是一个纯三层的协议,为OpenStack虚机和Docker容器提供多主机间通信。Calico不使用重叠网络比如flannel和libnetwork重叠网络驱动,它是一个纯三层的方法,使用虚拟路由代替虚拟交换,每一台虚拟路由通过BGP协议传播可达信息(路由)到剩余数据中心。
二、Calico结构组成
Calico不使用重叠网络比如flannel和libnetwork重叠网络驱动,它是一个纯三层的方法,使用虚拟路由代替虚拟交换,每一台虚拟路由通过BGP协议传播可达信息(路由)到剩余数据中心;Calico在每一个计算节点利用Linux Kernel实现了一个高效的vRouter来负责数据转发,而每个vRouter通过BGP协议负责把自己上运行的workload的路由信息像整个Calico网络内传播——小规模部署可以直接互联,大规模下可通过指定的BGP route reflector来完成。
结合上面这张图,我们来过一遍 Calico 的核心组件:
Felix: Calico agent,跑在每台需要运行 workload 的节点上,主要负责配置路由及 ACLs 等信息来确保 endpoint 的连通状态;
etcd: 分布式键值存储,主要负责网络元数据一致性,确保 Calico 网络状态的准确性;
BGPClient(BIRD): 主要负责把 Felix 写入 kernel 的路由信息分发到当前 Calico 网络,确保 workload 间的通信的有效性;
BGP Route Reflector(BIRD): 大规模部署时使用,摒弃所有节点互联的 mesh 模式,通过一个或者多个BGP Route Reflector来完成集中式的路由分发;
通过将整个互联网的可扩展 IP 网络原则压缩到数据中心级别,Calico 在每一个计算节点利用Linux kernel实现了一个高效的vRouter来负责数据转发而每个vRouter通过BGP
协议负责把自己上运行的 workload 的路由信息像整个 Calico 网络内传播 - 小规模部署可以直接互联,大规模下可通过指定的BGP route reflector 来完成。这样保证最终所有的 workload 之间的数据流量都是通过 IP 包的方式完成互联的。
三、Calico 工作原理
Calico把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以Calico方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。
对于控制平面,它每个节点上会运行两个主要的程序,一个是Felix,它会监听ECTD中心的存储,从它获取事件,比如说用户在这台机器上加了一个IP,或者是分配了一个容器等。接着会在这台机器上创建出一个容器,并将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。绿色部分是一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,你们路由的时候得到这里来。
由于Calico是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率可能是所有方案中最高的,因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。
四、Calico网络方式(两种)
IPIP
从字面来理解,就是把一个IP数据包又套在一个IP包里,即把 IP 层封装到 IP 层的一个 tunnel,看起来似乎是浪费,实则不然。它的作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需 IP,而这个 ipip 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来。ipip 的源代码在内核 net/ipv4/ipip.c 中可以找到。
BGP
边界网关协议(Border Gateway Protocol, BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或‘前缀’表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。BGP,通俗的讲就是讲接入到机房的多条线路(如电信、联通、移动等)融合为一体,实现多线单IP,BGP 机房的优点:服务器只需要设置一个IP地址,最佳访问路由是由网络上的骨干路由器根据路由跳数与其它技术指标来确定的,不会占用服务器的任何系统。
五、Calico网络通信模型
calico是纯三层的SDN 实现,它基于BPG 协议和Linux自身的路由转发机制,不依赖特殊硬件,容器通信也不依赖iptables NAT或Tunnel 等技术。
能够方便的部署在物理服务器、虚拟机(如 OpenStack)或者容器环境下。同时calico自带的基于iptables的ACL管理组件非常灵活,能够满足比较复杂的安全隔离需求。
在主机网络拓扑的组织上,calico的理念与weave类似,都是在主机上启动虚拟机路由器,将每个主机作为路由器使用,组成互联互通的网络拓扑。当安装了calico的主机组成集群后,其拓扑如下图所示:
每个主机上都部署了calico/node作为虚拟路由器,并且可以通过calico将宿主机组织成任意的拓扑集群。当集群中的容器需要与外界通信时,就可以通过BGP协议将网关物理路由器加入到集群中,使外界可以直接访问容器IP,而不需要做任何NAT之类的复杂操作。
当容器通过calico进行跨主机通信时,其网络通信模型如下图所示:
从上图可以看出,当容器创建时,calico为容器生成veth pair,一端作为容器网卡加入到容器的网络命名空间,并设置IP和掩码,一端直接暴露在宿主机上,并通过设置路由规则,将容器IP暴露到宿主机的通信路由上。于此同时,calico为每个主机分配了一段子网作为容器可分配的IP范围,这样就可以根据子网的CIDR为每个主机生成比较固定的路由规则。
当容器需要跨主机通信时,主要经过下面的简单步骤:
容器流量通过veth pair到达宿主机的网络命名空间上。
根据容器要访问的IP所在的子网CIDR和主机上的路由规则,找到下一跳要到达的宿主机IP。
流量到达下一跳的宿主机后,根据当前宿主机上的路由规则,直接到达对端容器的veth pair插在宿主机的一端,最终进入容器。
从上面的通信过程来看,跨主机通信时,整个通信路径完全没有使用NAT或者UDP封装,性能上的损耗确实比较低。但正式由于calico的通信机制是完全基于三层的,这种机制也带来了一些缺陷,例如:
calico目前只支持TCP、UDP、ICMP、ICMPv6协议,如果使用其他四层协议(例如NetBIOS协议),建议使用weave、原生overlay等其他overlay网络实现。
基于三层实现通信,在二层上没有任何加密包装,因此只能在私有的可靠网络上使用。
流量隔离基于iptables实现,并且从etcd中获取需要生成的隔离规则,有一些性能上的隐患。
深入理解flannel
https://www.cnblogs.com/YaoDD/p/7681811.html
1 概述
根据官网的描述,flannel是一个专为kubernetes定制的三层网络解决方案。它主要用于解决容器的跨主机通信问题。首先我们来简单看一下,它是如何工作的。
首先,flannel会利用Kubernetes API或者etcd用于存储整个集群的网络配置,其中最主要的内容为设置集群的网络地址空间,例如,设定整个集群内所有容器的IP都取自网段“10.1.0.0/16”。接着,flannel会在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。然后,flanneld再将本主机获取的subnet以及用于主机间通信的Public IP,同样通过kubernetes API或者etcd存储起来。最后,flannel利用各种backend mechanism,例如udp,vxlan等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。下面,我们以一个具体的例子来描述在flannel中,跨主机的容器间通信是如何进行的。
如下图所示,集群范围内的网络地址空间为10.1.0.0/16,Machine A获取的subnet为10.1.15.0/24,并且其中的两个容器的IP分别为10.1.15.2/24和10.1.15.3/24,两者都在10.1.15.0/24这一子网范围内,对于下方的Machine B同理。
现在,我们来简单看一下,如果上方Machine A中IP地址为10.1.15.2/24的容器要与下方Machine B中IP地址为10.1.16.2/24的容器进行通信,封包是如何进行转发的。从上文可知,每个主机的flanneld会将自己与所获取subnet的关联信息存入etcd中,例如,subnet 10.1.15.0/24所在主机可通过IP 192.168.0.100访问,subnet 10.1.16.0/24可通过IP 192.168.0.200访问。反之,每台主机上的flanneld通过监听etcd,也能够知道其他的subnet与哪些主机相关联。如下图,Machine A上的flanneld通过监听etcd已经知道subnet 10.1.16.0/24所在的主机可以通过Public 192.168.0.200访问,而且熟悉docker桥接模式的同学肯定知道,目的地址为10.1.16.2/24的封包一旦到达Machine B,就能通过cni0网桥转发到相应的pod,从而达到跨宿主机通信的目的。
因此,flanneld只要想办法将封包从Machine A转发到Machine B就OK了,而上文中的backend就是用于完成这一任务。不过,达到这个目的的方法是多种多样的,所以我们也就有了很多种backend。在这里我们举例介绍的是最简单的一种方式hostgw:因为Machine A和Machine B处于同一个子网内,它们原本就能直接互相访问。因此最简单的方法是:在Machine A中的容器要访问Machine B的容器时,我们可以将Machine B看成是网关,当有封包的目的地址在subnet 10.1.16.0/24范围内时,就将其直接转发至B即可。而这通过下图中那条红色标记的路由就能完成,对于Machine B同理可得。由此,在满足仍有subnet可以分配的条件下,我们可以将上述方法扩展到任意数目位于同一子网内的主机。而任意主机如果想要访问主机X中subnet为S的容器,只要在本主机上添加一条目的地址为R,网关为X的路由即可。
下面,我将以问题驱动的方式,来详细分析flannel是如何运作的
2 节点初始化
首先,我们最感兴趣的是,当一个新的节点加入集群时,它是如何初始化的。对此,我们可能会有以下几个疑问:
1) 若主机有多张网卡和多个IP,如何选择其中的一张网卡和一个IP用于集群主机间的通信
2) 主机如何获取属于自己的subnet并维护
3) 我们如何在集群中有新的节点加入时,获取对应的subnet和Public IP,并通过配置backend进行访问
2.1 网卡及对外IP选择
对于第一个问题,事实上我们可以在flanneld的启动参数中通过"--iface"或者"--iface-regex"进行指定。其中"--iface"的内容可以是完整的网卡名或IP地址,而"--iface-regex"则是用正则表达式表示的网卡名或IP地址,并且两个参数都能指定多个实例。flannel将以如下的优先级顺序来选取:
1) 如果"--iface"和"----iface-regex"都未指定时,则直接选取默认路由所使用的输出网卡
2) 如果"--iface"参数不为空,则依次遍历其中的各个实例,直到找到和该网卡名或IP匹配的实例为止
3) 如果"--iface-regex"参数不为空,操作方式和2)相同,唯一不同的是使用正则表达式去匹配
最后,对于集群间交互的Public IP,我们同样可以通过启动参数"--public-ip"进行指定。否则,将使用上文中获取的网卡的IP作为Public IP。
2.2 获取subnet
在获取subnet之前,我们首先要创建一个SubnetManager,它在具体的代码实现中,表现为一个接口,如下所示:
1
2
3
4
5
6
7
8
9
|
type Manager interface { GetNetworkConfig(ctx context.Context) (*Config, error) AcquireLease(ctx context.Context, attrs *LeaseAttrs) (*Lease, error) RenewLease(ctx context.Context, lease *Lease) error WatchLease(ctx context.Context, sn ip.IP4Net, cursor interface {}) (LeaseWatchResult, error) WatchLeases(ctx context.Context, cursor interface {}) (LeaseWatchResult, error) Name() string } |
从接口中各个函数的名字,我们大概就能猜出SubnetManager的作用是什么了。但是,为什么获取subnet的函数叫AcquireLease,而不叫AcquireSubnet呢?实际上,每台主机都是租借了一个subnet,如果到了一定时间不进行更新,那么该subnet就会过期从而重新分配给其他的主机,即主机和subnet的关联信息会从etcd中消失(在本文中我们将默认选择etcd作为SubnetManager的后端存储)。因此,lease就是一条subnet和所属主机的关联信息,并且具有时效性,需要定期更新。下面我们来看看,每台主机都是如何获取lease的:
1) 首先,我们调用GetNetworkConfig(),它会访问etcd获取集群网络配置并封装在结构Config中返回,Config结构如下所示。其中的Network字段对应的集群网络地址空间是在flannel启动前,必须写入etcd中的,例如"10.1.0.0/16"。
1
2
3
4
5
6
7
8
|
type Config struct { Network ip.IP4Net SubnetMin ip.IP4 SubnetMax ip.IP4 SubnetLen uint BackendType string `json: "-" ` Backend json.RawMessage `json: ",omitempty" ` } |
对于其他字段的含义及默认值如下:
(1) SubnetLen表示每个主机分配的subnet大小,我们可以在初始化时对其指定,否则使用默认配置。在默认配置的情况下,如果集群的网络地址空间大于/24,则SubnetLen配置为24,否则它比集群网络地址空间小1,例如集群的大小为/25,则SubnetLen的大小为/26
(2) SubnetMin是集群网络地址空间中最小的可分配的subnet,可以手动指定,否则默认配置为集群网络地址空间中第一个可分配的subnet。例如对于"10.1.0.0/16",当SubnetLen为24时,第一个可分配的subnet为"10.1.1.0/24"。
(3) SubnetMax表示最大可分配的subnet,对于"10.1.0.0/16",当subnetLen为24时,SubnetMax为"10.1.255.0/24"
(4) BackendType为使用的backend的类型,如未指定,则默认为“udp”
(5) Backend中会包含backend的附加信息,例如backend为vxlan时,其中会存储vtep设备的mac地址
2) 在获取了集群的网络配置之后,接下来我们就调用SubnetManager中的AcquireLease()获取本主机的subnet。其中的参数类型LeaseAttrs如下所示:
1
2
3
4
5
|
type LeaseAttrs struct { PublicIP ip.IP4 BackendType string `json: ",omitempty" ` BackendData json.RawMessage `json: ",omitempty" ` } |
显然,其中最重要的字段就是PublicIP,它实质上是标识了一台主机。在获取subnet之前,我们先要从etcd中获取当前所有已经存在的lease信息----leases,以备后用。下面我们将对不同情况下lease的获取进行讨论:
(1) 事实上,这可能并不是我们第一次在这台机器上启动flannel,因此,很有可能,在此之前,这台机器已经获取了lease。已知一台主机其实是由它的Public IP标识的,所以我们可以用Public IP作为关键字匹配leases中所有lease的Public IP。若匹配成功,则检查相应的lease是否和当前的集群网络配置兼容:检查的内容包括IP是否落在SubnetMin和SubnetMax内,以及subnet大小是否和SubnetLen相等。若兼容,则用新的LeaseAttrs和ttl更新该lease,表示成功获取本机的lease,否则只能将该lease删除。
(2) 当初始化SubnetManager时,会先试图解析之前flannel获取了lease后留下的配置文件(该文件的创建,会在下文描述),从中读取出之前获取的subnet。如果读取到的subnet不为空,则同样利用该subnet去匹配leases中所有lease的subnet。若匹配成功,则同样检查lease是否和当前的集群网络配置兼容。若兼容则更新lease,表示成功获取本机的lease,否则将其删除。如果该subnet并不能从leases中找到,但是它和当前的集群网络配置兼容的话,可以直接将它和LeaseAttrs封装为lease,写入etcd。
(3) 若此时还未获取到lease,那么我们有必要自己创建一个新的了。创建的方法很简单,从SubnetMin遍历到SubnetMax,将其中和leases中已有的subnet都不重合者加入一个集合中。再从该集合随机选择一个,作为本主机的subnet即可。最后,将subnet和LeaseAttrs封装为一个lease写入etcd。由此,该主机获取了自己的subnet。
最后,我们将有关的集群网络和subnet的配置信息写入文件/run/flannel/subnet.env(可通过命令行参数"--subnet-file"手动指定)中,写入的信息如下所示,包括:集群网络地址空间FLANNEL_NETWORK,获取的子网信息FLANNEL_SUBNET等等
1
2
3
4
5
|
cat /var/run/flannel/subnet . env FLANNEL_NETWORK=10.1.0.0 /16 FLANNEL_SUBNET=10.1.16.1 /24 FLANNEL_MTU=1450 FLANNEL_IPMASQ= false |
2.3 维护subnet
当SubnetManager的后端存储使用的是etcd时,各个主机还需要对自己的lease进行维护,在租期即将到期时,需要对etcd中的lease进行更新,调用SubnetManager中的RenewLease()方法,防止它到期后被自动删除。另外,我们可以在flanneld的命令行启动参数中用"--subnet-lease-renew-margin"指定在租期到期前多久进行更新。默认值为1小时,即每23小时更新一次lease,重新获取一次24小时的租期。
2.4 发现新节点
现在,初始化已经完成了,我们需要面对如下两个问题:
1、当本主机的flanneld启动时,如果集群中已经存在了其他主机,我们如何通过backend进行配置,使得封包能够到达它们
2、如果之后集群中又添加了新的主机,我们如何获取这一事件,并通过backend对配置进行调整,对于删除主机这类事件同理
当然上述两个问题,都是通过etcd解决的。backend会一边通过上文中的WatchLeases()方法对etcd进行监听,从中获取各类事件,另一边会启动一个事件处理引擎,不断地对监听到的事件进行处理。对于问题1,我们首先要从etcd中获取当前所有的lease信息,并将其转化为一系列的event,将它交于事件处理引擎进行处理,从而让封包能够到达这些主机。对于问题2,直接对etcd中的事件进行监听,将获取的事件转换为事件处理引擎能够处理的形式,并进行处理即可。事件的类型也很简单,总共就只有EventAdded和EventRemoved两种,分别表示新增了lease以及一个lease过期。因为不同backend的配置方式是完全不同的,下面我们就将对各种backend的基本原理进行解析,并说明它们如何处理EventAdded和EventRemoved这两类事件。
3 backend原理解析
在本节中,我将对hostgw,udp和vxlan三种backend进行解析
3.1 hostgw
hostgw是最简单的backend,它的原理非常简单,直接添加路由,将目的主机当做网关,直接路由原始封包。例如,我们从etcd中监听到一个EventAdded事件:subnet为10.1.15.0/24被分配给主机Public IP 192.168.0.100,hostgw要做的工作非常简单,在本主机上添加一条目的地址为10.1.15.0/24,网关地址为192.168.0.100,输出设备为上文中选择的集群间交互的网卡即可。对于EventRemoved事件,删除对应的路由即可。
3.2 udp
我们知道当backend为hostgw时,主机之间传输的就是原始的容器网络封包,封包中的源IP地址和目的IP地址都为容器所有。这种方法有一定的限制,就是要求所有的主机都在一个子网内,即二层可达,否则就无法将目的主机当做网关,直接路由。
而udp类型backend的基本思想是:既然主机之间是可以相互通信的(并不要求主机在一个子网中),那么我们为什么不能将容器的网络封包作为负载数据在集群的主机之间进行传输呢?这就是所谓的overlay。具体过程如下所示:
当容器10.1.15.2/24要和容器10.1.20.2/24通信时,因为该封包的目的地不在本主机是subnet内,因此封包会首先通过网桥转发到主机中。最终在主机上经过路由匹配,进入网卡flannel0。需要注意的是flannel0是一个tun设备,它是一种工作在三层的虚拟网络设备,而flanneld是一个proxy,它会监听flannel0并转发流量。当封包进入flannel0时,flanneld就可以从flannel0中将封包读出,由于flannel0是三层设备,所以读出的封包仅仅包含IP层的报头及其负载。最后flanneld会将获取的封包作为负载数据,通过udp socket发往目的主机。同时,在目的主机的flanneld会监听Public IP所在的设备,从中读取udp封包的负载,并将其放入flannel0设备内。由此,容器网络封包到达目的主机,之后就可以通过网桥转发到目的容器了。
最后和hostgw不同的是,udp backend并不会将从etcd中监听到的事件中包含的lease信息作为路由写入主机中。每当收到一个EventAdded事件,flanneld都会将其中的subnet和Public IP保存在一个数组中,用于转发封包时进行查询,找到目的主机的Public IP作为udp封包的目的地址。
3.3 vxlan
首先,我们对vxlan的基本原理进行简单的叙述。从下图所示的封包结构来看,vxlan和上文提到的udp backend的封包结构是非常类似的,不同之处是多了一个vxlan header,以及原始报文中多了个二层的报头。
下面让我们来看看,当有一个EventAdded到来时,flanneld如何进行配置的,以及封包是如何在flannel网络中流动的。
如上图所示,当主机B加入flannel网络时,和其他所有backend一样,它会将自己的subnet 10.1.16.0/24和Public IP 192.168.0.101写入etcd中,和其他backend不一样的是,它还会将vtep设备flannel.1的mac地址也写入etcd中。
之后,主机A会得到EventAdded事件,并从中获取上文中B添加至etcd的各种信息。这个时候,它会在本机上添加三条信息:
1) 路由信息:所有通往目的地址10.1.16.0/24的封包都通过vtep设备flannel.1设备发出,发往的网关地址为10.1.16.0,即主机B中的flannel.1设备。
2) fdb信息:MAC地址为MAC B的封包,都将通过vxlan首先发往目的地址192.168.0.101,即主机B
3) arp信息:网关地址10.1.16.0的地址为MAC B
现在有一个容器网络封包要从A发往容器B,和其他backend中的场景一样,封包首先通过网桥转发到主机A中。此时通过,查找路由表,该封包应当通过设备flannel.1发往网关10.1.16.0。通过进一步查找arp表,我们知道目的地址10.1.16.0的mac地址为MAC B。到现在为止,vxlan负载部分的数据已经封装完成。由于flannel.1是vtep设备,会对通过它发出的数据进行vxlan封装(这一步是由内核完成的,相当于udp backend中的proxy),那么该vxlan封包外层的目的地址IP地址该如何获取呢?事实上,对于目的mac地址为MAC B的封包,通过查询fdb,我们就能知道目的主机的IP地址为192.168.0.101。
最后,封包到达主机B的eth0,通过内核的vxlan模块解包,容器数据封包将到达vxlan设备flannel.1,封包的目的以太网地址和flannel.1的以太网地址相等,三层封包最终将进入主机B并通过路由转发达到目的容器。
事实上,flannel只使用了vxlan的部分功能,由于VNI被固定为1,本质上工作方式和udp backend是类似的,区别无非是将udp的proxy换成了内核中的vxlan处理模块。而原始负载由三层扩展到了二层,但是这对三层网络方案flannel是没有意义的,这么也做仅仅只是为了适配vxlan的模型。vxlan详细的原理参见文后的参考文献,其中的分析更为具体,也更易理解。
4 总结
总的来说,flannel更像是经典的桥接模式的扩展。我们知道,在桥接模式中,每台主机的容器都将使用一个默认的网段,容器与容器之间,主机与容器之间都能互相通信。要是,我们能手动配置每台主机的网段,使它们互不冲突。接着再想点办法,将目的地址为非本机容器的流量送到相应主机:如果集群的主机都在一个子网内,就搞一条路由转发过去;若是不在一个子网内,就搞一条隧道转发过去。这样以来,容器的跨网络通信问题不就解决了么?而flannel做的,其实就是将这些工作自动化了而已。
参考文献
1、flannel源码:https://github.com/coreos/flannel
2、《vxlan协议原理解析》:http://cizixs.com/2017/09/25/vxlan-protocol-introduction
3、《linux上实现vxlan网络》:http://cizixs.com/2017/09/28/linux-vxlan
4、《VxLAN和VTEP》:http://maoxiaomeng.com/2017/07/31/vxlan%E5%92%8Cvtep/
end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?