使用 bridge 网络
在计算机网络中,一个 bridge(网桥)是一个链路层设备,负责在不同的网段之间转发信息。 bridge 可以是真实的硬件设备也可以是由宿主机底层提供的软件模拟设备。
在 Docker 中,bridge 网络使用了软件虚拟的网桥,让连接到同一个桥上的容器能互相通讯的同时,也隔离了没有连接到同一个桥上的容器。Docker 的 bridge 网络驱动会自动在宿主机上安装好防火墙规则,隔离开不同网桥之间的直接通讯。
bridge 网络适用于同一个 Docker daemon 宿主机下的容器,对于不同主机之间的通讯,你可以自己在操作系统层面设置路由规则,或者是用 overlay network。
当你启动 Docker 的时候,一个默认的bridge网络 (也叫 bridge
) 会自动创建(参考下文),跟着所有后面启动的容器除非自己有指定,否则都会自动连接到上面。你也可以创建一个自定义的 bridge 网络。用户定义的 bridge 网络要优于默认的 bridge 网络。
用户定义的 bridge 跟默认 bridge 的区别
-
用户定义的 bridge 提供容器之间的自动 DNS 解析。
在默认 bridge 下的容器只能通过 IP 地址访问彼此,除非使用 --link 选项,此选项已经标记为遗弃状态。而在用户定义的 bridge 网络,容器间可以通过主机名或者别名来解析彼此的地址。
想象有一个应用栈,包含了一个网页前端跟一个数据库后端。假设你分别命名两种容器为
web
和db
,无论应用栈在哪个 Docker 宿主机跑着,web
容器都能够连接db
容器里面的数据库。如果你把这种应用栈跑在默认的 bridge 网络上,你就要手动给两种不同的容器加上链接(用遗弃的
--link
标志)。这些链接在链接的两个方向都要进行设置,可以想象超过两个以上的容器的情况会变得多么复杂。此外,你也可以修改容器里面的/etc/hosts
文件,但这会产生难以调试的问题。 -
用户定义的 bridge 提供了更好的隔离。
所有不用
--network
标志的容器都会连接到默认的 bridge 网络上。这是有风险的,这种情况下不相关的 应用栈/服务/容器 能够直接通讯。而用户定义的 bridge 网络就能够提供限定范围的网络,只有连接到各自的 bridge 上的容器才能互通。
-
容器能在用户定义的 bridge 上即时插拔。
在容器的生命周期内,你可以在用户定义的 bridge 上即时插拔它们。而你要在默认的 bridge 上删除某个容器时,就需要先停掉容器,然后再重新用不同的网络选项去创建它。
-
用户定义的网络提供了能灵活配置的 bridge。
如果你的容器使用的是默认的 bridge,你也可以配置,但所有的容器都会使用同一个设置,比如 MTU 大小和
iptables
规则。此外,配置默认 bridge 网络是在 Docker 之外进行的,这就需要 Docker 来一次重启。而用户定义得 bridge 使用
docker network create
来创建和配置。如果有几组不同的应用需要用到不同的网络,你可以分别给他们创建和配置不同的 bridge。 -
在默认 bridge 上所有的容器共享环境变量。
一开始,在两个不同的容器之间共享环境变量,只有用 --link 标志一个办法。这样的变量在用户定义的 bridge 上是共享不了的。但是,我们有更好的办法去共享环境变量。举例:
-
复数个容器可以用 Docker volume (卷),把共享信息挂载到容器里的某个文件或者路径下。
-
复数个容器可以用
docker-compose
一起启动,然后可以在 compose file 里面定义共享的变量。 -
你可以用 swarm 编排工具来管理容器,而不是单独启动,然后可以用上 swarm 的 secrets 和 configs 共享。
-
所有连接到同一个用户定义的 bridge 都有效地对彼此暴露所有端口。要想让在不同的网络上的容器或者非 Docker 主机能访问到某个端口,需要将此端口用 -p
或者--publish
标志给公开出来。
管理用户定义 bridge
用 docker network create
命令来创建一个用户定义 bridge 网络。
$ docker network create my-net
你可以指定子网掩码、IP地址范围、默认网关跟其他的选项。详情参考 docker network create 文档或者 docker network create --help
的输出。
使用 docker network rm
命令来移除一个用户定义 bridge 网络。如果上面有连着的容器,请先参考下文断连它们。
$ docker network rm my-net
这中间发生了什么?
当你创建或移除用户定义的 bridge 或在用户定义的 bridge 上连接或断开某些容器时,Docker 使用操作系统特有的工具来管理底层网络设施(比如在Linux上添加或移除网桥设备或配置
iptables
规则)。这些细节应看作是实现细节,让 Docker 来管理你的用户定义网络就好。
将某个容器连接到一个用户定义的 bridge 上
当你创建一个新的容器时,可以指定一个或者多个 --network
标志。此例子把一个 Nginx 容器连在了 my-net
网络上。同时把80端口公开在了宿主机的8080端口上,这样外部的客户端能够访问到这个端口。任何同样连接到 my-net
网络的其他容器,都能够访问到 my-nginx
容器的所有端口,反之亦然。
$ docker create --name my-nginx \--network my-net \--publish 8080:80 \nginx:latest
要将一个正在运行的容器连接到一个已有的用户定义 bridge,可使用 docker network connect
命令。下面的命令将一个已经跑起来的 my-nginx
容器连到了一个已有的 my-net
网络:
$ docker network connect my-net my-nginx
将某个容器从用户定义的 bridge 上断开
要将一个正在运行的容器从用户定义断开,可使用 docker network disconnect
命令。下面的命令将 my-nginx
容器从 my-net
网中断掉。
$ docker network disconnect my-net my-nginx
使用 IPv6
如果你在 Docker 容器中需要IPv6,那么在创建任意的 IPv6 网络跟给容器分配 IPv6 地址之前, 需要在 Docker daemon 层面 打开对应的选项 并重载配置。
在创建你的网络时,可以用 --ipv6
标志来开启 IPv6。你不能选择性地禁用默认 bridge 网络的 IPv6 支持。
启用从 Docker 容器到外部的转发功能
默认情况下,连接到默认 bridge 的容器的网络流量,不会转发到外部。要想启用转发,你需要修改两处设定。它们不是 Docker 命令,而是在 Docker 的宿主机的内核上生效的。
-
配置 Linux 内核,允许 IP 转发(IP forwarding)。
$ sysctl net.ipv4.conf.all.forwarding=1
-
修改
iptables
的FORWARD
策略,从DROP
修改为ACCEPT
。$ sudo iptables -P FORWARD ACCEPT
这些设置重启之后就没了,所以你可能需要把它们加到启动脚本里。
使用默认的 bridge 网络
默认的 bridge
网络可以看作是一个 Docker 快被遗弃的细节,不建议在生产环境中使用。配置它属于手动操作,而且它有技术缺陷(参考上面提到的两种 bridge 的不同)。
将某个容器连接到默认 bridge 上
如果你不用 --network
标志指定网络,而又指定了网络驱动,你的容器就会连接到默认 bridge
网络。连接到默认的 bridge
网络的容器是可以通讯的,但只能用 IP 地址来进行。除非你用准备遗弃的 --link 标志来连接他们。
配置默认的 bridge 网络
要配置默认的 bridge 网络, 你需要在 daemon.json
中指定选项。下面是一个指定了几个选项的 daemon.json
示例。只指定你需要改动的设置。
{"bip": "192.168.1.1/24","fixed-cidr": "192.168.1.0/25","fixed-cidr-v6": "2001:db8::/64","mtu": 1500,"default-gateway": "192.168.1.254","default-gateway-v6": "2001:db8:abcd::89","dns": ["10.20.1.2","10.20.1.3"]
}
重启 Docker 让这些改动生效。
在默认 bridge 网络上使用 IPv6
如果你将 Docker 配置了支持IPv6(参见上面的使用IPv6),默认 bridge 网络也会自动配置为IPv6。与用户定义的 bridge 不同,你不能在默认网桥上选择性地禁用IPv6。
接下来
- 通关 独立的网络教程
- 学习 容器视角的网络
- 学习 overlay 网络
- 学习 Macvlan 网络