overlay网络模型与flannel实践

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

内容简介:这两天了解一些关于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互通,这是所有网络插件的共同目标,所以真的要用起来并不需要把插件细节搞明白,理解原理就差不多了。


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

查看所有标签

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

Learning JavaScript

Learning JavaScript

Shelley Powers / Oreilly & Associates Inc / 2006-10-17 / $29.99

As web browsers have become more capable and standards compliant, JavaScript has grown in prominence. JavaScript lets designers add sparkle and life to web pages, while more complex JavaScript has led......一起来看看 《Learning JavaScript》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具