1. 前言

为了加深对docker网络的了解,本文将详细的介绍Docker网络模型

2. Docker 网络模型

  • bridge :默认的网络模型,通常创建容器时,使用 -p 选项则默认使用的是bridge网络模型。
  • host : 使用此网络模型,将删除容器与宿主机的net 名称空间的隔离,容器将直接占用宿主机端口,使用宿主机网络。
  • overlay : 叠加网络,通常为swram群集使用,创建后将借助两个Docker宿主机现存网络之上模拟出一条网络通道,使不同宿主机之间的容器可以通过叠加网络的网段相互通信。
  • macvlan : 此网络模型会将mac地址分配给容器,使其在网络中模拟出真实的物理设备。docker守护进程会通过mac地址,将流量路由至此容器。

2.1 网络模型的选择

  • 当一台宿主机运行多个容器时,为了保证容器间端口不冲突,建议使用bridge网络单独分配端口。
  • 当宿主机只为某一个容器工作,或者你不希望将容器的网络进行隔离,可以使用Host网络。
  • 当你Docker中采用集群模式时,或者你希望两台宿主机之间的容器之间通过IP地址即可访问时,可以使用overlay网络。
  • 当你想让容器在其他设备监控下像物理主机时,可以使用macvlan网络。

3. 使用 bridge 网络

如果使用过KVM虚拟化的小伙伴一定很清楚,KVM通过桥接网络来建立于虚拟机的通信。
桥接网络,顾名思义,是指在本地物理网卡模拟出一个虚拟网卡,这个虚拟网卡的流量实际上是通过物理网卡进行转发,所以称之为桥接网络。

docker默认网络则是桥接模式,会在宿主机新建出一个虚拟网卡:docker0
默认启动的容器,如果没有指定网络,将自动连接到此网络,并分配出一个IP地址

除此之外,用户也可以自己定义桥接网络,自定义的桥接网络会优于docker默认的桥接网络

3.1 用户自定义的bridge网络与默认的bridge区别

  • 使用用户自定义的网络docker将会提供DNS解析

我们可以测试一下:

1
2
3
4
5
6
7
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker run --rm --name test1 -it busybox /bin/sh
/ # ping test1
ping: bad address 'test1'
/ # ping test2
ping: bad address 'test2'
/ #</pre>

可以看到,ping本地以及ping其他主机并没有办法解析
下面我们自己创建网络测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker network create my-net
[root@localhost ~]# docker run --rm --network=my-net --name test1 -it busybox /bin/sh
/ # ping test1
PING test1 (172.21.0.2): 56 data bytes
64 bytes from 172.21.0.2: seq=0 ttl=64 time=0.133 ms
^C
--- test1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.133/0.133/0.133 ms
/ # ping test2
PING test2 (172.21.0.3): 56 data bytes
64 bytes from 172.21.0.3: seq=0 ttl=64 time=0.165 ms
64 bytes from 172.21.0.3: seq=1 ttl=64 time=0.193 ms
^C
--- test2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.165/0.179/0.193 ms
/ #</pre>
  • 用户可以随时的将容器加入或断开自定义网络

断开

1
2
3
4
5
6
7
8
9
10
11
12
13
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker network disconnect my-net test1
[root@localhost ~]#
# 进入容器查看
/ # ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:10 errors:0 dropped:0 overruns:0 frame:0
TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:618 (618.0 B) TX bytes:618 (618.0 B)
/ #</pre>

加入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker network connect my-net test1
[root@localhost ~]#
/ # ifconfig
eth1 Link encap:Ethernet HWaddr 02:42:AC:15:00:02
inet addr:172.21.0.2 Bcast:172.21.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:10 errors:0 dropped:0 overruns:0 frame:0
TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:618 (618.0 B) TX bytes:618 (618.0 B)</pre>

启动容器指定容器网络,并暴露端口

1
2
3
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker run --name my-nginx --network my-net -p 8888:80 nginx
Unable to find image 'nginx:latest' locally</pre>

3.1.1 总结

  1. Docker如果不通过 –network 指定网络,将使用默认网络。
  2. Docker 官方并不建议使用默认的网络。
  3. 使用默认网络的话,容器内部只能使用IP地址进行通信,不能使用容器名进行解析,而我们自定义的可以使用容器名解析。
  4. 我们可以随时的将容器从自定义的网络中断开和重连,断开后容器将没有IP地址
  5. 容器启动时,我们可以通过-p 或者 -P 来映射容器内的端口

4. 使用host网络

  • Host网络 除去了原本容器与宿主机之前的网络隔离,从而可以直接占用宿主机的网络
  • 使用Host网络,将无需映射端口,容器内监听的端口在宿主机直接使用netstat 命令即可看到
1
2
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker run --rm -d --network host --name my_nginx nginx
</pre>

5.1 创建overlay的前提条件

提前说明,此部分很重要,有很多人创建玩overlay网络后,发现并没有时间应该有的效果,并且检查也一头雾水,所以需要详细的了解overlay的工作过程。

overlay网络进行通信时,需要占用3个网络端口

  • 用于群集管理通信的TCP端口2377
  • TCP和UDP端口7946,用于节点之间的通信
  • UDP端口4789,用于overlay网络流量,如果此端口没有监听,或者防火墙阻拦,overlay网络将无法做到跨宿主机通信
  • 在创建overlay网络前,需要激活swarm集群管理,分别在两台节点上执行, docker swarm init 以及 docker swarm join

这两个都将创建默认的ingress 叠加网络,默认情况下,群集服务会使用该 叠加网络。即使您从未计划使用群体服务,也需要这样做。之后,您可以创建其他用户定义的叠加网络。

  1. 在manager, 初始化集群。如果主机只有一个网络接口,则该–advertise-addr标志是可选的
1
2
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker swarm init --advertise-addr=&lt;IP-ADDRESS-OF-MANAGER>
</pre>

保存下输出内容,方便以后加入集群时调用

  1. 在上worker-1,加入群体。如果主机只有一个网络接口,则该–advertise-addr标志是可选的。
1
2
3
4
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
docker swarm join --token &lt;TOKEN> \
--advertise-addr &lt;IP-ADDRESS-OF-WORKER-1> \
&lt;IP-ADDRESS-OF-MANAGER>:2377</pre>

3. 在上manager,列出所有节点。此命令只能由管理员执行。

1
2
3
4
5
6
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
b3n0ugrgd2kc8s7pj0l028rt2 * localhost.localdomain Ready Active Leader 18.09.6
uklmc8wr08vls7kehg0z6y8m9 localhost.localdomain Ready Active 18.09.6
[root@localhost ~]#</pre>

您还可以使用该–filter标志按角色进行过滤:

1
2
3
4
5
6
7
8
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker node ls --filter role=manager
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
b3n0ugrgd2kc8s7pj0l028rt2 * localhost.localdomain Ready Active Leader 18.09.6
[root@localhost ~]# docker node ls --filter role=worker
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
uklmc8wr08vls7kehg0z6y8m9 localhost.localdomain Ready Active 18.09.6
[root@localhost ~]#</pre>

创建服务

1
2
3
4
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker network create -d overlay nginx-net
au4hqfopux7jzebsf7w1rr8i7
[root@localhost ~]#</pre>
  1. 在manager上,创建一个新的叠加网络,称为 nginx-net:

注意,创建集群的网络只需要在管理节点中创建即可,当其他节点被管理节点运行服务时,会自动创建该叠加网络。

  1. 在上manager,创建一个连接到的5副本Nginx服务nginx-net。该服务将向外部世界发布80端口。所有服务任务容器都可以彼此通信,而无需打开任何端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker service create --name my-nginx -p 80:80 --replicas=5 --network nginx-net nginx
Error response from daemon: rpc error: code = InvalidArgument desc = port '80' is already in use by service 'flask_web' (dplob4wwnrrgxf8rjmec13vn9) as an ingress port
[root@localhost ~]# docker service create --name my-nginx -p 899:80 --replicas=5 --network nginx-net nginx
af9iufr1fff3j2qwbruxef2q6
overall progress: 5 out of 5 tasks
1/5: running [==================================================>]
2/5: running [==================================================>]
3/5: running [==================================================>]
4/5: running [==================================================>]
5/5: running [==================================================>]
verify: Service converged
[root@localhost ~]#</pre>

因为调试其他服务,80端口已经被占用,所以改成899端口

默认的发布模式ingress,当你不指定所使用mode的–publish标志,意味着如果你浏览到端口80上manager,worker-1或者worker-2即使没有,你会被连接到端口80上的5项服务任务之一,任务当前正在您浏览到的节点上运行。如果要使用host模式发布端口 ,则可以将其添加mode=host到–publish输出中。但是,在这种情况下,也应该使用–mode global代替–replicas=5,因为只有一个服务任务可以绑定给定节点上的给定端口。

  1. 运行docker service ls以监视服务启动情况,并更改不同主机的html页面进行验证,观察跨主机负载均衡是否生效
  2. 创建其他容器可连接的overlay网络,并检查overlay网络是否生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
[root@localhost ~]# docker network create -d overlay --attachable my-attachable-overlay
j7qbhhc3t4o0w7a73lplimxyo
[root@localhost ~]#
# 如果需要允许独立的容器使用此网络,则需要在创建网络时,使用 --attachable 选项
# node1
[root@localhost ~]# docker run -it --name test1 --rm --network my-attachable-overlay busybox /bin/sh
/ #
# node2
[root@localhost ~]# docker run -it --name test2 --rm --network my-attachable-overlay busybox /bin/sh
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
Digest: sha256:fe301db49df08c384001ed752dff6d52b4305a73a7f608f21528048e8a08b51e
Status: Downloaded newer image for busybox:latest
/ # ping test1
PING test1 (10.0.3.2): 56 data bytes
64 bytes from 10.0.3.2: seq=0 ttl=64 time=1.717 ms
64 bytes from 10.0.3.2: seq=1 ttl=64 time=0.948 ms
64 bytes from 10.0.3.2: seq=2 ttl=64 time=0.896 ms
# 我们可以看到,容器是可以互相通信的</pre>