前言
Weave作为在docker 0.x时代开始就比较完备的网络方案一直在不断发展,目前已经实现了对多种平台和软件的支持。本文将会介绍Weave Net的相关信息和进行体验。
简单介绍
支持云的平台、系统和软件有AWS,Azure,CoreOS,k8s,Mesos,Docker Machine,Docker Swarm等。目前Weave有三个成果:Weave Net,Weave Run,Weave Scope。
Weave Net的使用相当简单,Weave创建的网络可以连通在不同位置的容器,比如公有云、私有云,虚拟机和裸金属设备,容器网络可以承载二层和三层的流量,并支持多播;内建的加密功能让容器隔离更加容易实现; Weave网络还可以自动选择最快的路径路由容器流量,保证容器的网络速度。
原理
Weave Net是怎样实现跨主机通讯的呢?实际上,Weave的跨主机通讯方案与先前的Ambassador其实很相似,不过后者是让容器间通过docker的link功能实现通讯。
每台运行weave的主机都需要运行几个必须的容器,透过这些容器实现跨主机通讯。在一个weave网络中,会有多个运行在不同主机的peer,这些peer起到路由的作用。
在weave routers间会创建TCP或UDP连接,工作的流程是:
1.先执行handshake
2.随后交换拓扑信息
如果用户启用了加密(启用加密的方法会在后面说明),这些全双工的连接会使用UDP协议承载封装好的网包,并且可以透过防火墙。
在实现上,weave会在主机上创建一个网桥,容器会通过veth peer连接到网桥,一般情况下由weave自带的地址分配工具自动分配为容器分配地址,如果用户进行干预,则以用户设置优先。
因为起到路由作用的weave容器也会连接到上述网桥,所以,weave routers会借助pcap,透过设置为混杂模式的接入网桥的接口捕捉以太网包,但是对于直接透过内核转发的本地容器间流量或是宿主机与本地容器间的流量则会被排除。
被捕捉的数据包通过UDP协议转发到其他Host上的weave router peer上,一旦收到这些包,路由会把包通过pcap注入到它的网桥接口或转发到其他的peers。
weave路由会通过mac地址识别不同的peer所在的位置,连同拓扑信息去决定转发路径,避免采取像泛洪般的手段,把每个包都发到每个peer上,以提高网络性能。
安装、试用
笔者用了两个vmware虚拟机,分别为machine1(172.16.77.181)和machine2(172.16.77.182),只有一个NAT接口,使用的Docker版本为1.9。
安装
分别在两个vm上下载安装weave:
1 2 3 |
curl -L git.io/weave -o /usr/local/bin/weave chmod a+x /usr/local/bin/weave |
下载三个所需的docker image,tag全是1.4.3:
weaveworks/plugin
weaveworks/weaveexec
weaveworks/weave
下载完之后,检查一下,如果没有这三个镜像会启动失败:
在machine1上启动weave,如果正常启动的话,不会有任何输出:
1 2 |
root@machine1:~# weave launch |
在machin2上启动weave并指定machine1的ip地址,不输出任何结果:
1 2 |
root@machine2:~# weave launch 172.16.77.181 |
如果出现任何错误,请关闭并删除这三个运行中的容器,重新执行launch的步骤:
1 2 3 4 5 6 |
root@machine1:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 02fa7d00c7bc weaveworks/plugin:1.4.3 "/home/weave/plugin" 6 minutes ago Up 6 minutes weaveplugin ab54fcdfe90f weaveworks/weaveexec:1.4.3 "/home/weave/weavepro" 6 minutes ago Up 6 minutes weaveproxy a8064faa8ed4 weaveworks/weave:1.4.3 "/home/weave/weaver -" 6 minutes ago Up 6 minutes weave |
检查weave是否正确运行:
如果Connections后显示为failed,证明weave没有正确启动,参考上一步修正错误。
运行:
启动容器进行测试:
machine1:
1 2 |
root@machine1:~# docker run --net=weave -itd busybox |
machine2:
1 2 |
root@machine2:~# docker run --net=weave -itd busybox |
可以看到容器内多了一个网络接口ethwe0:
machine1:
machine2:
能互相ping通:
1 2 3 4 5 6 7 8 9 10 11 |
/ # ping -w 4 10.40.0.0 PING 10.40.0.0 (10.40.0.0): 56 data bytes 64 bytes from 10.40.0.0: seq=0 ttl=64 time=1.541 ms 64 bytes from 10.40.0.0: seq=1 ttl=64 time=0.610 ms 64 bytes from 10.40.0.0: seq=2 ttl=64 time=0.709 ms 64 bytes from 10.40.0.0: seq=3 ttl=64 time=0.560 ms --- 10.40.0.0 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.560/0.855/1.541 ms |
当我检查docker network时,发现weave已经为我们创建好网络了(weave):
1 2 3 4 5 6 7 8 |
root@machine1:~# docker network ls NETWORK ID NAME DRIVER 577f561de5e4 bridge bridge d3939e195d6a none null ebbc6c67837c host host 420ede818a78 weave weavemesh af63d717993f docker_gwbridge bridge |
如果我们检查namespace时,可以看到为容器创建的namespace:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
root@machine1:~# ls /var/run/docker/netns/ 29ad7412625b d96446acbf28 default root@machine1:~# ln -s /var/run/docker/netns/29ad7412625b /var/run/netns/29ad7412625b root@machine1:~# ip netns exec 29ad7412625b ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 11: ethwe0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UP group default link/ether 0a:d0:be:58:8f:c7 brd ff:ff:ff:ff:ff:ff inet 10.32.0.1/12 scope global ethwe0 valid_lft forever preferred_lft forever inet6 fe80::8d0:beff:fe58:8fc7/64 scope link valid_lft forever preferred_lft forever 13: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff inet 172.18.0.3/16 scope global eth1 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe12:3/64 scope link valid_lft forever preferred_lft forever |
再创建一个容器
1 2 3 |
root@machine1:~# docker run -itd --net=weave --name=c1 busybox 7ae82981d3b5f90ce9efacce4c284a39e6da425a78d18f0d64a4b4924f334a2b |
可以看到该容器的地址与在machine1上创建的第一个容器为同一地址段:
1 2 3 4 5 6 7 |
15: ethwe0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue link/ether 92:5d:26:b3:ac:95 brd ff:ff:ff:ff:ff:ff inet 10.32.0.2/12 scope global ethwe0 valid_lft forever preferred_lft forever inet6 fe80::905d:26ff:feb3:ac95/64 scope link valid_lft forever preferred_lft forever |
经过试验,c1可以ping通machine2上的容器也能ping同主机的容器
这时在/var/run/docker/netns中又多了一个namespace:
1 2 3 |
root@machine1:~# ls /var/run/docker/netns 23ead96553ca 29ad7412625b d96446acbf28 default |
由此可见,weave并不像docker原生网络组件一样,为网络又创建一个namespace。
以上只是简单地通过ip地址ping通容器,实际上,weave还提供了dns服务实现服务发现。
machine1:
1 2 3 4 5 |
root@machine1:~# docker run --net=weave -h c1.weave.local $(weave dns-args) -itd busybox 76879573b2fef919cbf02a101e2d3578e3f03106a22ce94aa40136dbcaaa394a root@machine1:~# weave dns-lookup c1.weave.local 10.32.0.1 |
这个命令实际上是用来创建一个hostname为c1.weave.local的容器,可以查询到这个hostname的ip地址为10.32.0.1。如果创建容器时指定了--name参数,在注册DNS时还是以hostname优先。
machine2:
1 2 3 |
root@machine2:~# docker run --net=weave -h c2.weave.local $(weave dns-args) -itd busybox 6a2fb5da61c60418aa64c78875790904354a89fd959c7c6631a584198d11d816 |
这时我们再c1.weave.local中并没有看到c2.weave.local的host记录,但是借助dns可以通过hostname ping通c2.weave.local。
至此,weave的安装和试用就到此结束。
下面是对其高级特性的探索:
负载均衡
实践起来无比简单:
环境和本系列的第一篇文章一样,两个虚拟机,ip地址分别为172.16.77.181和172.16.77.182,运行Docker1.9。
vm1:
1 2 |
root@machine1:~# weave launch --ipalloc-range 10.2.0.1/16 |
vm2:
1 2 |
weave launch --ipalloc-range 10.2.0.1/16 172.16.77.182 |
--ipalloc-range是用于指定用于分配的地址范围。
如果重启了机器,启动weave时会显示这个,它需要把几个weave所用的容器给启动起来才行。
1 2 |
weaveplugin is already running; you can stop it with 'weave stop-plugin'. |
如果connection established就没问题了。
在vm1上启动一个作为客户端的容器,用c1.weave.local作为hostname:
1 2 3 |
root@machine1:~# docker run -itd --net=weave -h c1.weave.local $(weave dns-args) ubuntu 44e2cce54acf2167a8c2cb9a2bdbce754a266469893108faa52dd2c28bcf5e0e |
如果你想修改local domain,可以这样:
1 2 |
weave launch --dns-domain="cluster.local." |
local domain可以通过weave status查看:
1 2 3 4 5 6 |
Service: dns Domain: weave.local. Upstream: 172.16.77.2 TTL: 1 Entries: 0 |
在vm2上以同一hostname启动多个容器,作为服务端(这里笔者启动了四个):
1 2 3 4 5 6 7 8 9 10 11 12 |
docker run -itd --net=weave -h c2.weave.local $(weave dns-args) ubuntu root@machine2:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b90598bef932 ubuntu "/bin/bash" 14 seconds ago Up 13 seconds cocky_ritchie 4c5d81457d4c ubuntu "/bin/bash" 17 seconds ago Up 16 seconds high_hugle 91929067ada3 ubuntu "/bin/bash" 23 seconds ago Up 23 seconds serene_sammet dfd34868a9f2 ubuntu "/bin/bash" 28 seconds ago Up 26 seconds grave_kirch d3071b666170 weaveworks/plugin:1.4.3 "/home/weave/plugin" 10 minutes ago Up 10 minutes weaveplugin 159816f2c7de weaveworks/weaveexec:1.4.3 "/home/weave/weavepro" 10 minutes ago Up 10 minutes weaveproxy 4ec0a94d1c6c weaveworks/weave:1.4.3 "/home/weave/weaver -" 10 minutes ago Up 10 minutes weave |
通过dns-lookup可以查询到对应c2.weave.local的ip地址有四个:
1 2 3 4 5 6 |
root@machine1:~# weave dns-lookup c2.weave.local 10.2.128.2 10.2.128.3 10.2.128.0 10.2.128.1 |
网络示意图:
attach到c1.weave.local中,安装好python2.7以后,创建以下脚本(lb.py):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import commands import pprint target = 'c2.weave.local' store = {} for i in range(10): raw_res = (commands.getoutput('ping -w 2 '+target).split()[2]) print 'host aware ....' res = raw_res.strip('(') res = res.strip(')') if store.has_key(res) == False: store[res] = 0 print 'aware done' for i in range(20): raw_res = (commands.getoutput('ping -w 2 '+target).split()[2]) print 'working ....' res = raw_res.strip('(') res = res.strip(')') store[res]=store[res]+1 print '1 done' print 'all done' pprint.pprint(store) |
尝试执行一下(会花上一点时间):
1 2 |
python lb.py |
最后程序输出了结果:
1 2 |
{'10.2.128.0': 3, '10.2.128.1': 6, '10.2.128.2': 7, '10.2.128.3': 4} |
可以知道ping了不同的地址多少次,这也证明weave的负载均衡功能是可用的。
子网、指定容器地址、容器隔离
虽然weave会自动为容器分配ip地址,并且允许了ip地址的重新利用,但是如果我们想指定容器所在的ip该怎么办?
如果想指定一个子网,可以在weave launch时指定:
1 2 |
weave launch --ipalloc-range 10.2.0.1/16 |
如果启动时没有指定这个参数,不同主机上,weave会在默认范围的基础上随机设定子网(默认网络范围可以从weave status中查看):
这是vm1上的容器c1的地址:
1 2 3 4 5 6 7 |
11: ethwe0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UP group default link/ether ba:7d:75:41:93:6f brd ff:ff:ff:ff:ff:ff inet 10.32.0.1/12 scope global ethwe0 valid_lft forever preferred_lft forever inet6 fe80::b87d:75ff:fe41:936f/64 scope link valid_lft forever preferred_lft forever |
vm2上c2的:
1 2 3 4 5 6 7 |
12: ethwe0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UP group default link/ether b6:8d:64:5c:88:c8 brd ff:ff:ff:ff:ff:ff inet 10.40.0.0/12 scope global ethwe0 valid_lft forever preferred_lft forever inet6 fe80::b48d:64ff:fe5c:88c8/64 scope link valid_lft forever preferred_lft forever |
我们可以在weave上查看某个容器的MAC和IP地址:
1 2 3 |
root@machine1:~# weave ps c1 c1 ba:7d:75:41:93:6f 10.32.0.1/12 |
我们可以在容器启动时指定WEAVE_CIDR参数以设置ip地址:
然而......
1 2 3 4 5 |
root@machine2:~# docker run -e WEAVE_CIDR=10.3.1.1/24 -itd --name=c3 ubuntu 5dc2266cf11c41487f4a6466290fbd2bc088740a63b4425b2d1842b96c26fb0e root@machine2:~# weave ps c3 root@machine2:~# |
根本没有查询结果?进入容器看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@5dc2266cf11c:/# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 16: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:2/64 scope link valid_lft forever preferred_lft forever |
WTH?其实是weave启动后没有进行这一步的缘故:
1 2 |
root@machine1:~# eval $(weave env) |
实际上这个过程是这样的:
1 2 3 |
root@machine1:~# weave env export DOCKER_HOST=unix:///var/run/weave/weave.sock |
结论:
如果我们没有在启动weave后执行eval $(weave env),那么想使用weave只能通过docker run时添加--net=weave这个参数(作为docker plugin运行,有兴趣的童鞋可以去docker文档那里研究如何编写自己的plugin)。只有执行前面的eval命令,容器才会自动attach到weave网络中。
试验一下:
vm2:
1 2 3 |
root@machine2:~# weave launch --ipalloc-range 10.2.0.0/16 --ipalloc-default-subnet 10.2.1.0/24 root@machine2:~# eval $(weave env) |
vm1:
1 2 3 |
root@machine1:~# weave launch --ipalloc-range 10.2.0.0/16 --ipalloc-default-subnet 10.2.1.0/24 172.16.77.182 root@machine1:~# eval $(weave env) |
上面的命令指定了网络地址范围和默认子网。
如果启动容器时不指定WEAVE_CIDR,容器就会自动attach到默认子网中。
通过指定WEAVE_CIDR,就可以使容器得到指定IP:
1 2 3 4 5 |
root@machine1:~# docker run --name default-c1 -itd ubuntu 3fab72d00d48b02313e97737c29e6543113af4669550d67e9f8b0619ae63a425 root@machine1:~# docker run --name default-c2 -itd ubuntu 30aa3780b7eaaa3ea0eb952b371dd50d48403d93d2bbe6398a1594347cfb010b |
可以看到default-c1的地址,分配到默认子网中了:
1 2 3 4 5 |
root@machine1:~# weave ps default-c1 default-c1 5a:58:44:4a:17:61 10.2.1.1/24 root@machine1:~# weave ps default-c2 default-c2 f2:8d:ff:1f:be:c1 10.2.1.2/24 |
若指定参数:
1 2 3 4 5 6 7 8 9 |
root@machine2:~# docker run -e WEAVE_CIDR=10.2.6.1/24 -itd --name=c3 ubuntu ccb32d50644f3d6c1cdadfb387e58c5f23736b3346eee14aacd4ac503848ab4f root@machine2:~# docker run -e WEAVE_CIDR=10.2.7.1/24 -itd --name=c4 ubuntu f1cbddab6722ef2a9fa5a250a371d3724c77c15fca5b63141c3f233675549d4f root@machine2:~# weave ps c3 c3 c2:91:db:be:fc:71 10.2.6.1/24 root@machine2:~# weave ps c4 c4 86:44:bd:5e:1b:b9 10.2.7.1/24 |
测试一下容器隔离的效果:
在default-c1中ping default-c2
1 2 3 4 5 |
root@default-c1:/# ping -w 4 default-c2 --- default-c2.weave.local ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 0.101/3.843/15.061/6.476 ms |
ping c3:
1 2 3 4 |
root@default-c1:/# ping -w 4 c3 --- c3.weave.local ping statistics --- 5 packets transmitted, 0 received, 100% packet loss, time 3999ms |
可以看到,如果执行eval那条命令,就不用再指定hostname,直接使用容器名就可以ping通
dns查询结果:
1 2 3 |
root@machine1:~# weave dns-lookup default-c1 10.2.1.1 |
手动attach容器到网络中
我们可以直接通过attach命令把没有设置网络的容器加入到weave中:
1 2 3 |
root@machine1:~# docker run -e WEAVE_CIDR=none -itd --name=nn ubuntu 2782cc603ac1af35bded29cef3ce1f49022c6dcad8ad726b7e41f17e8a56df38 |
启动了一个只有local lo和eth0(连接到默认的bridge上)接口的容器
1 2 3 |
root@machine1:~# weave attach nn 10.2.1.3 |
手动把容器attach到weave网络(默认子网)上,返回一个ip地址
如果想让它detach,输入:
1 2 3 4 |
root@machine1:~# weave detach nn 10.2.1.3 root@machine1:~# weave ps nn |
detach完毕。
我们也可以通过"net:"+地址将容器attach到weave,比如:
1 2 3 |
weave attach net:default THE_NAME_OF_CONTAINER weave attach net:10.2.1.0/24 THE_NAME_OF_CONTAINER |
我们可以为容器同时指定多个地址:
1 2 3 |
weave attach net:default net:10.2.2.0/24 net:10.2.3.0/24 $C 10.2.1.3 10.2.2.3 10.2.3.1 |
安全
启动weave时,我们可以很简单地添加一个--password或环境变量参数以实现加密通讯:
1 2 |
root@machine1:~# weave launch --password thisisapasswd |
如果不指定password去连接另一台主机,虽然不会输出错误信息,但检查weave status会发现问题:
1 2 |
root@machine2:~# weave launch 172.16.77.181 |
查询到问题:
1 2 3 |
Targets: 1 Connections: 1 (1 failed) |
输入密码:
1 2 |
root@machine2:~# weave launch --password thisisapasswd 172.16.77.181 |
发现已经连接成功:
1 2 3 4 |
Targets: 1 Connections: 1 (1 established) Peers: 2 (with 2 established connections) |
关于操作weave
在笔者实验的时候,出现过几次问题,需要注意一下几点:
1.机器重启后,如果weave并非随机启动的话,注意用docker ps看看weave所需的三个容器有没有显示出来,如果有,但不是全部的话,建议先weave reset,weave会删除那三个容器,等你重新启动weave再重新创建(自动)
2.正确的关闭方法应该是输入weave stop,此时weave会自动关闭并删除三个它所需的容器。不要随便用其他方法关闭。
3.weave启动后,建议检查weave status,如果weave连接失败,不会回显结果。检查后如果出现failed的字样,就要从多方面检查原因,比如网络有没有连通、有没有需要--password参数而没输入、三个所需的容器有没有正常启动等。
4.如果执行了前面eval $(weave env)这个命令,此时不应该直接关闭weave,而是修改相应的环境变量DOCKER_HOST到原来的值。或者直接重启机器即可。
结语
本文通过实践,对weave的安装与使用进行了深入研究,希望能对各位研究者有所帮助。
参考资料:
http://weave.works/guides/load-balancing-with-weave-run/#
http://docs.weave.works/weave/latest_release/features.html#virtual-ethernet-switch
http://docs.weave.works/weave/latest_release/weavedns.html
http://weave.works/guides/networking-docker-containers-with-weave-on-ubuntu/
http://docs.weave.works/weave/latest_release/plugin.html
作者简介:何智刚,2015至今,现为广东的一名在校高三学生,在学习之余,主要研究Docker,OpenStack,SDN,对各种领域都有所涉猎,目标是迈向full stack