赞
踩
本系列的教程涉及独立 Docker 容器的联网问题。关于与swarm服务的联网,请参阅 Swarm服务的联网教程。如果想了解更多 Docker 网络的理论知识,参考概述。
本主题包括三个不同的教程。你可以在Linux、Windows或Mac上运行它们中的每一个,但对于后两种操作系统,你需要在其他地方运行第二个Docker主机。
下面的例子里, 使用默认的 bridge 网络演示了如何使用Docker 为你自动设置的默认 bridge 网络。但这个网络并不是生产环境下的最佳选择。
下面使用用户定义的 bridge 网络展示了如何创建和使用自己的自定义 bridge 网络,以连接运行在同一Docker主机上的容器。在生产环境中运行的独立容器建议使用。
虽然 overlay 网络 通常用于swarm服务,但你也可以将它用于独立的容器。 overlay 网络教程 会有介绍。
在这个例子中,你在同一台 Docker 宿主机上启动两个不同的 alpine
容器,并做一些测试,以了解它们如何相互通信。你需要安装并运行 Docker。
打开一个终端窗口。在你做其他事情之前,列出当前的网络。如果你从未在这个Docker daemon 上添加过网络或初始化过swarm,你应该看到以下内容。你可能会看到不同的网络,但你至少应该看到这些(网络ID会不同):
- $ docker network ls
-
- NETWORK ID NAME DRIVER SCOPE
- 17e324f45964 bridge bridge local
- 6ed54d316334 host host local
- 7092879f2cc8 none null local
默认的 bridge
网络、 host
跟 none
在上面列了出来。后两者不是 “完整” 的网络,而是用来启动直接连接到 Docker daemon 宿主机协议栈,又或者是启动没有网络设备的容器的。本教程将把两个容器连接到 bridge
网络。
启动两个运行 ash
的 alpine
容器,ash
是 Alpine 的默认 shell 而不是 bash
。 -dit
标志意味着启动 detached 方式的、(即后台运行)、交互式的(能够向其中输入文字)和TTY(所以你可以看到输入和输出)容器。由于你是以 detached 方式启动的,所以你不会立即与容器连接。相反,容器的ID将被打印出来。因为你没有指定任何 --network
标志,所以容器会连接到默认的 bridge
网络。
- $ docker run -dit --name alpine1 alpine ash
-
- $ docker run -dit --name alpine2 alpine ash
检查两个启动的容器:
- $ docker container ls
-
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 602dbf1edc81 alpine "ash" 4 seconds ago Up 3 seconds alpine2
- da33b7aa74b0 alpine "ash" 17 seconds ago Up 16 seconds alpine1
Inspect 一下 bridge
网络,看看连接的容器:
- $ docker network inspect bridge
-
- [
- {
- "Name": "bridge",
- "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
- "Created": "2017-06-22T20:27:43.826654485Z",
- "Scope": "local",
- "Driver": "bridge",
- "EnableIPv6": false,
- "IPAM": {
- "Driver": "default",
- "Options": null,
- "Config": [
- {
- "Subnet": "172.17.0.0/16",
- "Gateway": "172.17.0.1"
- }
- ]
- },
- "Internal": false,
- "Attachable": false,
- "Containers": {
- "602dbf1edc81813304b6cf0a647e65333dc6fe6ee6ed572dc0f686a3307c6a2c": {
- "Name": "alpine2",
- "EndpointID": "03b6aafb7ca4d7e531e292901b43719c0e34cc7eef565b38a6bf84acf50f38cd",
- "MacAddress": "02:42:ac:11:00:03",
- "IPv4Address": "172.17.0.3/16",
- "IPv6Address": ""
- },
- "da33b7aa74b0bf3bda3ebd502d404320ca112a268aafe05b4851d1e3312ed168": {
- "Name": "alpine1",
- "EndpointID": "46c044a645d6afc42ddd7857d19e9dcfb89ad790afb5c239a35ac0af5e8a5bc5",
- "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
网络的信息,包括 Docker 宿主机和 bridge
网络之间的网关的IP地址(172.17.0.1
)。在 Containers
的 key下,列出了每个连接的容器,以及关于其IP地址的信息(172.17.0.2
为 alpine1
, 172.17.0.3
为 alpine2
)。
容器们都在后台跑着。用 docker attach
命令连接 alpine1
.
- $ docker attach alpine1
-
- / #
提示符变成了 #
,表示你是容器内的 root
用户。使用 ip addr show
命令来显示 alpine1
的网络接口,如下:
- # ip addr show
-
- 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
- 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
- 27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
- 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
第一个接口时环回(loopback)设备,暂时忽略它。注意第二个IP是 172.17.0.2
的接口,此IP跟上面显示的 alpine1
信息是一致的。
在 alpine1
中,确保你能通过ping baidu.com
连接到互联网。 -c 2
标志限制了该命令的两次 ping
尝试。
- # ping -c 2 baidu.com
- PING baidu.com (110.242.68.66) 56(84) bytes of data.
- 64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=49 time=42.8 ms
- 64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=2 ttl=49 time=42.6 ms
-
- --- baidu.com ping statistics ---
- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms
- rtt min/avg/max/mdev = 42.699/42.777/42.855/0.078 ms
现在常识ping第二个容器。首先,用它的 IP 地址 172.17.0.3
去ping:
- # ping -c 2 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.086 ms
- 64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.094 ms
-
- --- 172.17.0.3 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.086/0.090/0.094 ms
成功。接着,尝试用 alpine2
的容器名去 ping,这会失败。
- # ping -c 2 alpine2
-
- ping: bad address 'alpine2'
通过使用 detach 序列, CTRL
+ p
CTRL
+ q
(按住 CTRL
键,输入 p
,然后输入q
),在不停止 alpine1
的情况下从其 detach 。如果你愿意,可以连接到 alpine2
,在那里重复步骤4、5和6,用 alpine1
代替 alpine2
。
停掉并删除两个容器。
- $ docker container stop alpine1 alpine2
- $ docker container rm alpine1 alpine2
记住,默认的 bridge
网络不建议用在生产环境里。想了解更多的用户定义 bridge 网络,请接着往下看。
在这个例子中,我们再次启动了两个 alpine
容器,但不同的是,我们把它们连接到我们已经创建的一个叫做 alpine-net
网络的用户定义的网络。这些容器没有连接到默认的 bridge
网络。然后我们启动第三个 alpine
容器,它连接到 bridge
网络,但没有连接到 alpine-net
,第四个 alpine
容器则同时连接到两个网络。
创建 alpine-net
网络。你不需要用 --driver bridge
标志来指定使用的是 bridge 驱动,因为 bridge 就是默认的方式。但例子里还是展示了如何指定它。
$ docker network create --driver bridge alpine-net
列出 Docker 的网络:
- $ docker network ls
-
- NETWORK ID NAME DRIVER SCOPE
- e9261a8c9a19 alpine-net bridge local
- 17e324f45964 bridge bridge local
- 6ed54d316334 host host local
- 7092879f2cc8 none null local
Inspect 一下 alpine-net
网络。下面展示了它的 IP 地址以及还未有容器连接他的现状:
- $ docker network inspect alpine-net
-
- [
- {
- "Name": "alpine-net",
- "Id": "e9261a8c9a19eabf2bf1488bf5f208b99b1608f330cff585c273d39481c9b0ec",
- "Created": "2017-09-25T21:38:12.620046142Z",
- "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,
- "Containers": {},
- "Options": {},
- "Labels": {}
- }
- ]

注意这网络的网关为 172.18.0.1
, 相对的,默认的 bridge 网络的网关是 172.17.0.1。
确切的IP地址可能在你的机器上会有所不同。
创建你的容器。注意 --network
标志。在运行 docker run
命令的时候只能连接到一个网络。所以你需要在之后使用 docker network connect
,将 alpine4
也连接到 bridge
网络。
- $ docker run -dit --name alpine1 --network alpine-net alpine ash
-
- $ docker run -dit --name alpine2 --network alpine-net alpine ash
-
- $ docker run -dit --name alpine3 alpine ash
-
- $ docker run -dit --name alpine4 --network alpine-net alpine ash
-
- $ docker network connect bridge alpine4
验证一下所有的容器都已经在运行:
- $ docker container ls
-
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 156849ccd902 alpine "ash" 41 seconds ago Up 41 seconds alpine4
- fa1340b8d83e alpine "ash" 51 seconds ago Up 51 seconds alpine3
- a535d969081e alpine "ash" About a minute ago Up About a minute alpine2
- 0a02c449a6e9 alpine "ash" About a minute ago Up About a minute alpine1
再次 Inspect 一下 bridge
网络跟 alpine-net
网络:
- $ docker network inspect bridge
-
- [
- {
- "Name": "bridge",
- "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
- "Created": "2017-06-22T20:27:43.826654485Z",
- "Scope": "local",
- "Driver": "bridge",
- "EnableIPv6": false,
- "IPAM": {
- "Driver": "default",
- "Options": null,
- "Config": [
- {
- "Subnet": "172.17.0.0/16",
- "Gateway": "172.17.0.1"
- }
- ]
- },
- "Internal": false,
- "Attachable": false,
- "Containers": {
- "156849ccd902b812b7d17f05d2d81532ccebe5bf788c9a79de63e12bb92fc621": {
- "Name": "alpine4",
- "EndpointID": "7277c5183f0da5148b33d05f329371fce7befc5282d2619cfb23690b2adf467d",
- "MacAddress": "02:42:ac:11:00:03",
- "IPv4Address": "172.17.0.3/16",
- "IPv6Address": ""
- },
- "fa1340b8d83eef5497166951184ad3691eb48678a3664608ec448a687b047c53": {
- "Name": "alpine3",
- "EndpointID": "5ae767367dcbebc712c02d49556285e888819d4da6b69d88cd1b0d52a83af95f",
- "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": {}
- }
- ]

容器 alpine3
跟 alpine4
了解到了 bridge
网络
- $ docker network inspect alpine-net
-
- [
- {
- "Name": "alpine-net",
- "Id": "e9261a8c9a19eabf2bf1488bf5f208b99b1608f330cff585c273d39481c9b0ec",
- "Created": "2017-09-25T21:38:12.620046142Z",
- "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,
- "Containers": {
- "0a02c449a6e9a15113c51ab2681d72749548fb9f78fae4493e3b2e4e74199c4a": {
- "Name": "alpine1",
- "EndpointID": "c83621678eff9628f4e2d52baf82c49f974c36c05cba152db4c131e8e7a64673",
- "MacAddress": "02:42:ac:12:00:02",
- "IPv4Address": "172.18.0.2/16",
- "IPv6Address": ""
- },
- "156849ccd902b812b7d17f05d2d81532ccebe5bf788c9a79de63e12bb92fc621": {
- "Name": "alpine4",
- "EndpointID": "058bc6a5e9272b532ef9a6ea6d7f3db4c37527ae2625d1cd1421580fd0731954",
- "MacAddress": "02:42:ac:12:00:04",
- "IPv4Address": "172.18.0.4/16",
- "IPv6Address": ""
- },
- "a535d969081e003a149be8917631215616d9401edcb4d35d53f00e75ea1db653": {
- "Name": "alpine2",
- "EndpointID": "198f3141ccf2e7dba67bce358d7b71a07c5488e3867d8b7ad55a4c695ebb8740",
- "MacAddress": "02:42:ac:12:00:03",
- "IPv4Address": "172.18.0.3/16",
- "IPv6Address": ""
- }
- },
- "Options": {},
- "Labels": {}
- }
- ]

容器 alpine1
, alpine2
跟 alpine4
连接到了 alpine-net
网络。
在像 alpine-net
这样的用户定义的网络上,容器之间不仅可以通过IP地址进行通信,而且还可以将容器名称解析为IP地址。这种能力被称为自动服务发现(automatic service discovery),类似于局域网的主机名通讯。让我们连接到 alpine1
并测试一下。 alpine1
应该能够将 alpine2
和 alpine4
(以及 alpine1
本身)解析为IP地址。
- $ docker container attach alpine1
-
- # ping -c 2 alpine2
-
- PING alpine2 (172.18.0.3): 56 data bytes
- 64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.085 ms
- 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.090 ms
-
- --- alpine2 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.085/0.087/0.090 ms
-
- # ping -c 2 alpine4
-
- PING alpine4 (172.18.0.4): 56 data bytes
- 64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.076 ms
- 64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.091 ms
-
- --- alpine4 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.076/0.083/0.091 ms
-
- # ping -c 2 alpine1
-
- PING alpine1 (172.18.0.2): 56 data bytes
- 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.026 ms
- 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.054 ms
-
- --- alpine1 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.026/0.040/0.054 ms

你应该从 alpine1
连接不到 alpine3
,因为 alpine3
没有连接到alpine-net
网络。
- # ping -c 2 alpine3
-
- ping: bad address 'alpine3'
不止如此,你也不能从 alpine1
用IP地址连接到 alpine3
。看回 docker network inspect
有关于 bridge
网络的输出,找到 alpine3
的IP地址 172.17.0.2
然后尝试连接它:
- # ping -c 2 172.17.0.2
-
- PING 172.17.0.2 (172.17.0.2): 56 data bytes
-
- --- 172.17.0.2 ping statistics ---
- 2 packets transmitted, 0 packets received, 100% packet loss
用 Detach 操作序列从 alpine1
断开, CTRL
+ p
CTRL
+ q
(按下 CTRL
然后依次键入 p
、 q
)。
记得,alpine4
同时连接了默认的 bridge
跟 alpine-net,
理论上它能连接其他所有的容器。然而对于 alpine3
,它需要用IP地址来定位,并不能用容器名。连接它并跑一下测试。
- $ docker container attach alpine4
-
- # ping -c 2 alpine1
-
- PING alpine1 (172.18.0.2): 56 data bytes
- 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.074 ms
- 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.082 ms
-
- --- alpine1 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.074/0.078/0.082 ms
-
- # ping -c 2 alpine2
-
- PING alpine2 (172.18.0.3): 56 data bytes
- 64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.075 ms
- 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.080 ms
-
- --- alpine2 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.075/0.077/0.080 ms
-
- # ping -c 2 alpine3
- ping: bad address 'alpine3'
-
- # ping -c 2 172.17.0.2
-
- PING 172.17.0.2 (172.17.0.2): 56 data bytes
- 64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.089 ms
- 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
-
- --- 172.17.0.2 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.075/0.082/0.089 ms
-
- # ping -c 2 alpine4
-
- PING alpine4 (172.18.0.4): 56 data bytes
- 64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.033 ms
- 64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.064 ms
-
- --- alpine4 ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.033/0.048/0.064 ms

作为最后的测试,确保你的容器都能通过ping baidu.com
连接到互联网。既然你已经连接到了 alpine4
,那么我们从那里开始尝试。接下来,从 alpine4
分离并连接到 alpine3
(它只连接到 bridge
网络)并再次尝试。最后,连接到 alpine1
(它只连接到 alpine-net
网络),再试一次。
- # ping -c 2 baidu.com
-
- PING baidu.com (110.242.68.66): 56 data bytes
- 64 bytes from 110.242.68.66: seq=0 ttl=41 time=9.778 ms
- 64 bytes from 110.242.68.66: seq=1 ttl=41 time=9.634 ms
-
- --- baidu.com ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 9.634/9.706/9.778 ms
-
- CTRL+p CTRL+q
-
- $ docker container attach alpine3
-
- # ping -c 2 baidu.com
-
- PING baidu.com (110.242.68.66): 56 data bytes
- 64 bytes from 110.242.68.66: seq=0 ttl=41 time=9.706 ms
- 64 bytes from 110.242.68.66: seq=1 ttl=41 time=9.851 ms
-
- --- baidu.com ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 9.706/9.778/9.851 ms
-
- CTRL+p CTRL+q
-
- $ docker container attach alpine1
-
- # ping -c 2 baidu.com
-
- PING baidu.com (110.242.68.66): 56 data bytes
- 64 bytes from 110.242.68.66: seq=0 ttl=41 time=9.606 ms
- 64 bytes from 110.242.68.66: seq=1 ttl=41 time=9.603 ms
-
- --- baidu.com ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 9.603/9.604/9.606 ms
-
- CTRL+p CTRL+q

停止并删除所有容器以及 alpine-net
网络。
- $ docker container stop alpine1 alpine2 alpine3 alpine4
-
- $ docker container rm alpine1 alpine2 alpine3 alpine4
-
- $ docker network rm alpine-net
现在你已经完成了独立容器的网络教程,你可能想跑一下其他的:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。