overlay网络模型与flannel实践

栏目: 编程工具 · 发布时间: 6年前

内容简介:这两天了解一些关于docker容器跨节点网络模型的知识,觉得收获还是挺大的,并且也动手尝试搭建了flannel容器网络,所以非常有必要把思考记录下来。在讲容器的网络之前,先得讲讲服务器之间是怎么通讯的。通过交换机连在一起的节点属于同一个网段,比如我有2台属于同网段的机器:

这两天了解一些关于 docker 容器跨节点网络模型的知识,觉得收获还是挺大的,并且也动手尝试搭建了flannel容器网络,所以非常有必要把思考记录下来。

过往的知识

在讲容器的网络之前,先得讲讲服务器之间是怎么通讯的。

通过交换机连在一起的节点属于同一个网段,比如我有2台属于同网段的机器:

enp0s3    Link encap:Ethernet  HWaddr 08:00:27:ac:f0:7f
          inet addr:172.18.10.96  Bcast:172.18.11.255  Mask:255.255.252.0

以及

enp0s3    Link encap:Ethernet  HWaddr 08:00:27:17:0e:a9
          inet addr:172.18.10.98  Bcast:172.18.11.255  Mask:255.255.252.0

它们的IP地址&掩码是相同的。

通过查看路由表,可以确定它俩连在一台交换机上,属于局域网关系:

172.18.8.0      *               255.255.252.0   U     0      0        0 enp0s3
default         172.18.11.254   0.0.0.0         UG    0      0        0 enp0s3

以及

172.18.8.0      *               255.255.252.0   U     0      0        0 enp0s3
default         172.18.11.254   0.0.0.0         UG    0      0        0 enp0s3

如果某个节点要向目标IP发包,那么先查自己的路由表。

  • 如果目标IP符合172.18.8.0网段,那么说明目标IP在局域网里,可以直接ARP广播问一下目标IP的MAC地址,然后直接从enp0s3网卡送出去就可以送达。
  • 否则就需要ARP问一下网关172.18.11.254的MAC地址,然后把包发给网关,让网关想办法去。

网关怎么想办法呢?网关一般带路由功能,它继续查看自己的路由表决定包往哪里送,就像刚刚我们机器做的那样,这是一个往往复复的过程。

容器带来的网络问题

我们最终希望容器技术可以像真的机器一样,每个容器有自己的IP并认为自己就是普通的机器,不同机器上的容器之间可以直接基于容器的IP通讯,完全不用需要在乎自己运行在物理机还是虚拟机上。

然而我们手里的机器只有一块网卡,也只分配了一个公网IP,这就是我们的现状,所以如何给每个容器不一样的IP呢?

overlay的本质

因为上述目标,所以出现了若干种实现容器跨节点网络通讯的技术方案。

overlay不是具体方案,它是一种实现思路。

我们可以通过 linux 虚拟化技术给每个容器虚拟化一个网卡,并且赋予一个独一无二的虚拟IP,这些IP与我们物理机所处的网络中的任何IP都不冲突。

这感觉就像自欺欺人,我们在容器里配了一些假的IP,然后还期望容器可以基于假IP调用到另外一个机器上的容器,但overlay的确就是要做这样一件事情。

我们知道物理机有真实IP,可以基于现有网络拓扑实现同网段或者跨网段的任意通讯,那么最直接的想法就是物理机把容器发出来的包封装一下,基于物理机所处的网络把包送到目标容器所处的物理机IP,然后目标物理机再进行解封得到原始容器的包,交给目标容器处理。

Overlay重叠网络就是这个意思,在现有的网络环境之上隐藏或者说虚拟一套假IP,并且可以用物理网络进行跨节点的运输,而一般这套封装协议就是vxlan协议。经过在现有网络上封装与解封,不仅可以实现虚拟IP之间的互通,而且容器自身完全无感知,就像真的有这些虚拟IP一样。

overlay实现原理

容器发出的包,源IP、目标IP、源MAC、目标MAC都是假的,经过宿主机的vxlan协议封装后,外层的源IP、目标IP、源MAC、目标MAC都变成真的,内层封装的仍旧是容器的包,这就是基本的overlay原理。

overlay只是一个思路,在实现上就有多种方案,原理有差异,但是思想与目标都和上述相同。

在实现overlay的时候,要搞定2个事情:

  • 确保容器的虚拟IP不重复,因为经过overlay封装后,容器之间通讯就和真实的网络环境一样,IP冲突就无法通讯了。
  • 物理机封装容器发出的包时,得知道目标容器IP在哪个物理机IP上。

实现方法一般就是搭建一套etcd来实现虚拟IP的唯一分配,以及维护虚拟IP与物理IP之间的关系。

因此,每台物理机上要有一个进程与etcd交互来申请虚拟IP分配到机器上的容器,以及获知虚拟IP与物理IP的关系,完成overlay包的封装。

不同overlay方案在实现虚拟IP分配时就会有不同,我大概发现了2种思路:

  1. etcd中的IP池子是同网段的,启动容器之前去etcd获取一个虚拟IP,因为同网段通讯只需要交换机转发即可,所以就在每台机器上虚拟一个交换机,这样容器的包就可以被虚拟交换机拿到,通过overlay封装发出。
  2. etcd中的IP池子是个大网段,每台物理机一次性获取其中的一个子网段占为己有,启动容器前只需要从占据的子网段中分配一个IP。同一台机器上的容器网段相同,所以需要一个交换机即可互通。不同机器上的容器网段不同,所以在每台机器上配置一个虚拟网关来协助路由,这样跨节点通讯就会被虚拟网关拿到,通过overlay发出。

上述总结的思路,其实就是想说明overlay虚拟化网络和物理世界的网络工作原理是一致的,如果虚拟了IP就要根据情况虚拟对应的中间设备。

以flannel为例

flannel是docker的一款网络插件,它实现了基于vxlan封装的overlay模型。

通过安装配置flannel,我们就可以打通容器之间的虚拟网络,对了,它属于第2种思路,也就是每台机器占据一个虚拟IP网段,不同机器的网段不同。

基本环境

我创建了3台virtualbox+ubuntu 16的虚拟机,配置他们的网络为host-only,这样它们的虚拟MAC地址就暴露到局域网了,可以直接从局域网中分配IP,可以和宿主机通讯,可以互相通讯,也可以访问外网。

接着安装docker,大家根据docker官网的步骤通过apt-get安装即可,然后需要给docker配置镜像加速器,大家注册 https://www.daocloud.io/ 获取加速器,配置到3台ubuntu机器上即可,这些基础工作就不详细说了。

3台机器的情况如下,大家就把他们看做3台物理机就好了:

搭建etcd的机器 172.18.10.95
搭建docker和flannel 172.18.10.96
搭建docker和flannel 172.18.10.98

安装etcd

前面说过,etcd用来维护虚拟IP池以及物理IP与虚拟IP的映射关系。

登录95机器,先下载linux amd64版本的release二进制: https://github.com/etcd-io/etcd/releases

解压启动:

nohup ./etcd --listen-client-urls http://0.0.0.0:2379 -advertise-client-urls http://0.0.0.0:2379 &

这样单机的etcd就可以工作了。

安装flannel

我以96机器为例,说明安装过程和原理。

下载linux amd64的flannel二进制: https://github.com/coreos/flannel/releases

解压后会有一个flanneld的二进制程序以及一个mk-docker-opts.sh的脚本。

负责给容器分配IP以及完成vxlan打包/解包的就是flanneld程序,因此它需要与etcd交互。

启动flanneld最基本的要求就是配置etcd的地址,其他参数保持默认一般是可以工作的:

nohup ./flanneld -etcd-endpoints http://172.18.10.95:2379 &

启动后会有一些警告日志,因为我们没有在etcd配置flannel的虚拟IP池信息,所以flanneld无法获取虚拟IP网段。

配置ip池

回到95机器上,打开一个flannel.json文件,我们配置一下flannel的IP池:

{
	"Network": "10.0.0.0/8",
	"SubnetLen": 24,
	"Backend": {"Type": "vxlan"}
}

这个意思是整个大IP池是10.x.x.x,每台机器可以拿走一个掩码为24的网段。

比如某台机器可以申请到一个这样的网段:10.0.5.0/24,那么这台机器上的容器可以分配的IP范围就是10.0.5.0~10.0.5.255,也就是最多部署255个容器IP就会用尽。

同样道理,大IP池总共可以划分出2^16个网段,即支持2^16台机器加入到flannel的集群中来。

我们使用etcd v2版本的API,把配置上传到flanneld的默认位置:

./etcdctl set /coreos.com/network/config < ~/flannel.json

物理机网络配置

现在查看96机器的flanneld日志,会发现它已经分配到了IP段:10.0.5.0/24。

观察机器网络设备的变化,会发现多了一个叫做flannel1.1的虚拟网卡,这个网卡的分配到了一个IP地址是10.0.5.0:

flannel.1 Link encap:Ethernet  HWaddr 36:ba:b9:5c:8d:d6
          inet addr:10.0.5.0  Bcast:0.0.0.0  Mask:255.255.255.255
          inet6 addr: fe80::34ba:b9ff:fe5c:8dd6/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:53 errors:0 dropped:0 overruns:0 frame:0
          TX packets:53 errors:0 dropped:8 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:4452 (4.4 KB)  TX bytes:4452 (4.4 KB)

就和物理网卡有自己的IP地址一样,虚拟网卡也可以有自己的虚拟IP,现在IP就是10.0.5.0。

这张虚拟网卡实际背后就是flanneld进程,任何发往这张网卡的数据都会被flanneld进程处理,从而实现vxlan封装,并通过物理网卡发出去,大家理解这一点即可。

配置docker

既然flanneld已经分配到了虚拟IP网段,接下来我们要让docker生成容器时从这个网段中分配IP,这一点只需要给docker改改配置就可以实现。

一旦flanned成功分配到网段,就会生成这样一个配置文件:

root@ubuntu:~# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.0.0.0/8
FLANNEL_SUBNET=10.0.5.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

其中记录了分配到的subnet是10.0.5.1/24,之所以末尾是1,是因为需要预留出了10.0.5.0给flannel1.1虚拟网卡用。

我们现在执行伴随flannel下载的那个脚本:

sh mk-docker-opts.sh -c

可以生成一个docker的启动命令参数:

root@ubuntu:~# cat /run/docker_opts.env
DOCKER_OPTS=" --bip=10.0.5.1/24 --ip-masq=true --mtu=1450"

–bip参数就是用来告诉docker,创建容器时从这个IP段内分配IP,是不是很巧妙?docker本来就要给容器分配IP的呀。

现在修改docker的启动脚本即可:

/etc/systemd/system/multi-user.target.wants/docker.service

修改如下:

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
EnvironmentFile=/run/docker_opts.env
ExecStart=/usr/bin/dockerd -H unix:// $DOCKER_OPTS

然后让systemctl重新加载配置,然后重启docker:

root@ubuntu:~# systemctl daemon-reload
root@ubuntu:~# systemctl restart docker

物理机网络配置

root@ubuntu:~# ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:2c:3b:dd:2b
          inet addr:10.0.5.1  Bcast:10.0.5.255  Mask:255.255.255.0
          inet6 addr: fe80::42:2cff:fe3b:dd2b/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:11025 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11742 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:803316 (803.3 KB)  TX bytes:31400158 (31.4 MB)

现在可以看到,docker默认创建的docker0网桥,其IP分配到了10.0.5.1。不仅如此,后续创建的容器都会在这个网段下分配。

所有的容器都将连在docker0上,因为它们的IP段相同,所以本机容器之间可以直接基于docker0通讯,docker0网桥就是虚拟交换机的概念。

容器内网络

接下来,我们创建一个容器,看看容器内的网络配置:

root@ubuntu:~# docker run -it busybox

docker给容器分配的IP:

/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:05:02
          inet addr:10.0.5.2  Bcast:10.0.5.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:7 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:598 (598.0 B)  TX bytes:0 (0.0 B)

查看容器内的路由:

/ # route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         10.0.5.1        0.0.0.0         UG    0      0        0 eth0
10.0.5.0        *               255.255.255.0   U     0      0        0 eth0

可见,发往10.0.5.0/24的包属于同网段通讯,在docker0网桥上通过arp可以直接得到目标mac地址,经过docker0交换即可。

其他的包则直接发往默认网关docker0(10.0.5.1),可以为理解docker0既支持路由又支持交换,像家庭路由器一样。

从物理机上可以看出docker0虚拟机交换机上,拉了一根到容器eth0网口的网线:

root@ubuntu:~# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02422c3bdd2b	no		veth43dc82c

这么描述大家应该更容易理解。

docker0继续路由

docker0是物理机上的网桥,所以使用的是物理机的路由表。

/ # root@ubuntu:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.18.11.254   0.0.0.0         UG    0      0        0 enp0s3
10.0.5.0        *               255.255.255.0   U     0      0        0 docker0
172.18.8.0      *               255.255.252.0   U     0      0        0 enp0s3

如果发往的目标IP是外网IP(例如:百度),那么只能走物理机的默认网关,不过这里会经过iptables的NAT转换,百度那边认为是我们的物理机在访问它。

我们关注的是容器访问另外一台机器上的容器,现在因为没有启动另外一台机器,所以没有我们想看到的路由规则。

配置98机器的flannel

配置过程与96机器一致,分配到的网段是:

root@ubuntu:~# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.0.0.0/8
FLANNEL_SUBNET=10.0.20.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

观察96机器的路由表,发现多了一条路由规则:

/ # root@ubuntu:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.18.11.254   0.0.0.0         UG    0      0        0 enp0s3
10.0.5.0        *               255.255.255.0   U     0      0        0 docker0
10.0.20.0       10.0.20.0       255.255.255.0   UG    0      0        0 flannel.1
172.18.8.0      *               255.255.252.0   U     0      0        0 enp0s3

也就是如果目标容器地址属于10.0.20.0/24网段,那么通过flannel1.1虚拟网卡发出,所以flanneld进程就可以有机会进行vxlan封装,并通过物理网卡发往目标物理机。

同样的,98机器的路由表也是类似的:

root@ubuntu:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.18.11.254   0.0.0.0         UG    0      0        0 enp0s3
10.0.5.0        10.0.5.0        255.255.255.0   UG    0      0        0 flannel.1
10.0.20.0       *               255.255.255.0   U     0      0        0 docker0
172.18.8.0      *               255.255.252.0   U     0      0        0 enp0s3

发往10.0.5.0的都交给flannel1.1处理,而flanneld进程要做的事情就是根据etcd中的记录获知目标物理机IP,然后vxlan封装送过去。

我们去看一下etcd中的记录就会更清晰的认识到这一点:

root@ubuntu:~/etcd-v3.3.10-linux-amd64# ./etcdctl ls /coreos.com/network/subnets/
/coreos.com/network/subnets/10.0.5.0-24
/coreos.com/network/subnets/10.0.20.0-24
root@ubuntu:~/etcd-v3.3.10-linux-amd64# ./etcdctl get /coreos.com/network/subnets/10.0.5.0-24
{"PublicIP":"172.18.10.96","BackendType":"vxlan","BackendData":{"VtepMAC":"36:ba:b9:5c:8d:d6"}}
root@ubuntu:~/etcd-v3.3.10-linux-amd64# ./etcdctl get /coreos.com/network/subnets/10.0.20.0-24
{"PublicIP":"172.18.10.98","BackendType":"vxlan","BackendData":{"VtepMAC":"9e:94:df:d8:67:c3"}}

一目了然,虚拟网段与物理机IP的关系都记录在etcd中。

测试

现在可以让2台物理机上的容器互相ping通,并且是基于虚拟IP:

root@ubuntu:~# docker run -it busybox
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:05:02
          inet addr:10.0.5.2  Bcast:10.0.5.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:9 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:758 (758.0 B)  TX bytes:0 (0.0 B)
 
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
 
/ # ping 10.0.20.3
PING 10.0.20.3 (10.0.20.3): 56 data bytes
64 bytes from 10.0.20.3: seq=0 ttl=62 time=0.626 ms
64 bytes from 10.0.20.3: seq=1 ttl=62 time=0.946 ms

反过来也是一样的:

/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:14:03
          inet addr:10.0.20.3  Bcast:10.0.20.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:15 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1278 (1.2 KiB)  TX bytes:574 (574.0 B)
 
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
 
/ # ping 10.0.5.2
PING 10.0.5.2 (10.0.5.2): 56 data bytes
64 bytes from 10.0.5.2: seq=0 ttl=62 time=0.602 ms
64 bytes from 10.0.5.2: seq=1 ttl=62 time=0.559 ms

总结

以flannel为例,通过实践可以更加具体的感受虚拟化网络的玩法和思路。

不过flannel性能比较差劲,目前据说最好的选型是calico方案,但是理解和使用起来也会更复杂。

但是无论选用任何一种网络模型,最终实现的效果都是一样的,就是容器基于虚拟IP互通,这是所有网络插件的共同目标,所以真的要用起来并不需要把插件细节搞明白,理解原理就差不多了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

A Byte of Python

A Byte of Python

Swaroop C H / Lulu Marketplace / 2008-10-1 / USD 27.98

'A Byte of Python' is a book on programming using the Python language. It serves as a tutorial or guide to the Python language for a beginner audience. If all you know about computers is how to save t......一起来看看 《A Byte of Python》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具