容器网络

 

在前面的博客中已经详细讲解了几种网络方案: none, host, bridge,user-defined。但是他们只是解决了单个主机间的容器的通信问题,并不能实现多个主机容器之间的通信。本篇博客将详细介绍如何实现该功能。

跨主机网络方案包括两大类:

  1. docker原生的:overlay和macvlan
  2. 第三方方案:flannel、weave和calico

本篇博客将详细讲解overlay以及weave两种方案。

 

overlay

 

Docerk overlay 网络需要一个 key-value 数据库用于保存网络状态信息,包括 Network、Endpoint、IP 等。Consul、Etcd 和 ZooKeeper 都是 Docker 支持的 key-vlaue 软件,我们这里使用 Consul。

 

在 docker 主机 host1(172.20.10.2)和 host2(172.20.10.7)上实践各种跨主机网络方案,在 172.20.10.2 上部署支持的组件,比如 Consul。

 

第一步:启动路由转发功能

两台主机上面都需要开启



[root@ken3 ~]# echo "1" > /proc/sys/net/ipv4/ip_forward



 

第二步:运行consul

最简单的方式是以容器方式运行 Consul:



[root@ken1 ~]# docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap



 

第三步:浏览器访问

容器启动后,可以通过 http://172.20.10.2:8500 访问 Consul。

portainer管理另一台主机docker docker跨主机通信_IP

 

第四步:修改docker启动文件

接下来修改 host1 和 host2 的 docker daemon 的配置文件/usr/lib/systemd/system/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
ExecStart=/usr/bin/dockerd -H unix:// --cluster-store=consul://172.20.10.2:8500 --cluster-advertise=eth0:2376
...



--cluster-store 指定 consul 的地址。
--cluster-advertise 告知 consul 自己的连接地址(eth0即为网卡名,固定格式)

重启 docker daemon。



[root@ken1 ~]# systemctl daemon-reload
[root@ken1 ~]# systemctl restart docker



重启consul



[root@ken1 ~]# docker rm $(docker ps -aq)
[root@ken1 ~]# docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap



 

第五步:浏览器查看

可以发现两个主机已经自动注册上去了

portainer管理另一台主机docker docker跨主机通信_IP_02

 

 创建overlay网络

 

在host1中创建网络ov_ken



[root@ken1 ~]# docker network create -d overlay ov_ken
25979f1d44c12362f3866295995756984468060c10479288835d64588f27fa3b
[root@ken1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
e36545bfeb92        bridge              bridge              local
43b0997b8ce5        host                host                local
36d9a3277b19        none                null                local
25979f1d44c1        ov_ken              overlay             global



注意到 ov_ken 的 SCOPE 为 global,而其他网络为 local。在 host2 上查看存在的网络:

portainer管理另一台主机docker docker跨主机通信_docker_03

host2 上也能看到 ov_ken。这是因为创建 ov_ken 时 host1 将 overlay 网络信息存入了 consul,host2 从 consul 读取到了新网络的数据。之后 ov_ken 的任何变化都会同步到 host1 和 host2。

 

docker network inspect 查看 ov_ken 的详细信息:

portainer管理另一台主机docker docker跨主机通信_docker_04

 

 docker 自动为 ov_ken分配的 IP 空间为 10.0.0.0/24。

 

在overlay中运行容器

 

行一个 busybox 容器并连接到 ov_ken:



[root@ken1 ~]# docker run -itd --name b1 --network ov_ken busybox
fc6454145e12c1c1a494914e3c71d4a9c7c7da0d6ed888e4b6d2afe80379e000



 

访问外网测试

可以发现容器访问外网是没有问题的

外网想要访问主机可以通过端口映射



[root@ken1 ~]# docker exec b1 ping -c 2 www.baidu.com
PING www.baidu.com (61.135.169.125): 56 data bytes
64 bytes from 61.135.169.125: seq=0 ttl=54 time=25.144 ms
64 bytes from 61.135.169.125: seq=1 ttl=54 time=24.358 ms

--- www.baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 24.358/24.751/25.144 ms



 

跨主机连通

 

在 host2 中运行容器 b2



[root@ken3 ~]# docker run -itd --name b2 --network ov_ken busybox



 

查看b2的网络

b2的ip为10.0.0.3



[root@ken3 ~]# docker exec b2 ip r
default via 172.18.0.1 dev eth1 
10.0.0.0/24 dev eth0 scope link  src 10.0.0.3 
172.18.0.0/16 dev eth1 scope link  src 172.18.0.2



 

测试b1能否ping通b2



[root@ken1 ~]# docker exec b1 ping -c 2 10.0.0.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=10.344 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=1.694 ms

--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.694/6.019/10.344 ms

[root@ken1 ~]# docker exec b1 ping -c 2 b2
PING b2 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=2.916 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=1.950 ms

--- b2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.950/2.433/2.916 ms



可见 overlay 网络中的容器可以直接通信,同时 docker 也实现了 DNS 服务。

 

overlay网络隔离

 

不同的 overlay 网络是相互隔离的。我们创建第二个 overlay 网络 ov_ken2 并运行容器 b



[root@ken1 ~]# docker network create -d overlay ov_ken2
2008b1f950566f105a567ea422ef2269b1db226de58b2d8d8350a91106542d27

[root@ken1 ~]# docker run -itd --name b3 --network ov_ken2 busybox
9f6b4166169ebe5993f8533c57dbf0089fe539a09607d94d3d9442990c8393ea



 

查看b3分配到的IP

可以发现是10.0.1.2



[root@ken1 ~]# docker exec b3 ip r
default via 172.18.0.1 dev eth1 
10.0.1.0/24 dev eth0 scope link  src 10.0.1.2 
172.18.0.0/16 dev eth1 scope link  src 172.18.0.3



 

现在尝试ping b2



[root@ken1 ~]# docker exec b3 ping -c 2 10.0.0.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes

--- 10.0.0.3 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss



ping 失败,可见不同 overlay 网络之间是隔离的。

 

如果要实现 b3 与 b2 通信,可以将 b3 也连接到 ov_ken。



[root@ken1 ~]# docker network connect ov_ken b3

[root@ken1 ~]# docker exec b3 ip r
default via 172.18.0.1 dev eth1 
10.0.0.0/24 dev eth2 scope link  src 10.0.0.4 
10.0.1.0/24 dev eth0 scope link  src 10.0.1.2 
172.18.0.0/16 dev eth1 scope link  src 172.18.0.3



可以发现现在的b3有了一个eth2的网卡,ip地址为10.0.0.4

 

现在再来测试一下b3能否ping通b2



[root@ken1 ~]# docker exec b3 ping -c 2 10.0.0.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=7.145 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=2.769 ms

--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 2.769/4.957/7.145 ms



可以发现现在可以ping通了

 

weave网络

 

weave 不依赖分布式数据库(例如 etcd 和 consul)交换网络信息,每个主机上只需运行 weave 组件就能建立起跨主机容器网络。接下来在 host1 和 host2 上部署 weave 并实践 weave 的各项特性。

 

安装部署weave

 

weave 安装非常简单,在 host1 和 host2 上执行如下命令:



[root@ken1 ~]# curl -L git.io/weave -o /usr/local/bin/weave
[root@ken1 ~]# chmod a+x /usr/local/bin/weave



 

host1中启动weave

 

在 host1 中执行 weave launch 命令,启动 weave 相关服务。weave 的所有组件都是以容器方式运行的,weave 会从 docker hub 下载最新的 image 并启动容器。



[root@ken1 ~]# weave launch



 

查看启动的容器



[root@ken1 ~]# docker ps
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS                          PORTS               NAMES
7a59dcccd908        weaveworks/weave:2.5.1   "/home/weave/weaver …"   37 seconds ago      Restarting (1) 36 seconds ago                       weave



weave 运行了一个容器:

weave 是主程序,负责建立 weave 网络,收发数据 ,提供 DNS 服务等。

 

host1中启动容器

 



[root@ken ~]# eval $(weave env)
[root@ken ~]# docker run --name b1 --rm -itd busybox
e04f798321072542450ca800601e980d206e00e8850b1b1ce16f4cba8ce94a32



首先执行 eval $(weave env) 很重要,其作用是将后续的 docker 命令发给 weave proxy 处理。如果要恢复之前的环境,可执行 eval $(weave env --restore)

 

查看一下当前容器 bbox1 的网络配置:

portainer管理另一台主机docker docker跨主机通信_docker_05

b1 有两个网络接口 eth0 和 ethwe,其中 eth0 连接的是默认 bridge 网络,即网桥 docker0。

分配的 IP 10.32.0.1/12  ethwe 与 weave 相关

 

再运行一个容器 b2。



[root@ken ~]# docker run --name b2 --rm -itd busybox
abf55e9dead051e0e87afd524243fbb7c3a6985042d0d924a829a91399c76ade



 

测试b1与b2的连通性(相同主机host!)

可以发现b1与b2不仅可以互相ping通,还可以解析



[root@ken ~]# docker exec b1 ping -c 2 b2
PING b2 (10.32.0.2): 56 data bytes
64 bytes from 10.32.0.2: seq=0 ttl=64 time=0.141 ms
64 bytes from 10.32.0.2: seq=1 ttl=64 time=0.282 ms

--- b2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.141/0.211/0.282 ms



 

weave跨主机通信

 

第一步:首先在host2 执行如下命令



[root@ken3 ~]# weave launch 172.20.10.2
e69a5bec56eb8d5471b139ce2c3080f2681e44c7bf582ef798c017d83107e3ac



这里必须指定 host1 的 IP 172.20.10.2,这样 host1 和 host2 才能加入到同一个 weave 网络。

 

第二步:开启路由转发功能



echo "1">/proc/sys/net/ipv4/ip_forward



 

第三步:启动容器



[root@ken3 ~]# eval $(weave env)
[root@ken3 ~]# docker run --name b3 --rm -itd busybox
4c86439dd59d545eb73e796709872cd51e4953503e63c2a0ff4549b2eb3ce8fa



 

第四步:测试容器连接性

可以发现主机host2上运行的b3容器可以与host1主机上面运行的b1和b2容器进行通信,而且可以解压主机名。



[root@ken3 ~]# docker exec b3 ping -c 2 b1
PING b1 (10.32.0.1): 56 data bytes
64 bytes from 10.32.0.1: seq=0 ttl=64 time=7.157 ms
64 bytes from 10.32.0.1: seq=1 ttl=64 time=1.021 ms

--- b1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.021/4.089/7.157 ms
[root@ken3 ~]# docker exec b3 ping -c 2 b2
PING b2 (10.32.0.2): 56 data bytes
64 bytes from 10.32.0.2: seq=0 ttl=64 time=8.116 ms
64 bytes from 10.32.0.2: seq=1 ttl=64 time=1.960 ms

--- b2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 1.960/5.038/8.116 ms



 

weave网络隔离

 

默认配置下,weave 使用一个大 subnet(例如 10.32.0.0/12),所有主机的容器都从这个地址空间中分配 IP,因为同属一个 subnet,容器可以直接通信。如果要实现网络隔离,可以通过环境变量 WEAVE_CIDR 为容器分配不同 subnet 的 IP,举例如下:



[root@ken3 ~]# docker run -e WEAVE_CIDR=net:10.32.2.0/24 --rm -itd busybox
98bc93412af210e08b4be7a9456e40749ca4a0672f3ebbc5a5f616ab0b4d3b50



 

这里 WEAVE_CIDR=net:10.32.2.0/24 的作用是使容器分配到 IP 10.32.2.2。由于 10.32.0.0/12 与 10.32.2.0/24 位于不同的 subnet,所以无法 ping 到 b2。



[root@ken3 ~]# docker exec 98bc93412af ping -c 2 b2
PING b2 (10.32.0.2): 56 data bytes

--- b2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss



 

除了 subnet,我们还可以直接为容器分配特定的 IP:



[root@ken3 ~]# docker run -e WEAVE_CIDR=ip:10.32.6.6/24 -it busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    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
36: eth0@if37: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
38: ethwe@if39: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1376 qdisc noqueue 
    link/ether ae:e0:cd:41:ac:f5 brd ff:ff:ff:ff:ff:ff
    inet 10.32.6.6/24 brd 10.32.6.255 scope global ethwe
       valid_lft forever preferred_lft forever
/ # ping -c 2 b2
PING b2 (10.32.0.2): 56 data bytes

--- b2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss



 

weave与外网的连通性

 

weave 是一个私有的 VxLAN 网络(容器可以访问外网),默认与外部网络隔离。外部网络如何才能访问到 weave 中的容器呢?

答案是:

  1. 首先将主机加入到 weave 网络。
  2. 然后把主机当作访问 weave 网络的网关。

 

第一步:要将主机加入到 weave,执行 weave expose



[root@ken3 ~]# weave expose
10.32.2.129



 

第二步:查看IP

这个 IP 10.32.2.129 会被配置到 host2 的 weave 网桥上。

portainer管理另一台主机docker docker跨主机通信_3c_06

 

第三步:测试连通性

ping同一主机的b3



[root@ken3 ~]# ping -c 2 10.44.0.0
PING 10.44.0.0 (10.44.0.0) 56(84) bytes of data.
64 bytes from 10.44.0.0: icmp_seq=1 ttl=64 time=0.327 ms
64 bytes from 10.44.0.0: icmp_seq=2 ttl=64 time=0.068 ms

--- 10.44.0.0 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.068/0.197/0.327/0.130 ms



 

ping host1中的b1



[root@ken3 ~]# ping -c 2 10.32.0.1
PING 10.32.0.1 (10.32.0.1) 56(84) bytes of data.
64 bytes from 10.32.0.1: icmp_seq=1 ttl=64 time=28.4 ms
64 bytes from 10.32.0.1: icmp_seq=2 ttl=64 time=0.975 ms

--- 10.32.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.975/14.708/28.442/13.734 ms



 

接下来要让其他非 weave 主机访问到 bbox1 和 bbox3,只需将网关指向 host1。例如在 172.20.10.9上添加如下路由:



[root@host2 ~]# ip route add 10.32.0.0/12 via 172.20.10.2

[root@host2 ~]# ping -c 2 10.32.0.1
PING 10.32.0.1 (10.32.0.1) 56(84) bytes of data.
64 bytes from 10.32.0.1: icmp_seq=1 ttl=63 time=1.45 ms
64 bytes from 10.32.0.1: icmp_seq=2 ttl=63 time=2.25 ms

--- 10.32.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.450/1.852/2.254/0.402 ms



 

10.32.0.0/12 是 weave 网络使用的默认 subnet,如果此地址空间与现有 IP 冲突,可以通过 --ipalloc-range 分配特定的 subnet。

weave launch --ipalloc-range 10.2.0.0/16

不过请确保所有 host 都使用相同的 subnet。