使用桥接网络
在网络方面,桥接网络是链路层设备,它在网络段之间转发流量。桥接网络可以是硬件设备或在主机内核中运行的软件设备。
就Docker而言,桥接网络使用软件桥接器,该软件桥接器允许连接到同一桥接网络的容器进行通信,同时提供与未连接到该桥接网络的容器的隔离。Docker桥驱动程序会自动在主机中安装规则,以便不同桥接网络上的容器无法直接相互通信。
桥接网络适用于在同一个 Docker守护程序主机上运行的容器。对于在不同Docker守护程序主机上运行的容器之间的通信,您可以在操作系统级别管理路由,也可以使用覆盖网络。
启动Docker时,会自动创建默认桥接网络(也称为bridge
),并且除非另行指定,否则新启动的容器将连接到该网络。您还可以创建用户定义的自定义桥接网络。用户定义的桥接网络 优于默认bridge
网络。
用户定义的桥接器可在容器化应用程序之间提供更好的隔离和互操作性
连接到同一个用户定义的桥接网络的容器会自动将所有端口相互暴露,而不会向外界显示任何端口。这使得容器化应用程序可以轻松地相互通信,而不会意外地打开对外界的访问。
想象一下具有Web前端和数据库后端的应用程序。外部世界需要访问Web前端(可能在端口80上),但只有后端本身需要访问数据库主机和端口。使用用户定义的桥接网络,只需要打开Web端口,并且数据库应用程序不需要打开任何端口,因为Web前端可以通过用户定义的桥接网络访问它。
如果在默认桥接网络上运行相同的应用程序堆栈,则需要打开Web端口和数据库端口,并使用 每个的标记-p
或--publish
标记。这意味着Docker主机需要通过其他方式阻止对数据库端口的访问。
用户定义的桥接器在容器之间提供自动DNS解析
默认桥接网络上的容器只能通过IP地址相互访问,除非您使用被认为是过期的的--link
选项。在用户定义的桥接网络上,容器可以通过名称或别名相互解析。
想象一下与前一点相同的应用程序,具有Web前端和数据库后端。如果你调用容器中的web或者db,Web容器可以在连接到数据库容器,无论托管在哪个docker 主机,应用程序都会在堆栈上运行。
如果在默认桥接网络上运行相同的应用程序堆栈,则需要在容器之间手动创建链接(使用过期的--link
标志)。这些链接需要在两个方向上创建,因此您可以看到这对于需要通信的两个以上容器而言变得复杂。或者,您可以操作/etc/hosts
容器中的文件,但这会产生难以调试的问题。
容器可以在运行中与用户定义的网络连接和分离
在容器的生命周期中,您可以动态地将其与用户定义的网络连接或断开连接。要从默认桥接网络中删除容器,您需要停止容器并使用不同的网络选项重新创建容器。
每个用户定义的网络都会创建一个可配置的桥接网络
如果容器使用默认桥接网络,则可以对其进行配置,但所有容器都使用相同的设置,例如MTU和iptables
规则。此外,配置默认桥接网络发生在Docker本身之外,并且需要重新启动Docker。
使用创建和配置用户定义的桥接网络 docker network create
。如果不同的应用程序组具有不同的网络要求,则可以在创建时单独配置每个用户定义的桥接网络。
默认桥接网络上的容器共享环境变量
-
最初,在两个容器之间共享环境变量的唯一方法是使用
--link
标志链接它们。用户定义的网络无法实现这种类型的变量共享。但是,有更好的方法来共享环境变量。一些想法:
连接到同一用户定义的桥接网络的容器有效地将所有端口相互暴露。对于可以访问不同网络上的容器或非Docker主机的端口,必须使用or 标志发布该端口。-p
--publish
管理用户定义的桥
使用此docker network create
命令可以创建用户定义的桥接网络。
$ docker network create my-net
您可以指定子网,IP地址范围,网关和其他选项。有关详细信息,请参阅 docker network create reference或输出docker network create --help
。
使用此docker network rm
命令删除用户定义的桥接网络。如果容器当前已连接到网络, 请先断开它们 。
$ docker network disconnect my-net my-nginx
$ docker network rm my-net
当您创建或删除用户定义的桥接网络或从用户定义的桥接网络连接或断开容器时,Docker使用特定操作系统的工具来管理底层网络基础结构(例如iptables
在Linux上添加或删除桥接网络设备或配置规则) )。这些细节应视为实施细节。让Docker为您管理用户定义的网络。
将容器连接到用户定义的桥
创建新容器时,可以指定一个或多个--network
标志。此示例将Nginx容器连接到my-net
网络。它还将容器中的端口80发布到Docker宿主机上的端口8080,因此外部客户端可以访问该端口。连接到my-net
网络的任何其他容器都可以访问my-nginx
容器上的所有端口,反之亦然。
[root@benjamincloud ~]# docker create --name my-nginx \ > --network my-net \ > --publish 8080:80 \ > nginx:latest
要将正在运行的容器连接到现有的用户定义的桥,请使用该 docker network connect
命令。以下命令将已在运行的my-nginx
容器连接 到已存在的my-net
网络:
[root@benjamincloud ~]# docker network connect my-net my-nginx
断开容器与用户定义的桥接器的连接
要断开正在运行的容器与用户定义的桥接器的连接,请使用该docker network disconnect
命令。以下命令将my-nginx
容器与my-net
网络断开连接。
[root@benjamincloud ~]# docker network disconnect my-net my-nginx
使用IPv6
如果需要对Docker容器的IPv6支持,则需要 在创建任何IPv6网络或分配容器IPv6地址之前,在Docker守护程序上启用该选项并重新加载其配置。
创建网络时,可以指定--ipv6
标志以启用IPv6。您无法在默认bridge
网络上有选择地禁用IPv6支持。
启用从Docker容器转发到外部世界
默认情况下,来自连接到默认桥接网络的容器的流量 不会转发到外部世界。要启用转发,您需要更改两个设置。这些不是Docker命令,它们会影响Docker主机的内核。
-
配置Linux内核以允许IP转发。
$ sysctl net.ipv4.conf.all.forwarding=1
-
将策略的
iptables
FORWARD
策略更改DROP
为ACCEPT
。$ sudo iptables -P FORWARD ACCEPT
这些设置在重新启动时不会持续存在,因此您可能需要将它们添加到启动脚本中,或者相应的配置文件中。
使用默认桥接网络
默认bridge
网络被视为Docker的遗留细节,不建议用于生产用途。配置它是一种手动操作,它有 技术缺点。
将容器连接到默认桥接网络
如果未使用该--network
标志指定网络,并且指定了网络驱动程序,则默认情况下容器将连接到默认bridge
网络。连接到默认bridge
网络的容器只能通过IP地址进行通信,除非它们使用旧--link
标志进行链接 。
配置默认桥接网络
要配置默认bridge
网络,请在中指定选项daemon.json
。这是一个daemon.json
指定了几个选项的示例。仅指定您需要自定义的设置。
{ "bip": "192.168.1.5/24", "fixed-cidr": "192.168.1.5/25", "fixed-cidr-v6": "2001:db8::/64", "mtu": 1500, "default-gateway": "10.20.1.1", "default-gateway-v6": "2001:db8:abcd::89", "dns": ["10.20.1.2","10.20.1.3"] }
重新启动Docker以使更改生效。
将IPv6与默认桥接网络一起使用
如果将Docker配置为支持IPv6(请参阅使用IPv6),则还会自动为IPv6配置默认桥接网络。与用户定义的桥接网络不同,您无法在默认桥接网络上有选择地禁用IPv6。
与独立容器联网
预计阅读时间: 18分钟
这一系列教程涉及独立Docker容器的网络连接。有关群组服务的网络,请参阅 使用群组服务进行网络连接。如果您需要了解有关Docker网络的更多信息,请参阅概述。
这里仅介绍linux下的教程
-
使用默认桥接网络演示了如何使用
bridge
Docker自动为您设置的默认网络。该网络不是生产系统的最佳选择。 -
使用用户自定的网络显示如何创建和使用自己的自定义桥接网络,以连接在同一Docker主机上运行的容器。对于在生产中运行的独立容器,建议使用此选项。
尽管覆盖网络通常用于群集服务,但Docker 17.06及更高版本允许您将覆盖网络用于独立容器。
使用默认桥接网络
在此示例中,您alpine
在同一个Docker主机上启动两个不同的容器,并进行一些测试以了解它们如何相互通信。您需要安装并运行Docker。
1.打开终端窗口。在执行任何其他操作之前列出当前网络。如果您从未在此Docker守护程序上添加网络或初始化群组,那么您应该看到以下内容。您可能会看到不同的网络,但至少应该看到这些(网络ID会有所不同):
$ docker network ls NETWORK ID NAME DRIVER SCOPE 17e324f45964 bridge bridge local 6ed54d316334 host host local 7092879f2cc8 none null local
2.bridge是
默认网络, 以及host
和none
。后两者不是完全成熟的网络,但用于启动直接连接到Docker守护程序主机的网络堆栈的容器,或用于启动没有网络设备的容器。本教程将两个容器连接到bridge
网络。
启动两个alpine
容器运行ash
,这是Alpine的默认shell是ash而不是bash
。该-dit
标志意味着要首先分离容器(背景),互动(与输入到它的能力),并与TTY(这样你就可以看到输入和输出)。
由于您正在启动它,因此您不会立即连接到容器。而是打印容器的ID。由于您尚未指定任何 --network
标志,因此容器将连接到默认bridge
网络。
$ docker run -dit --name alpine1 alpine ash
$ docker run -dit --name alpine2 alpine ash
检查两个容器是否实际启动:
3.检查bridge
网络以查看连接到它的容器。
在顶部附近,bridge
列出了有关网络的信息,包括Docker主机和bridge
网络之间的网关的IP地址(172.17.0.1
)。在Containers
密钥下面,列出了每个连接的容器,以及有关其IP地址(172.17.0.2
for alpine1
和172.17.0.3
for alpine2
)的信息。
4.由于容器在后台运行。使用docker attach
命令连接到alpine1
。
[root@benjamincloud ~]# docker attach alpine1
/ #
提示符更改为#
表示您是root
容器中的用户。使用该ip addr show
命令显示alpine1
容器内的网络接口:
第一个接口是环回设备。暂时忽略它。请注意,第二个接口具有IP地址172.17.0.2
,该地址alpine1
与上一步中显示的地址相同。
5.从内部alpine1
,确保您可以通过ping连接到互联网baidu.com
。该-c 2
标志将命令限制为两次ping
尝试。
6.现在尝试ping第二个容器。首先,通过IP地址ping它 172.17.0.3
:
这成功了。接下来,尝试alpine2
按容器名称ping 容器。这将失败。
alpine1
使用分离序列分离而不停止它, CTRL
+ p 然后
q
(按住CTRL
并键入p
后跟q
)。如果你愿意,重视alpine2
并重复步骤4,5和6出现,用alpine1取代alpine2。
停止并移除两个容器。
$ docker container stop alpine1 alpine2 $ docker container rm alpine1 alpine2
请记住,bridge
不建议将默认网络用于生产。要了解用户定义的桥接网络,请继续学习 下面的内容
使用用户定义的网桥
在此示例中,我们再次启动两个alpine
容器,但将它们附加到alpine-net
我们已创建的用户定义的网络中。
这些容器根本没有连接到默认bridge
网络。然后,我们启动第三个alpine
连接到bridge
网络,但未连接到的容器alpine-net
,以及连接到两个网络的第四个alpine容器。
1.创建alpine-net
网络。您不需要该--driver bridge
标志,因为它是默认值,但此示例显示了如何指定它。
[root@benjamincloud ~]# docker network create --driver bridge alpine-net
65ddc2eff605b8efdabfb8e476af20de32b9babdd2bf12d2b3182115b03abfaf
2.列出Docker的网络:
检查alpine-net
网络。这将显示其IP地址以及没有容器连接到它的事实:
请注意,该网络的网关172.18.0.1
与其网关所在的默认网桥相对172.17.0.1
。您的系统上的确切IP地址可能有所不同。
3.创建您的四个容器。注意--network
标志。您只能在docker run
命令期间连接到一个网络,因此您需要在 docker network connect
以后连接alpine4
到bridge
网络。
[root@benjamincloud ~]# docker run -dit --name alpine1 --network alpine-net alpine ash afe579c54cf2dc7c247d2b6a2a8db98b3f7296f002a07be21aa27dd4cf8f052b [root@benjamincloud ~]# docker run -dit --name alpine2 --network alpine-net alpine ash 9a1a89cff8ea0028ea2ebb7e20b90354d4dc7475d9c716b8040e8b325cdb0f01 [root@benjamincloud ~]# docker run -dit --name alpine3 alpine ash b0c0451946a0c8929cd31da1133b812b6353a59542a00f70938f2e3b70305ef2 [root@benjamincloud ~]# docker run -dit --name alpine4 --network alpine-net alpine ash c9d91c0cf3d5d30514c649a13c772b1cefc26578b1155aaac0b79fde8c95984e
验证所有容器是否正在运行:
4.再次检查bridge
网络和alpine-net
网络:
容器alpine1
,alpine2
和alpine4
连接到 alpine-net
网络。
5. 在用户定义的网络上alpine-net
,容器不仅可以通过IP地址进行通信,
还可以将容器名称解析为IP地址。此功能称为自动服务发现。让我们连接alpine1
并测试一下。
alpine1
应该能够解析 alpine2
和alpine4
(和alpine1
本身)IP地址。
/ # ping -c2 alpine1 PING alpine1 (172.18.0.2): 56 data bytes 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.041 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.066 ms --- alpine1 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.041/0.053/0.066 ms / # ping -c2 alpine2 PING alpine2 (172.18.0.3): 56 data bytes 64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.080 ms 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.096 ms --- alpine2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.080/0.088/0.096 ms / # ping -c2 alpine4 PING alpine4 (172.18.0.4): 56 data bytes 64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.086 ms 64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.100 ms --- alpine4 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.086/0.093/0.100 ms
6.从alpine1
,您根本无法连接alpine3
,因为它不在alpine-net
网络上。
/ # ping -c2 alpine3 ping: bad address 'alpine3'
不仅如此,而且无法连接到alpine3
来自alpine1
它的IP地址的ping。回顾一下网络的docker network inspect
输出 bridge
并查找alpine3
IP地址:172.17.0.2
尝试ping它。
/ # ping -c2 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
退出容器使其在后台运行 ctrl+p 接q
7.请记住,alpine4
它连接到默认bridge
网络和alpine-net
。它应该能够到达所有其他容器。但是,您需要alpine3
通过其IP地址进行寻址。连接到它并运行测试。
/ # 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.065 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.132 ms --- alpine1 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.065/0.098/0.132 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.101 ms 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.121 ms --- alpine2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.101/0.111/0.121 ms / # ping -c 2 alpine3 ping: bad address 'alpine3' / # 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.074 ms 64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.067 ms --- alpine4 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.067/0.070/0.074 ms / # 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.124 ms 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.097 ms --- 172.17.0.2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.097/0.110/0.124 ms
8.作为最终测试,请确保您的容器都可以通过ping来连接到Internet baidu.com
。你已经附上了,alpine4
所以从那里开始尝试。
接下来,分离alpine4
并连接alpine3
(仅连接到bridge
网络),然后重试。最后,连接到alpine1
(仅连接到alpine-net
网络)并再试一次。
/ # ping -c2 baidu.com PING baidu.com (220.181.57.216): 56 data bytes 64 bytes from 220.181.57.216: seq=0 ttl=50 time=57.520 ms 64 bytes from 220.181.57.216: seq=1 ttl=50 time=57.523 ms --- baidu.com ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 57.520/57.521/57.523 ms / # read escape sequence [root@benjamincloud ~]# docker attach alpine3 / # ping -c 2 baidu.com PING baidu.com (123.125.115.110): 56 data bytes 64 bytes from 123.125.115.110: seq=0 ttl=51 time=36.863 ms 64 bytes from 123.125.115.110: seq=1 ttl=51 time=36.954 ms --- baidu.com ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 36.863/36.908/36.954 ms / # read escape sequence [root@benjamincloud ~]# docker attach alpine1 / # ping -c 2 baidu.com PING baidu.com (220.181.57.216): 56 data bytes 64 bytes from 220.181.57.216: seq=0 ttl=50 time=57.521 ms 64 bytes from 220.181.57.216: seq=1 ttl=50 time=57.547 ms --- baidu.com ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 57.521/57.534/57.547 ms
9.停止并删除所有容器和alpine-net
网络。
root@benjamincloud ~]# docker stop alpine1 alpine2 alpine3 alpine4 alpine1 alpine2 alpine3 alpine4 [root@benjamincloud ~]# docker rm alpine1 alpine2 alpine3 alpine4 alpine1 alpine2 alpine3 alpine4 [root@benjamincloud ~]# docker network rm alpine-net alpine-net