文章目录
  1. 1. 背景介绍
  2. 2. 准备工作
  3. 3. 搭建环境
  4. 4. 运行容器
  5. 5. 参考资料

我们都知道docker支持多种网络,覆盖网络overlay顾名思义,就是指建立在另一个网络上的网络。比如p2p技术,就是建立在因特网上的覆盖网络。而因特网,原来也是建立在电话网络上的覆盖网络,但现在,电话网络更像是建立在因特网上的覆盖网络(引自维基百科)。Docker使用覆盖网络可以解决容器跨主机互通和隔离的问题。在覆盖网络这个领域里,除了docker的overlay network以外,Flannel、Calico、Weave等也都是干这个的。

背景介绍

如果把容器的网络驱动设置成overlay,就意味着所有的容器都可以直接ping通,所以所有容器的ip地址都不会重复。为了做到这一点,就需要一个轻量级的存储来存放已经分配出去的ip信息,和其它的一些配置信息。技术上Docker使用了libkvlibnetwork来实现自己的覆盖网络。它们都是用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来做实验。

准备工作

我们需要先安装virtualBoxvagrant。通过vagrant来驱动virtualBox搭建一个虚拟测试环境。首先在本地任意路径新建一个空文件夹比如test,运行以下命令:

virtual box host
1
2
3
4
mkdir test
cd test
vagrant init minimum/ubuntu-trusty64-docker
vi Vagrantfile

里面应该有一句config.vm.box = "minimum/ubuntu-trusty64-docker",在它的下面添加如下几行代码,相当于给它分配三台虚拟机,一台叫做conf,它的IP是192.168.33.19;另两台叫做node1node2,它们的IP是192.168.33.17192.168.33.18

Vagrantfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
config.vm.define "node1" do | host |
host.vm.hostname = "node1"
host.vm.network "private_network", ip: "192.168.33.17"
end
config.vm.define "node2" do | host |
host.vm.hostname = "node2"
host.vm.network "private_network", ip: "192.168.33.18"
end
config.vm.define "conf" do | host |
host.vm.hostname = "conf"
host.vm.network "private_network", ip: "192.168.33.19"
end

这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后分别在三个终端运行以下命令启动并连接三台虚拟机。

virtual box host terminal 1
1
2
vagrant up
vagrant ssh node1

virtual box host terminal 2
1
vagrant ssh node2
virtual box host terminal 3
1
vagrant ssh conf

Ubuntu 14.04的内核版本是3.13,而使用docker overlay网络需要Linux内核版本3.16+,所以需要升级内核,建议升级到3.19或以上。因为我们用node1和node2来跑覆盖网络,所以只用升级这两台虚拟机就好了,conf不用管。命令如下:

node1 and node2
1
2
3
sudo apt-get install linux-generic-lts-vivid
sudo reboot

等待重启之后,重新连接进vagrant虚拟机:

virtual box host terminal 1
1
vagrant ssh node1

virtual box host terminal 2
1
vagrant ssh node2

完成之后再用uname -r看一下,现在应该已经是3.19了。

搭建环境

如上所述,docker的overlay网络支持consul,etcd,zookeeper等键值存储系统。这里我们使用比较轻量点的etcd,整个镜像不到15Mb。运行以下命令启动etcd:

conf
1
2
3
4
5
6
7
8
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上分别运行以下命令:

node1
1
2
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

node2
1
2
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:

node1 or node2
1
2
3
docker network create --driver overlay --subnet=10.0.7.0/24 backend
docker network create --driver overlay --subnet=10.0.8.0/24 frontend
docker network ls

所有的网络在节点上是分享的,不管在哪个node上运行docker network ls,都能看到全部网络。另外,墙裂建议创建覆盖网络的时候使用--subnet,否则,docker将随机分配一个子网段,虽然docker内部是不会出现重复IP的,但是跟docker外部的其他虚拟机、容器等就不能保证不重复了。

现在在node1上分别用两个网络各自创建一个容器:

node1
1
2
3
4
docker run -d --net=backend --name=be1 busybox sleep 3600
docker run -d --net=frontend --name=fe1 busybox sleep 3600
docker exec be1 ifconfig
docker exec fe1 ifconfig

执行上面的ifconfig可以看到,eth0的IP地址确实是在指定的subnet里的。然后在node2上用backend网络创建一个容器:

node2
1
2
docker run -d --net=backend --name=be2 busybox sleep 3600
docker exec be2 ifconfig

容器建好以后,我们可以ping一下看看:

node2
1
2
docker exec be2 ping -c 4 be1
docker exec be2 ping -c 4 fe1

上面命令ping里的主机名可以换成IP。运行完可以看到,node2上是可以访问node1上的相同overlay网络的,但是不同的overlay网络不能访问。在node1运行docker exec be1 ping -c 4 fe1也能知道,即使主机相同,不同的overlay网络也是不能访问的,这就很好地实现了网络隔离。原来默认使用bridge的时候,相同主机的所有容器都是可以相互访问的,而不同主机的所有容器都是不能相互访问的。

最后我们再按照上面的原理图中间那个容器那样,创建一个既使用backend网络也使用frontend网络的容器:

node2
1
2
3
docker run -d --net=backend --name=bf2 busybox sleep 3600
docker network connect frontend bf2
docker exec bf2 ifconfig

从上面的ifconfig中可以看到,这个新容器既有10.0.7网段的IP地址,又有10.0.8网段的IP地址。网络搭好以后,我们可以ping一下看看,果然它能够和两个覆盖网络里的容器通信:

node2
1
2
docker exec bf2 ping -c 4 be1
docker exec bf2 ping -c 4 fe1

有兴趣的话,我们还可以去conf虚拟机上看看etcd里面的东东:

conf
1
2
3
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官方文档新手教程

文章目录
  1. 1. 背景介绍
  2. 2. 准备工作
  3. 3. 搭建环境
  4. 4. 运行容器
  5. 5. 参考资料