Flannel 介绍及使用场景

简介

Flannel是由go语言开发,是一种基于 Overlay 网络的跨主机容器网络解决方案,也就是将TCP数据包封装在另一种网络包里面进行路由转发和通信,Flannel 是 CoreOS 开发,专门用于docker多主机互联的一个工具,简单来说,它的功能是让集群中的不同节点主机创建的容器都具有全局唯一的虚拟IP地址。

在默认的Docker配置中,每个节点上的Docker服务会分别独立负责自己所在节点容器的IP地址分配,这样就导致一个问题,不同节点上容器获得的IP地址可能一样,容器之间无法直接通信,需要借助nat桥接的方式来进行通信。

Flannel 的设计目的就是为了集群中所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得同一个子网内且不重复的IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。

Flannel 实质上是一种"覆盖网络(overlay network)",也就是将 TCP 数据包报封装在另一种网络包里面进行路由转发和通信,目前已经支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等数据转发方式,默认的节点间数据通信方式是UDP 转发。

docker 默认跨主机通信

物理主机在同一个内网中,两台独立的主机内,容器如何通信。假如 node2 中的 sever 容器作为web服务器,这个web仅仅内部使用,如何才能让node1中的client容器访问到?

  • 首先 server容器是服务端,需要暴露80端口,在docker中,暴露80端口供其他节点访问需要做 dnat规则,将容器的端口映射到宿主机上的某端口;
  • client容器去访问server容器时,必须先到达 node2 节点的某个映射好的端口,而 client访问其他节点在docker中需要做snat规则。

通过上面的描述得知,docker不同宿主机之间网络通信,一次数据传输需要做两次 nat 规则转换,而且是内网访问,仅仅因为这两个容器在不同的主机上所造成的。而flannel的出现则是解决了这一痛点。

docker+flannel 通信过程

image-20210114154720661

  • 容器发送数据,首先送到容器网关 docker0;
  • 到达docker0以后,根据路由规则将数据送至 flannel0;
  • flannel0 查看数据包的目的IP,flanneld从etcd维护的对应表中获取到对端设备的必要信息,封装数据包;
  • flannel0将数据包发送到对端设备。对端节点的网卡接收到数据包,发现数据包为 overlay数据包,解开外层封装,并发送内层封装给 flannel0 ;
  • flannel 设备查看数据包,根据路由表匹配,将数据发送给docker0
  • docker0匹配路由表,将数据发送给网桥上对应的端口

上面简短的介绍了 docker+flannel 的工作机制,后面会详细解读通信过程。通过这个过程发现 跨主机的内网容器通信没有一次nat操作。


docker nat网桥回顾:

  1. 当容器访问外部网络时,docker0作为容器的默认网关,数据先送达到docker0;

  2. 当数据送达到docker0,也就到达宿主机,然后会查询宿主机的路由表,发现包应该从宿主机的eth0发往宿主机的网关,这里从docker0 -> eth0 必须宿主机要打开核心转发功能(net.ipv4.ip_forward = 1)

  3. 数据包到达eth0以后,这里的iptables规则就开始操作了,会对数据包做源地址转换,将源地址转为eth0的地址。这样,外界看来,这个包就是从eth0的地址发出来的,Docker容器不可见。


Flannel 特点

  1. 使集群中的不同Node节点创建 docker 容器都具有全集群唯一的虚拟IP地址;
  2. 建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖网络是建立在另一个三层网络之上并由基础设施支持的虚拟网络。覆盖网络通过将一个分组封装到另一个分组内来将网络服务与底层基础设施分离。在将封装的数据包转发到端点后,将其解封装;
  3. 创建一个新的虚拟网卡flannel0 接收 docker0网桥的数据,通过维护路由表,对接收的数据进行封包和转发(vxlan);
  4. etcd 保证了所有 node 节点上 flanned 所看到的配置是一致的。同时每个node上的flanned监听 etcd 上数据变化,实时感知集群中node的变化。
  5. flannel默认的后端封装为udp,常见的有:udp、vxlan、host-gw。

Docker+Flannel 部署过程

环境介绍

系统及版本

系统 docker版本
Centos7.x 20.10.2-3.el7

主机信息

IP地址 主机名
10.0.30.101 master
10.0.30.102 node1

主机初始化

  1. 关闭selinux
  2. 关闭防火墙
  3. ntp时间服务
  4. 主机名修改和hosts定义
  5. 配置好yum源

安装

这里需要配置好 epel源、docker-ce源,自行阿里云源都可使用。

master 节点操作:

# yum install flannel etcd docker-ce -y

node 节点操作:

# yum install flannel docker-ce -y

配置

注意:以下仅仅在master节点操作

etcd配置

注意:这里etcd不做集群,这里只是单点测试。

[root@master ~]# cd /etc/etcd/
[root@master /etc/etcd]# cp -a etcd.conf{,.bak}
[root@master /etc/etcd]# vim etcd.conf
[root@master /etc/etcd]# egrep -v "^#|^$" etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd" #数据存放位置
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001" #监听其他etcd实列地址
ETCD_NAME="master" #节点名称
ETCD_ADVERTISE_CLIENT_URLS="http://master:2379,http://master:4001" #通知客户端地址

启动 etcd

[root@master /etc/etcd]# systemctl enable etcd ; systemctl start etcd 
Created symlink from /etc/systemd/system/multi-user.target.wants/etcd.service to /usr/lib/systemd/system/etcd.service.
[root@master /etc/etcd]# netstat -ntplu | egrep etcd 
tcp        0      0 127.0.0.1:2380          0.0.0.0:*               LISTEN      1739/etcd
tcp6       0      0 :::4001                 :::*                    LISTEN      1739/etcd
tcp6       0      0 :::2379                 :::*                    LISTEN      1739/etcd 

查看 etcd 状态:

[root@master ~]# etcdctl member list
8e9e05c52164694d: name=master peerURLs=http://localhost:2380 clientURLs=http://master:2379,http://master:4001 isLeader=true

flannel 配置

编辑配置文件 /etc/sysconfig/flannel

[root@master ~]# cd /etc/sysconfig/
[root@master /etc/sysconfig]# cp -a flanneld{,.bak}
[root@master /etc/sysconfig]# egrep -i ^[a-z] flanneld
FLANNEL_ETCD_ENDPOINTS="http://10.0.30.101:2379"	#etcd集群的地址,包括各个成员
FLANNEL_ETCD_PREFIX="/atomic.io/network" #etcd集群的地址,包括各个成员
FLANNEL_OPTIONS="--logtostderr=false --log_dir=/var/log/ --etcd-endpoints=http://10.0.30.101:2379 --iface=eth0" #定义了日志级别,路径,以及etcd集群地址和绑定的屋里网卡信息

重点这里的 FLANNEL_ETCD_PREFIX="/atomic.io/network" 这个路径在 etcd中也会用到,两者必须保持一致。

在启动 flannel之前需要向 etcd内写入子网信息

注意:这里采用的后端封包为:vxlan,flannel默认为:udp

[root@master ~]# etcdctl mk /atomic.io/network/config '{"Network":"10.10.0.0/16","SubnetMin":"10.10.1.0","SubnetMax":"10.10.254.0","Backend": {"Type": "vxlan"}}'
{"Network":"10.10.0.0/16","SubnetMin":"10.10.1.0","SubnetMax":"10.10.254.0","Backend": {"Type": "vxlan"}}

子网范围为 10.10.0.0/16,最小子网开始为10.10.1.0 最大子网结束为10.10.254.0,至此自定义子网信息我们已经写入完毕。

启动 flannel

[root@master ~]# systemctl enable flanneld; systemctl start flanneld
[root@master ~]# netstat -ntplu | egrep flanneld
udp        0      0 10.0.30.101:8285        0.0.0.0:*                           14013/flanneld

docker 配置

确保 etcd 和 flanneld 都启动正常后,开始配置docker

通过 systemctl show docker 可以看到 flannel为docker新增了一个配置文件

[root@master ~]# systemctl show docker | egrep DropInPaths
DropInPaths=/usr/lib/systemd/system/docker.service.d/flannel.conf

## 查看配置文件
[root@master ~]# cat /usr/lib/systemd/system/docker.service.d/flannel.conf
[Service]
EnvironmentFile=-/run/flannel/docker	# 环境变量文件
[root@master ~]# cat /run/flannel/docker # 这里定义了 DOCKER_NETWORK_OPTIONS
DOCKER_OPT_BIP="--bip=10.10.1.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1472"
DOCKER_NETWORK_OPTIONS=" --bip=10.10.1.1/24 --ip-masq=true --mtu=1472"

修改 docker.service

[root@master ~]# vim /lib/systemd/system/docker.service
...
#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS
...

启动docker服务

[root@master ~]# systemctl daemon-reload
[root@master ~]# systemctl restart docker

查看验证

[root@master /etc/sysconfig]# ifconfig docker; ifconfig flannel.1
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 10.10.9.1  netmask 255.255.255.0  broadcast 10.10.9.255
        ether 02:42:95:09:b2:1b  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.10.9.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::c419:adff:fea8:91aa  prefixlen 64  scopeid 0x20<link>
        ether c6:19:ad:a8:91:aa  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 8 overruns 0  carrier 0  collisions 0

确保 docker0 和 flannel0 在一同子网内。

master 节点配置完成

注意:以下仅仅在node节点操作

配置 flannel

详细配置和 master 节点一致。

[root@node1 ~]# cd /etc/sysconfig/
[root@node1 /etc/sysconfig]# cp -a flanneld{,.bak}

[root@node1 /etc/sysconfig]# egrep -i ^[a-z] flanneld
FLANNEL_ETCD_ENDPOINTS="http://10.0.30.101:2379"
FLANNEL_ETCD_PREFIX="/atomic.io/network"
FLANNEL_OPTIONS="--logtostderr=false --log_dir=/var/log/ --etcd-endpoints=http://10.0.30.101:2379 --iface=eth0"

启动flanneld

[root@node1 ~]# systemctl enable flanneld ; systemctl start flanneld

配置docker

修改启动脚本:

[root@node1 ~]# vim /lib/systemd/system/docker.service
...
#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS
...

启动docker服务

[root@node1 ~]# systemctl enable docker ; systemctl start docker

验证查看

[root@node1 ~]# ifconfig docker; ifconfig flannel.1
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 10.10.15.1  netmask 255.255.255.0  broadcast 10.10.15.255
        ether 02:42:6a:4d:77:a5  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.10.15.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::402e:a6ff:fefd:a792  prefixlen 64  scopeid 0x20<link>
        ether 42:2e:a6:fd:a7:92  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 7 overruns 0  carrier 0  collisions 0

到此, 两台主机的 docker+flannel 已经配置完成。

验证

首先两台主机相互 ping 对方的docker0 网桥所在的地址。

节点IP docker0 地址
master(10.0.30.101) 10.10.9.1
node(10.0.30.102) 10.10.15.1

master 节点:

image-20210118112203514

node 节点:

image-20210118112307170

OK, 都可到达双方的docker0网桥。

需要特别关注的点,否则主机内容器无法通信

启动docker时,docker默认会生成很多 iptables 规则,docker也是利用这些iptables规则来做流量转发的,但是这里使用了 flannel 来为docker重新组网,所以docker生成的一些iptables 规则会阻碍 flannel 的正常使用,这里需要特别关注,否则主机内容器无法通信。

查看 iptables 规则:

image-20210115101829768

注意:这里的 FORWARD 链默认是 DROP 状态,上面讲 docker0 和 flannel0 通信时,需要打开转发功能。所以每个节点(包括master)都必须将 FORWARD链设置为 ACCEPT

每个物理节点上执行:

iptables -P FORWARD ACCEPT

接下来就可以在主机内创建容器,查看两台物理机之间容器是否可以直接通信

### 两台主机都创建容器
docker run --rm -it busybox:latest /bin/sh

master节点容器信息:

image-20210118134117056

node1节点容器信息:

image-20210118134210411

通过ping 测试网络:

image-20210118134351902

到这里,docker+flannel 的配置才算完成了,使用了 flannel 实现了跨主机的容器间直接通信。

Flannel 对网络要求的解决办法

互相不冲突的IP

  • flannel 利用 etcd 记录整个节点的网络配置信息,根据配置记录使用的网段;
  • flannel 在每个主机中运行 flanneld 作为 agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段Subnet,本主机内所有容器的IP地址都将从中分配。

master节点网络:

image-20210118134452970

node1节点网络:

image-20210118134522076

image-20210118134822781

在 flannel network中,每个容器都会被分配唯一的IP地址,且每个节点的subnet各不重叠,没有交集。

容器之间相互访问

  • flanneld 将本机获取的 subnet 以及用于主机间通信的Public IP通过 etcd存储起来,需要时发送给相应的模块;
  • flanneld利用各种backend mechanism ,例如:udp、vxlan等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。

Flannel 架构原理

flannel 架构图:

flannel

各个组件解释:

Docker0:网桥设备,每次创建一个容器都会创建一对 veth pair 。其中一端是 容器中的 eth0,另外一端在docker0网桥中的端口(网卡)。容器中从网卡 eth0 发出的流量都会发送到 docker0网桥设备的端口(网卡)上。

image-20210118140138804

docker0 设备获得的ip地址是该节点subnet的第一个IP地址。

Flannel.1:overlay网络的设备,用来进行 udp 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端。

image-20210118140227160

Flanneld:flannel在每个主机中运行 flanneld作为 agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听etcd,为 flannel.1 设备提供封装数据时必要的mac、ip等网络数据信息。

通信流程

容器到docker0

同一物理网络中两台宿主机内的容器能相互ping通

image-20210118140638767

ping 包的 dst ip为 10.10.15.2,根据容器内路由表,凡是 10.10.9.0/24 都转发到 10.10.9.1

image-20210118141253464

10.10.9.1 为 宿主机上 docker0 的地址:

image-20210118141413477

Docker0 到 flannel.1

当 icmp 包达到 docker0之后,flannel.1 发现 dst 为 10.10.15.2,docker0根据宿主机路由表来查找匹配项

image-20210118141705236

根据上面路由项,凡是 10.10.0.0/16 的访问都送达到 flannel.1 上。

Flannel.1

flannel.1 为 vxlan 设备,当数据包来到 flannel.1 时,需要将数据包封装起来。此时的 dst ip 为 10.10.15.2,src ip 为 10.10.9.2。数据包继续封装需要知道 10.10.15.2 ip地址对应的mac地址。此时,flannel.1 不会发送arp请求去获取 10.10.15.2 的mac地址,而是由 Linux Kernel 将一个 “L3 Miss” 事件请求发送给用户空间 flanneld程序。Flanneld程序收到内核的请求事件之后,从 etcd 查找能够匹配该地址的子网的 flannel.1 设备的mac地址,即发往的容器所在宿主机中 flannel.1 设备的mac地址。Flannel在为node节点分配ip网段时记录了所有的网段和mac等信息,所以能够知道。交互流程如下:

image-20210118142651424

flanneld 将查询到的信息放入 arp cache 表中:

image-20210118142804126

到这里,vxlan的内层数据包就完成了封装。格式是这样:

image-20210118143607742

简单总结这个流程:

  1. 数据包到达 flannel.1 ,通过查找路由表,知道数据包要通过 flannel.1 发往 10.10.15.0
  2. 通过 arp cache 表,知道了目的ip 10.10.15.0 的mac地址。

kernel 需要查看node上的fdb(forwarding database)以获得内层封包中目的 vtep设备所在的node地址。因为已经从 arp table 中查到目的设备mac地址为 42:2e:a6:fd:a7:92,同时在fdb中存在该mac地址对应的node节点的ip地址。如果fdb中没有这个信息,那么kernel会向用户空间的flanneld程序发起 “L2 MISS” 事件。flanneld 收到该事件后,会查询etcd,获取 vtep设备对应的node的 “Public IP” ,并将信息注册到 fdb中。

当内核获得了发往机器的ip地址后,arp得到mac地址,之后就能完成vxlan的外层封装。

image-20210118144338769

node1 的 flannel.1

node1宿主机收到eth0网卡接收到vxlan设备包,kernel 将识别出这是一个vxlan包,将包拆开之后转给节点上的flannel.1设备。这样数据包就从发送节点到达目的节点,flannel.1 设备将接收到一个如下的数据包:

image-20210118144559737

目的地址为 10.10.15.2 ,flannel.1 查询路由表,根据路由表完成转发

image-20210118144951640

flannel.1 将去往 10.10.15.0/24 的流量转发到 docker0上。

docker0 到 容器

docker0 是一个网桥设备,当docker0拿到数据包之后,通过 veth pair,将数据包发送给容器。查看node节点中的网桥。

image-20210118145240422

在node节点上通过arp解析可以开出,10.10.15.2 的mac地址为:02:42:0a:0a:0f:02

image-20210118145347499

该mac为node节点内容器eth0的mac地址:

image-20210118150242707

同时通过veth pair 的配对关系可以看出,容器中的 eth0 是 veth pair 的一端,另一端在node节点上,对应的网卡是 veth7218a86@if5。所以,在 docker0网桥上挂载容器的 veth pair 为 veth7218a86,即:

image-20210118150546382

veth7218a86@if5 和 veth7218a86 组成的一对 veth pair 。其效果相当于一根网线的两头,一头插在docker0这个交换机上,一头插在 容器这个小型电脑上。所以简单总结docker0转发流量的原理:

  1. 首先通过 arp 查找出 ip地址对应的mac地址;
  2. 将流量转发给mac地址所在 eth0网的对应 veth pair 端口;
  3. veth pair端口接收流量,直接将流量注入到 容器的 eth0网卡上。

Flannel 不同后端的封装

Flannel 项目本身只是一个框架,真正为用户提供容器网络功能的是后端实现,常见的有三种:

  1. UDP
  2. VXLAN
  3. host-gw

flannel会在宿主机上创建一个flannel0设备和创建一系列的路由表规则

flannel0设备介绍

负责在操作系统内核和用户应用程序之间传递IP包

  1. 内核态向用户态流动

    ​ 当操作系统将一个IP包发送给 flannel0设备后,flannel0设备就会把这个IP包交给创建这个设备的应用程序也就是 flanneld 进程

  2. 用户态向内核态流动

    ​ flanneld进程向flannel0设备发送了一个IP包,这个IP包就会在宿主机的网络栈中,然后根据宿主机上的路由表规则处理

UDP模式

UDP数据包封装模式:

容器进程 -> docker0 -> 内核  			    用户态到内核态
内核 -> flannel0 -> flanneld进程	 	 内核态到用户态
flanneld进程 -> 内核 -> eth0物理网卡发出	 用户态到内核态

由于UDP模式在内核态和用户态切换次数多,所以性能比较差。

系统级别编程的优化原则就是要减少用户态到内核态的切换次数,并且把核心的处理逻辑都放在内核态进行。

数据包流程分析:

UDP包的源地址和目的地址都是物理机地址,目标主机的flanneld进程在收到UDP包后,

  1. flanneld进程解析UDP包里的容器源IP和目标容器IP然后把解析后的包发给flannel0设备
  2. 操作系统自动接收flannel0中的数据,根据宿主机路由表转发到 docker0设备;
  3. docker0通过自身的端口找到目标容器

img

修改 flannel 为 udp模式

### 修改 backend type为: udp
etcdctl set /atomic.io/network/config '{"Network":"10.10.0.0/16","SubnetMin":"10.10.1.0","SubnetMax":"10.10.254.0","Backend": {"Type": "udp"}}'
### 修改成功后重启 flanneld 和 docker 服务

image-20210119094004141

udp模式原理

通过的是两个宿主机之间UDP通信,直接像目标主机发送UDP数据包即可(只要求宿主机之间可以相互访问即可),通过flannel维护了一个子网(容器网段)和宿主机IP的对应关系路由规则即可。

VxLAN 模式

VXLAN 本身是Linux内核自带的模块,完全可以在内核态实现封装和解封装的操作

VXLAN的设计思想:

​ 在现有的三层网络上构建一个虚拟的由内核VXLAN 模块负责维护的二层网络,使得连接在这个VXLAN二层网络中的虚拟机或者容器可以像一个LAN中自由通信,在一个LAN中的所有主机相当于连接在一个交换机上,通过交换机就可以找到目标主机。

VTEP虚拟隧道端点设备 在宿主机上创建的设备叫flannel.1

​ VTEP设备既有自己的IP地址 也有自己的MAC地址,VTEP设备和flannel0设备的作用相同 区别在于VTEP设备封装和解封装的对象是二层数据帧,flannel0设备封装和解封装的对象是UDP数据包。

通过每个宿主机上的VTEP设备组成了一个虚拟的二层网络 通过二层数据帧进行通信,二层通信需要封装源MAC地址和目标MAC地址,根据目标IP来查找目标的MAC地址 是通过ARP表来实现的。flanneld进程会在每个节点上自动维护VTEP的IP和MAC地址的对应关系。

[root@master ~]# ip neigh show dev flannel.1
10.10.15.1 lladdr 42:2e:a6:fd:a7:92 STALE
10.10.15.2 lladdr 42:2e:a6:fd:a7:92 REACHABLE
10.10.15.0 lladdr 42:2e:a6:fd:a7:92 STALE

flanneld进程

  1. 维护主机路由表,记录网段 网关 处理网卡设备信息,根据路由记录找到目标设备的IP地址;
  2. 维护VTEP设备的ARP表,记录VTEP的IP和MAC地址的对应关系,根据目标设备的IP地址找到目标设备的MAC地址
  3. 维护FDB 网桥转发规则 记录VTEP的MAC地址和宿主机IP对应关系
  4. flannel子网和宿主机之间的对应关系

flanneld做为宿主机上的一个用户进程 它们之间只要宿主机能相互通信,那么每个宿主机上的flanneld进程是可以通过宿主机网络相互传递数据包的。最新版本的 Flannel 并不依赖 L3 MISS 事件和 ARP 学习,而会在每台节点启动时把它的 VTEP 设备对应的 ARP 记录,直接下放到其他每台宿主机上。

封装好VTEP设备的MAC地址(内部数据帧),对于宿主机网络来说并没有什么实际意义所以上面封装出来的这个数据帧,并不能在我们的宿主机二层网络里传输,所以接下来,Linux 内核还需要再把“内部数据帧”进一步封装成为宿主机网络里的一个普通的数据帧,好让它“载着”“内部数据帧”,通过宿主机的eth0网卡进行传输。

VNI机制

Linux 内核会在“内部数据帧”前面,加上一个特殊的 VXLAN 头,用来表示这个“乘客”实际上是一个 VXLAN 要使用的数据帧。而这个 VXLAN 头里有一个重要的标志叫作 VNI,它 是 VTEP 设备识别某个数据帧是不是应该归自己处理的重要标识。而在Flannel中,VNI 的默认值是1,这也是为何宿主机上的 VTEP 设备都叫作flannel.1 的原因,这里的“1”,其 实就是 VNI 的值

VXLAN模式组建的覆盖网络,其实就是一个由不同宿主机上的VTEP设备,也就是 flannel.1 设备组成的虚拟二层网络.对于VTEP设备来说,它发出的“内部数据帧”就仿佛是一直在这个虚拟的二层网络上流动.这也正是覆盖网络的含义

img

flannel修改 VXLAN模式

### 修改backend 为 vxlan
etcdctl set /atomic.io/network/config '{"Network":"10.10.0.0/16","SubnetMin":"10.10.1.0","SubnetMax":"10.10.254.0","Backend": {"Type": "vxlan"}}'

### 修改成功后,重启 flannel 和 docker服务
systemctl restart flanneld docker

image-20210119101523812

VXLAN模式原理

通过VXLAN搭建的一个虚拟二层网络,二层网络通信就必须由自己的MAC地址才行。

创建自己的VTEP设备(有自己的IP和MAC地址),IP是用来给路由表用的 MAC地址是用来在虚拟二层网络中通信用的.

封装好的二层数据帧本质上还是走的宿主机之间的UDP通信机制,也只要求宿主机之间能够连通即可

目标容器IP根据路由规则找到目标VTEP设备的IP,目标设备IP根据ARP表找到目标设备MAC,目标MAC地址根据FDB表找到目标宿主机IP

找到目标宿主机IP就变成了两个宿主机之间的UDP通信,封装和解析UDP包的内容交给了flannel和其创建的独特的网络设备。

host-gw 模式

三层网络方案,而在这种模式下容器通信的过程就免除了额外的封包和解包带来的性能损耗。

host-gw 模式能够正常工作的核心,就在于 IP 包在封装成帧发送出去的时候,会使用路由表里的“下一跳”来设置目的 MAC 地址。这样,它就会经过二层网络到达目的宿主机。

flannel host-gw 模式必须要求集群宿主机之间是二层连通的。

由flanneld负责创建和维护路由规则,以下是master主机(10.0.30.101)修改为 host-gw 的路由表规则:

image-20210119102747097

10.10.9.0/24 dev docker0 proto kernel scope link src 10.10.9.1 # 本地容器路由规则
10.10.15.0/24 via 10.0.30.102 dev eth0 # 去往10.10.15.0/24 网段的IP包,应该经过本机的 eth0 设备发出去,并且下一跳(next-hop)是 10.0.30.102(即 via 10.0.30.102)

所谓下一跳地址就是:如果IP包从主机A发到主机B,需要经过路由设备X的中转,那么X的IP地址就应该配置为主机A的下一跳地址。

10.10.15.x是宿主机10.0.30.102 创建的容器ip地址段 当 10.0.30.101主机上的容器要访问 10.10.15.x 容器时,IP包从网络层进入链路层,主机会把下一跳mac地址封装成目标mac地址。

正因如此,就要求两个宿主机 10.0.30.101 和 10.0.30.102 必须在同一个二层网络中,只有这样两个主机才能直接通过mac地址通信。

修改 flannel 为 host-gw模式

### 修改backend 为 vxlan
etcdctl set /atomic.io/network/config '{"Network":"10.10.0.0/16","SubnetMin":"10.10.1.0","SubnetMax":"10.10.254.0","Backend": {"Type": "host-gw"}}'

### 修改成功后,重启 flannel 和 docker服务
systemctl restart flanneld docker

image-20210118153905743

参考链接:
https://www.cnblogs.com/goldsunshine/p/10740928.html

https://www.cnblogs.com/yxh168/p/12206368.html

posted @ 2021-01-19 10:47  hukey  阅读(2087)  评论(0编辑  收藏  举报