Docker网络设置

转载请注明出处:www.huamo.online
字节杭州 求贤若渴:

  1. https://job.toutiao.com/s/JXTdQaH
  2. https://job.toutiao.com/s/JXTMWW3
  3. https://job.toutiao.com/s/JXT1tpC
  4. https://job.toutiao.com/s/JXTdu6h

本文概述了Docker默认网络的配置,包含了默认创建的网络类型,以及如何创建你自己的用户定义网络。同时也描述了在一台主机或者在一个集群中创建网络所需要的资源。

如要了解在Linux主机中,Docker是如何与iptables进行交互的,可以参看Docker and iptables

默认网络

当你安装Docker时,它会自动创建3个网络。你可以使用docker network ls命令罗列出这些网络:

1
2
3
4
5
6
$ docker network ls

NETWORK ID NAME DRIVER SCOPE
b8ed42a010f8 bridge bridge local
f8c5b547c829 host host local
58ec52c671cb none null local

这3个网络內建在Docker中。当你运行一个container时,你可以使用--network标记来指定你的容器要连接哪个网络。

bridge网络代表了在所有Docker安装中都存在的docker0网络。除非你使用docker run --network=<NETWORK>命令指定了其它网络,Docker后台默认都会将container连接到这个网络。你可以在主机上使用ifconfig命令看到这个bridge网络属于主机网络栈的一部分。

1
2
3
4
5
6
7
8
9
$ ifconfig

docker0 Link encap:Ethernet HWaddr 02:42:45:cf:e9:c3
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 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:0 (0.0 B) TX bytes:0 (0.0 B)

none网络会将容器添加到一个容器特定的(container-specific)网络堆栈中。这种容器会缺少一个网络接口,即Docker不会为这个容器配置任何IP地址,该容器也无法访问外网以及其它容器。它只有一个本地回环地址,通常用来进行批处理工作。如果你进入到这个容器中并查看它的网络堆栈,你会发现验证上面的描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ docker run -it --network=none registry.cn-hangzhou.aliyuncs.com/docker/ubuntu14.04 /bin/bash

root@bd6af240c5cd:/# ping 192.168.9.63
connect: Network is unreachable

root@bd6af240c5cd:/# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

root@bd6af240c5cd:/# 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:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

备注:以上的进入方式,可以输入exit退出container

host网络则是将container添加到主机的网络堆栈中。就网络而言,主机和容器并没有隔离。比如,如果你的容器使用host网络,在80端口运行了一个web服务器,那么这个服务器就可以在主机的80端口上使用。

none网络和host网络在Docker中不能直接配置。但是,你可以配置默认的bridge网络,你自己定义的网桥网络也可以配置。

默认的bridge网络

默认的bridge网络存在于所有的Docker主机上。如果你不指定其它的网络,新的container都会自动连接到这个默认的bridge网络中。

docker network inspect命令可以返回一个网络的相关信息:

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
26
27
28
29
30
31
32
33
34
35
36
37
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b8ed42a010f8014be4788401c6dd013b59d0542d137325355c0f58720b32df07",
"Created": "2017-07-28T16:19:53.903583477+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]

运行下面2个命令启动2个busybox容器,连接到默认的bridge网络上。

1
2
3
4
5
$ docker run -itd --name=container1 busybox
a8d3bd2901a8946a0fed762f5af7ef0c3e65ae915364f7440db8253b6977277b

$ docker run -itd --name=container2 busybox
54b2d9cd0ebcb260597dca91d8e1ac6488b16b8b9a9cf34667e6f355beae4415

启动完这2个容器之后,再次查看bridge网络。2个busybox容器都已经连接上该网络。记下它们的IP地址,在你的主机上的结果,可能会与下面的例子不同。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b8ed42a010f8014be4788401c6dd013b59d0542d137325355c0f58720b32df07",
"Created": "2017-07-28T16:19:53.903583477+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"54b2d9cd0ebcb260597dca91d8e1ac6488b16b8b9a9cf34667e6f355beae4415": {
"Name": "container2",
"EndpointID": "b083c545b43a5ac1aae0f14c2caf653273c4da9abf1e2f56089d68fcf2176aea",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"a8d3bd2901a8946a0fed762f5af7ef0c3e65ae915364f7440db8253b6977277b": {
"Name": "container1",
"EndpointID": "a0e2e1d825dbc80cc3bcf6335cb53c054ddb3b57bc10a088eea605741f5de5bf",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]

连接到默认bridge网络的容器,可以通过IP地址相互通信。Docker不支持在默认bridge网络上自动服务发现。如果你想让容器可以根据容器名字解析出IP地址,你就需要使用用户定义网络而不是默认bridge网络。你也可以使用旧版的docker run --link选项将2个容器连接起来,但在大多数情况下不建议这么做。

你可以attach到一个正在运行的container中,去从容器内部观察网络情况。因为是以root身份进入的,所以命令提示符变为了#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ docker attach container1
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 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:0 (0.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:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

在容器内部,使用ping命令来测试和其它容器的IP连接情况。

1
2
3
4
5
6
7
8
9
/ # ping -w3 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.110 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.098 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.098 ms

--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.098/0.102/0.110 ms

使用cat命令查看容器的/etc/hosts文件。这里展示了容器可以识别的主机名和IP地址。

1
2
3
4
5
6
7
8
/ # cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 a8d3bd2901a8

想要从container1容器中退出来,并且让它继续保持运行状态,可以使用ctrl+pctrl+q退出。如果你有兴趣,还可以attachcontainer2中重复如上命令。

默认的docker0网桥网络支持使用端口映射,搭配上docker run --link就可以允许容器在docker0网络中交流。但是不推荐这种做法。如果可能的话,你应该使用下面的用户定义的网桥网络方式来实现。

用户定义的网络

推荐使用用户定义的桥接网络来控制哪些容器可以互相通信,并且以此启用容器名到IP地址的自动DNS解析。Docker提供了默认的网络驱动来创建这些网络。你可以创建一个新的bridge网络overlay网络MACVLAN网络。你也可以创建一个网络插件或者远程网络进行完整的个性化和控制。

你可以创建足够多的网络,并且可以让一个容器在任意时刻连接到0个或多个这些网络中。此外,你可以不需要重启容器,就能将运行中的容器连接或者脱离网络。当一个容器连接到了多个网络上,它的外部连接是由第一个非内部网络来提供的,按照词典排序。下面会更详细的描述Docker中每个内置的网络驱动。

bridge网络驱动

bridge网络是在Docker中最常用的网络类型。用户创建的bridge网络类似于默认的bridge网络,但加上了一些新的特性,并移除掉了一些旧的功能。下面的例子创建了一些bridge网络,并且让容器在这些网络中执行了一些实验。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$ docker network create --driver bridge isolated_nw
fb7bdfe0b5495fd8ed3cfd865364ec2bce9a4703fcb84209624aa975676f1b2e

$ docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "fb7bdfe0b5495fd8ed3cfd865364ec2bce9a4703fcb84209624aa975676f1b2e",
"Created": "2017-07-28T20:42:52.601980497+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

$ docker network ls
NETWORK ID NAME DRIVER SCOPE
b8ed42a010f8 bridge bridge local
f8c5b547c829 host host local
fb7bdfe0b549 isolated_nw bridge local
58ec52c671cb none null local

创建完网络后,你可以使用docker run --network=<NETWORK>选项将容器连到该网络上。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ docker run --network=isolated_nw -itd --name=container3 busybox
20f39ef9cb5dc726e9e24b598619c073094fa637f762a641179bbaccf2d6a362

$ docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "fb7bdfe0b5495fd8ed3cfd865364ec2bce9a4703fcb84209624aa975676f1b2e",
"Created": "2017-07-28T20:42:52.601980497+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"20f39ef9cb5dc726e9e24b598619c073094fa637f762a641179bbaccf2d6a362": {
"Name": "container3",
"EndpointID": "e4aaf7227a060f6a55e29a416a06cf421d2e6a340dab8fc84715adad99c9127b",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

你部署在这个网络中的容器必须和该网络处于同一个Docker宿主机上。在该网络中的容器可以直接相互通信。但是,网络本身将这些容器和外部网络隔离开。

Alt text

在一个用户定义的bridge网络中,是不支持linking的。你可以在这个网络中的容器上暴露和发布容器端口。这样可以让你从外部网络访问bridge网络中的部分容器。

Alt text

bridge网络对于在单一主机上运行一个规模相对较小的网络是非常适合的。但是,你还可以通过创建overlay网络来搭建一个更加庞大的网络环境。

docker_gwbridge网络

docker_gwbridge是一个本地bridge网络,在如下2种不同的状况下,会由Docker自动创建出来:

  • 当你初始化或者加入一个swarm时,Docker会创建出docker_gwbridge网络,并使用它在swarm节点的不同机器之间进行通信。
  • 当一个容器的所有网络都不能提供外部连接时,Docker会将该容器额外再连接到docker_gwbridge网络,以便这个容器可以连接外部网络,或者其它的swarm节点。

如果你需要一个自定义配置,可以提前创建docker_gwbridge网络,否则Docker就会按需创建。下面的这个例子使用了一些定制选项来创建docker_gwbridge网络。

1
2
3
4
5
$ docker network create --subnet 172.30.0.0/16 \
--opt com.docker.network.bridge.name=docker_gwbridge \
--opt com.docker.network.bridge.enable_icc=false \
docker_gwbridge
874f358b9cdf3a44c62ada65f2d96976ba6f7c3ad00e976c7849101a9cf0e59f

当你使用overlay网络时,docker_gwbridge网络始终存在

swarm模式中的overlay网络

你可以在一个运行swarm模式的管理者节点上创建一个overlay网络,并不需要外部的key-value存储。swarm使overlay网络仅用于需要服务的swarm中的节点。当你创建一个使用overlay网络的服务时,管理者节点会自动将overlay网络扩展到运行服务任务的节点上。

想要了解更多关于以swarm模式运行Docker引擎的知识,可以参看Swarm mode overview

下面的例子展示了如何在swarm中的一个管理节点上,创建一个网络,并将它用于一个服务:

1
2
3
4
5
6
7
8
$ docker network create \
--driver overlay \
--subnet 10.0.9.0/24 \
my-multi-host-network
400g6bwzd68jizzdx5pgyoe95

$ docker service create --replicas 2 --network my-multi-host-network --name my-web nginx
716thylsndqma81j6kkkb5aus

只有swarm服务才能连接overlay网络,独立的容器是无法连接的。关于swarm更多信息,可以看Docker swarm mode overlay network security modelAttach services to an overlay network

swarm模式的overlay网络

如果你没有以swarm模式运行Docker引擎,那么overlay网络就需要一个合法的key-value存储服务。支持的key-value存储包含Consul, Etcd, ZooKeeper (分布式存储)。在以这种方式创建网络之前,你必须安装和配置好你选中的key-value存储服务。该Docker的宿主机必须能够联网,并且服务必须能够通信。

swarm模式运行的Docker引擎,不兼容和外部的key-value存储进行网络连接。

这种使用overlay网络的方式对于大多数Docker使用者都不推荐。它可以在单一的swarm集群中使用,或者对那些在Docker之上搭建解决方案的系统开发人员有用处。在未来这种方式可能会被废弃。如果你实在需要通过这种方式使用overlay网络,看这篇指导文档

自定义网络插件

如果你需要的东西在上文中都没被提到,那么你可以使用Docker的插件基础设施,编写自己的网络驱动插件。插件会以一个单独的进程运行在执行Docker后台的主机上。使用网络插件是一个高阶话题。

网络插件遵循和其它插件一样的限制和安装规则。所有的插件都使用插件API,并且有一个围绕安装,启动中,停止中,和激活的生命周期。

当你创建并安装了一个自定义的网络驱动后,你就可以创建一个网络,通过--driver参数使用这个自定义驱动。

1
$ docker network create --driver weave mynet

你可以inspect这个网络,将容器连接上或者脱离开这个网络,甚至移除它。一个特定的插件为了使用可能会有特定的需求依赖。查看插件的文档获取特定的信息。关于编写插件更多内容,可以查看Extending DockerWriting a network driver plugin

内置的DNS服务器

Docker后台运行了一个内置的DNS服务器,为连接在同样的用户定义网络中的容器之间提供DNS解析服务,以便这些容器能够将容器名解析为IP地址。如果内置的DNS服务器无法解析一个请求,它将会转发给为容器配置的任何一个外部DNS服务器。为了在容器创建时促成这些功能,只有127.0.0.11可以访问的内置DNS服务器才会被罗列到容器的resolv.conf文件中。关于用户定义网络中的内置DNS服务器更多信息,可以参看embedded DNS server in user-defined networks

暴露和发布容器端口

在Docker网络中,有2种不同的,都直接涉及到网络端口的运行机制:暴露和发布端口。这个适用于默认的bridge网络和用户定义的bridge网络。

  • 在Dockerfile中使用EXPOSE关键字,或者在docker run命令行中使用--expose参数,你就可以暴露端口。暴露端口是一种描述哪些端口已被使用的方式,但它实际上并没有映射或者打开任何端口。暴露端口是可选的。
  • 在Dockerfile中使用PUBLISH关键字,或者在docker run命令行中使用--publish参数,你就可以发布端口。这会告诉Docker在容器的网络接口中要打开哪些端口。当一个容器端口被发布后,它会被映射到宿主机一个可用的高序端口上(端口号高于30000),除非你在运行时指定了要映射到宿主机哪个端口上。不能在Dockerfile中指定映射到宿主机哪个端口,因为没有办法可以保证,当你运行这个镜像时,这个指定的主机端口还依然可用。

下面这个例子发布了容器的80端口,并映射到主机一个随机的高序端口上(在本例中,是32768)。-d参数使得容器在后台运行,这样你就可以继续输入docker ps命令。

1
2
3
4
$ docker run -it -d -p 80 nginx

$ docker ps
64879472feea nginx "nginx -g 'daemon ..." 43 hours ago Up About a minute 443/tcp, 0.0.0.0:32768->80/tcp blissful_mclean

下一个例子指定将容器的80端口映射到宿主机上的8080端口。如果8080端口不可用则将会失败。

1
2
3
4
5
$ docker run -it -d -p 8080:80 nginx

$ docker ps

b9788c7adca3 nginx "nginx -g 'daemon ..." 43 hours ago Up 3 seconds 80/tcp, 443/tcp, 0.0.0.0:8080->80/tcp goofy_brahmagupta

在Docker支持用户定义网络之前,你可以使用Docker的--link特性来让容器将其它容器的名字解析为IP地址,并且还可以访问被链接的容器的环境变量。如果可能,你应该尽量避免使用这个古老的--link参数。

当你创建链接时,它会和使用默认bridge网络或者使用用户定义bridge网络表现不同。更多信息,可以查看在默认bridge网络中的Legacy Links,和在用户定义网络中的linking containers in user-defined networks

Docker和iptables

Linux主机使用一个叫iptables的内核模块来管理网络设备的访问,包括路由,端口转发,网络地址转换(NAT),和其它相关的操作。Docker会修改iptables的规则,比如当你启动或停止一个发布端口的容器时,当你创建或修改网络时,当你将容器加入网络时,或者其它网络相关的操作时。

关于iptables的完整讨论超出了本文范围。随时查看iptables的规则是行之有效的方法,你可以使用iptables -L命令进行查看。会罗列出很多列表,你可以只显示一个特定的列表,比如natpreroutingpostrouting,用类似这样的命令就能做到:iptables -t nat -L。要查看iptables的全面文档,可以到netflilter/iptables

一般情况下,iptables规则是由一个初始化脚本或者一个譬如firewalld的守护进程创建。规则在系统重启后会不复存在,所以脚本或者工具必须在系统启动时运行,通常是在运行级别3或者直接是网络初始化完成后运行。也可以查看Linux发行版的对应网络文档,找出合适的方法使得iptables规则持久化。

Docker动态管理iptables规则用于守护进程,也为容器,服务,网络提供支持。在Docker 17.06以及更高版本,你可以向一个名叫DOCKER-USER的新列表添加规则,这些规则将会在Docker自动创建的任何规则之前被加载。如果你需要在Docker运行之前就启用一些iptables规则,这个正好是你需要的。

参考文章

转载请注明出处:www.huamo.online