我们都知道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
,运行以下命令:
1234 | mkdir testcd testvagrant init minimum/ubuntu-trusty64-dockervi Vagrantfile |
里面应该有一句config.vm.box = "minimum/ubuntu-trusty64-docker"
,在它的下面添加如下几行代码,相当于给它分配三台虚拟机,一台叫做conf,它的IP是192.168.33.19;另两台叫做node1和node2,它们的IP是192.168.33.17和192.168.33.18。
1234567891011121314 | config.vm.define "node1" do | host | host.vm.hostname = "node1" host.vm.network "private_network", ip: "192.168.33.17"endconfig.vm.define "node2" do | host | host.vm.hostname = "node2" host.vm.network "private_network", ip: "192.168.33.18"endconfig.vm.define "conf" do | host | host.vm.hostname = "conf" host.vm.network "private_network", ip: "192.168.33.19"end |
这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后分别在三个终端运行以下命令启动并连接三台虚拟机。
12 | vagrant upvagrant ssh node1 |
1 | vagrant ssh node2 |
1 | vagrant ssh conf |
Ubuntu 14.04的内核版本是3.13,而使用docker overlay网络需要Linux内核版本3.16+,所以需要升级内核,建议升级到3.19或以上。因为我们用node1和node2来跑覆盖网络,所以只用升级这两台虚拟机就好了,conf不用管。命令如下:
123 | sudo apt-get install linux-generic-lts-vividsudo reboot |
等待重启之后,重新连接进vagrant虚拟机:
1 | vagrant ssh node1 |
1 | vagrant ssh node2 |
完成之后再用uname -r
看一下,现在应该已经是3.19了。
搭建环境
如上所述,docker的overlay网络支持consul,etcd,zookeeper等键值存储系统。这里我们使用比较轻量点的etcd,整个镜像不到15Mb。运行以下命令启动etcd:
12345678 | docker run -d --net=host --name=etcd kubernetes/etcd:2.0.5 /usr/local/bin/etcd --addr=192.168.33.19:4001 --bind-addr=0.0.0.0:4001 --data-dir=/var/etcd/data |
在node上,docker daemon启动参数里需要支持TCP,指定键值存储等。在node1和node2上分别运行以下命令:
12 | sudo sh -c 'echo DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.33.19:4001 --cluster-advertise=192.168.33.17:2375" >> /etc/default/docker'sudo service docker restart |
12 | sudo sh -c 'echo DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.33.19:4001 --cluster-advertise=192.168.33.18:2375" >> /etc/default/docker'sudo service docker restart |
有了这些,就可以开始创建overlay网络了。
运行容器
现在在node1或者node2上创建两个overlay网络backend和frontend:
123 | docker network create --driver overlay --subnet=10.0.7.0/24 backenddocker network create --driver overlay --subnet=10.0.8.0/24 frontenddocker network ls |
所有的网络在节点上是分享的,不管在哪个node上运行docker network ls
,都能看到全部网络。另外,墙裂建议创建覆盖网络的时候使用--subnet
,否则,docker将随机分配一个子网段,虽然docker内部是不会出现重复IP的,但是跟docker外部的其他虚拟机、容器等就不能保证不重复了。
现在在node1上分别用两个网络各自创建一个容器:
1234 | docker run -d --net=backend --name=be1 busybox sleep 3600docker run -d --net=frontend --name=fe1 busybox sleep 3600docker exec be1 ifconfigdocker exec fe1 ifconfig |
执行上面的ifconfig
可以看到,eth0的IP地址确实是在指定的subnet
里的。然后在node2上用backend网络创建一个容器:
12 | docker run -d --net=backend --name=be2 busybox sleep 3600docker exec be2 ifconfig |
容器建好以后,我们可以ping
一下看看:
12 | docker exec be2 ping -c 4 be1docker exec be2 ping -c 4 fe1 |
上面命令ping
里的主机名可以换成IP。运行完可以看到,node2上是可以访问node1上的相同overlay网络的,但是不同的overlay网络不能访问。在node1运行docker exec be1 ping -c 4 fe1
也能知道,即使主机相同,不同的overlay网络也是不能访问的,这就很好地实现了网络隔离。原来默认使用bridge的时候,相同主机的所有容器都是可以相互访问的,而不同主机的所有容器都是不能相互访问的。
最后我们再按照上面的原理图中间那个容器那样,创建一个既使用backend网络也使用frontend网络的容器:
123 | docker run -d --net=backend --name=bf2 busybox sleep 3600docker network connect frontend bf2docker exec bf2 ifconfig |
从上面的ifconfig
中可以看到,这个新容器既有10.0.7
网段的IP地址,又有10.0.8
网段的IP地址。网络搭好以后,我们可以ping
一下看看,果然它能够和两个覆盖网络里的容器通信:
12 | docker exec bf2 ping -c 4 be1docker exec bf2 ping -c 4 fe1 |
有兴趣的话,我们还可以去conf虚拟机上看看etcd里面的东东:
123 | docker exec -it etcd etcdctl ls /docker/nodes docker exec -it etcd etcdctl ls /docker/network/v1.0/endpoint # 所有的endpoint信息docker exec -it etcd etcdctl ls /docker/network/v1.0/overlay/network # 所有的overlay网络信息 |
参考资料
这篇文章比较了Docker Overlay Network、Flannel、Calico、Weave等4种覆盖网络,作者还有其它文章分别使用了这些覆盖网络。
此外,当然还有Docker官方文档和新手教程。