Docker的覆盖网络是怎么工作的
我们都知道docker支持多种网络,覆盖网络overlay顾名思义,就是指建立在另一个网络上的网络。比如p2p技术,就是建立在因特网上的覆盖网络。而因特网,原来也是建立在电话网络上的覆盖网络,但现在,电话网络更像是建立在因特网上的覆盖网络(引自维基百科)。Docker使用覆盖网络可以解决容器跨主机互通和隔离的问题。在覆盖网络这个领域里,除了docker的overlay network以外,Flannel、Calico、Weave等也都是干这个的。
背景介绍
如果把容器的网络驱动设置成overlay,就意味着所有的容器都可以直接ping通,所以所有容器的ip地址都不会重复。为了做到这一点,就需要一个轻量级的存储来存放已经分配出去的ip信息,和其它的一些配置信息。技术上Docker使用了libkv和libnetwork来实现自己的覆盖网络。它们都是用go语言所写,前者是对操作分布式键值存储系统如consul,etcd,zookeeper的抽象层,后者实现了容器的网络连接,它的设计如下图:
容器网络模型由三部分组成:
- Sandbox:保存着容器的网络配置,一个Sandbox里可以有多个Endpoint
- Endpoint:可以通过Network和其他的Endpoint通信,每个Endpoint都只在一个网络里
- Network:由数个可以相互通信的Endpoint组成,不同的网络相互隔离
基于这个网络模型,在同一个网络里的所有容器,都是可以相互通信的。如果容器需要隔离,创建另一个网络即可。通过创建Swarm集群来应用覆盖网络是个很自然的做法,也是docker官方的推荐。而kubernetes并不打算支持docker的覆盖网络,据说技术上最主要的原因是kubernetes并不仅仅是为docker这一种容器技术服务的,kubernetes既不想再引入一个键值存储,也不愿意把自己的键值存储暴露给docker用。当然如果用户自己把docker需要的键值存储管理起来,docker自己的覆盖网络还是能够工作的。非技术上的原因是kubernetes认为docker不够开放,都是因为利益啊。详情可以参考这篇文章。这里为了不引入更多复杂度,我们不用Swarm,直接使用原生的docker daemon来做实验。
准备工作
我们需要先安装virtualBox和vagrant。通过vagrant来驱动virtualBox搭建一个虚拟测试环境。首先在本地任意路径新建一个空文件夹比如test
,运行以下命令:
里面应该有一句config.vm.box = "minimum/ubuntu-trusty64-docker"
,在它的下面添加如下几行代码,相当于给它分配三台虚拟机,一台叫做conf,它的IP是192.168.33.19;另两台叫做node1和node2,它们的IP是192.168.33.17和192.168.33.18。
这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后分别在三个终端运行以下命令启动并连接三台虚拟机。
|
|
|
|
Ubuntu 14.04的内核版本是3.13,而使用docker overlay网络需要Linux内核版本3.16+,所以需要升级内核,建议升级到3.19或以上。因为我们用node1和node2来跑覆盖网络,所以只用升级这两台虚拟机就好了,conf不用管。命令如下:
等待重启之后,重新连接进vagrant虚拟机:
|
|
完成之后再用uname -r
看一下,现在应该已经是3.19了。
搭建环境
如上所述,docker的overlay网络支持consul,etcd,zookeeper等键值存储系统。这里我们使用比较轻量点的etcd,整个镜像不到15Mb。运行以下命令启动etcd:
在node上,docker daemon启动参数里需要支持TCP,指定键值存储等。在node1和node2上分别运行以下命令:
|
|
有了这些,就可以开始创建overlay网络了。
运行容器
现在在node1或者node2上创建两个overlay网络backend和frontend:
所有的网络在节点上是分享的,不管在哪个node上运行docker network ls
,都能看到全部网络。另外,墙裂建议创建覆盖网络的时候使用--subnet
,否则,docker将随机分配一个子网段,虽然docker内部是不会出现重复IP的,但是跟docker外部的其他虚拟机、容器等就不能保证不重复了。
现在在node1上分别用两个网络各自创建一个容器:
执行上面的ifconfig
可以看到,eth0的IP地址确实是在指定的subnet
里的。然后在node2上用backend网络创建一个容器:
容器建好以后,我们可以ping
一下看看:
上面命令ping
里的主机名可以换成IP。运行完可以看到,node2上是可以访问node1上的相同overlay网络的,但是不同的overlay网络不能访问。在node1运行docker exec be1 ping -c 4 fe1
也能知道,即使主机相同,不同的overlay网络也是不能访问的,这就很好地实现了网络隔离。原来默认使用bridge的时候,相同主机的所有容器都是可以相互访问的,而不同主机的所有容器都是不能相互访问的。
最后我们再按照上面的原理图中间那个容器那样,创建一个既使用backend网络也使用frontend网络的容器:
从上面的ifconfig
中可以看到,这个新容器既有10.0.7
网段的IP地址,又有10.0.8
网段的IP地址。网络搭好以后,我们可以ping
一下看看,果然它能够和两个覆盖网络里的容器通信:
有兴趣的话,我们还可以去conf虚拟机上看看etcd里面的东东:
参考资料
这篇文章比较了Docker Overlay Network、Flannel、Calico、Weave等4种覆盖网络,作者还有其它文章分别使用了这些覆盖网络。
此外,当然还有Docker官方文档和新手教程。