网络模式

容器的网络通信可以分为两大方面:单主机上的容器之间相互通信和跨主机的容器相互通信。

docker 的单主机通信基于 Network Namespace 实现,它可以为容器创建隔离的网络环境。docker 官方本身提供了 5 种网络模式,可以基本满足日常开发中的需求:

网络模式 说明
bridge 默认,为每个容器分配一个 IP,该 IP 会连接到 docker 宿主机的 docker0 虚拟网卡
host 容器不会拥有自己的虚拟网卡和 IP,而是直接使用宿主机的 IP 和端口
none 为容器创建独立网络名称空间,但不做任何网络配置,容器中只有 lo,用户可以就此对容器网络做任意定制
container 类似 host,容器不会拥有自己的网卡和 IP,而是和一个指定的容器共享 IP,端口等
用户自定义 在 docker 1.9 以后新增的特性,允许容器使用第三方的网络实现或者创建单独的 bridge 网络,提供网络隔离能力

在安装完成 docker 之后,会默认创建三种网络:

1
docker network ls

在运行容器时,可以使用 --network 或者 --net 指定容器使用的网络模式,如:--net bridge

同时也可以使用命令查看网络模式下有哪些容器:

1
docker network inspect bridge

bridge

bridge 模式是 docker 默认的网络模式,也是开发者最常使用的网络模式。

在这种模式下,docker 为容器创建独立的网络栈,保证容器内的进程使用独立的网络环境,实现容器与容器之间、容器与宿主机之间的网络栈隔离。

同时,通过宿主机上的 docker0 网桥,容器可以与宿主机乃至外界进行网络通信。其网络模型可以参考下图:

大致流程如下:

  1. docker 先创建一对虚拟网卡 veth pair 设备(其特征为:成对出现,数据从一个设备进入,就会从另一个设备出来,常用于实现数据通道)。

  2. docker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0。

  3. docker 将 veth pair 设备的另一端放在宿主机中,以 vethxxx 格式命名,并将它加入到 docker0 网桥中(可通过 brctl show 命令查看到)。

  4. 从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 为容器的默认网关。

测试验证

查看本机网络情况:

docker 安装完后会在宿主机创建一个 docker0 虚拟网卡。其作用类似于网络交换设备,用于实现容器之间,容器与宿主机之间甚至容器与外部主机之间的通信

docker0 网桥的 IP 一般会是 docker 配置网段的第一个 IP,且这个 IP 会作为 bridge 网络模式的容器的网关使用。

本文由于我们在配置 docker 时有另外指定了 IP 网段,所以这里看到的网段为:172.16.0.0/16 而不是 docker 默认的网段 172.17.0.0/16

新建两个容器并查看其网络情况:
1
2
3
4
5
# 终端 1
docker container run --rm -it --name demo01 busybox /bin/sh

# 终端 2
docker container run --rm -it --name demo02 busybox /bin/sh

终端 1:

终端 2:

查看此时宿主机网络情况:

宿主机多了两个 veth 网卡,这两个网卡就是和容器内部的 eth0 成对出现的,通过 bridge-utils 提供的命令也可以看到它们和 docker0 的连接关系。

查看任意容器的详细信息:
1
docker container inspect demo01

找到网络配置部分,如下图所示:

docker0 网卡的 IP 地址被作为容器的网关。

测试容器和外部连通性:

host

host 网络模式的容器可直接使用宿主机的 IP 与外界通信,同时容器内的端口也直接使用宿主机的端口,无需额外 NAT 转换。创建容器时通过参数 --net host 或者 --network host 指定。

测试示例

创建一个 Nginx 容器:
1
docker container run -d --net host --rm --name demo01 nginx

查看端口使用情况:

可以发现 80 端口已经在宿主机启动。

查看容器的网络信息:
1
docker container inspect demo01

网络模式已经成为 host,且本身没有 IP 信息。

测试端口占用信息:
1
docker container run -d --net host --rm --name demo02 nginx

容器无法运行,查看日志提示端口被占用。

none

none 网络模式是指禁用网络功能,只有 lo 本地环回接口。在创建容器时通过参数 --net none--network none 指定。

创建一个测试容器:

1
docker container run -it --rm none --name demo01 busybox /bin/sh

查看网络信息:

该容器无法和宿主机通信。

container

Container 网络模式是 docker 中一种较为特别的网络的模式。在创建容器时通过参数 –net container:已运行的容器名称|ID 或者 –network container:已运行的容器名称|ID 指定。

处于这个模式下的容器会共享一个网络栈,这样两个容器之间可以使用 localhost 高效快速通信。

Container 网络模式下,新创建的容器不会创建自己的网卡,而是和一个指定的容器共享 IP、端口范围等。除了网络方面相同之外,其它都是隔离的。

其原理大致如下:

测试示例

创建一个 bridge 网络模式的容器:
1
2
docker container run -d --name demo01 nginx
docker container inspect demo01

网络配置如下所示:

创建一个 container 网络模式的容器:
1
docker container run --rm -it --net container:demo01 --name demo02 busybox /bin/sh

网络信息如图所示:

此时删除 demo01 容器再查看 demo02 的网络:
1
docker container rm -f demo01

网卡只剩下 lo 回环网卡。

docker container run --link

可以用来连接两个容器,能够实现被链接的容器(源容器)和主动去链接的容器(接收容器)之间互相通信,并且接收容器可以获取源容器的一些数据,如环境变量。不过该方法将来可能被 docker 废弃。

自定义网络

docker 提供的默认网络模式使用起来比较简单,但是在实际应用中,为了保证应用的安全性,还是更推荐自定义网络来进行容器管理。并启用容器名称到容器 IP 的自动 DNS 解析。

从 docker 1.10 版本开始,docker daemon 实现了一个内嵌 DNS Server,但只能在用户自定义的网络模式使用,它可以实现直接使用容器的名称进行通信。

创建自定义网络

使用命令直接创建一个自定义的 bridge 网络:

1
docker network create -d bridge hello_bridge

如图所示:

-d,--driver 参数可以指定网络模式,默认不指定就是 bridge 模式。

测试网络

再次创建一个自定义网络协助测试:

1
docker network create -d bridge world_bridge

创建相关的容器:

1
2
3
4
5
6
7
8
9
# 默认网络
docker container run -it --rm --name demo01 busybox sh

# hello_bridge 网络
docker container run -it --rm --net hello_bridge --name demo02 busybox sh
docker container run -it --rm --net hello_bridge --name demo03 busybox sh

# world_bridge 网络
docker container run -it --rm --net world_bridge --name demo04 busybox sh

查看此时的网络情况

可以发现 IP 多了一些的新的网段,同一网络模式的容器处于同一网段。

此时查看本机的网络情况:

可以发现多了两个类似于 docker0 网桥的网卡,都分配了新的网段。由此可以知道,每新建一个 bridge 网络,就会在宿主机新建一个 bridge 网桥,并分配一个新的网段。

测试网络连通性:

demo01 测试:

demo01 测试

demo02 测试:

demo02 测试

剩下的就不做测试了,但是大致能得出以下结论:

  1. 不同的网络之间是隔离的,无法互相通信。
  2. 在自定义网络中,容器可以使用容器名称直接通信。

连接网络

可以将现有的容器连接到其它网络中:

1
docker network connect bridge demo04

此时查看网络情况:

可以发现限制 demo04 容器既属于默认 bridge 网络,也属于 world_bridge 网络了,这也就能和默认网络的 demo01 进行通信。

断开网络

可以连接网络自然也可以断开网络:

1
2
docker network disconnect bridge demo04
docker network disconnect world_bridge demo04

如图所示:

当连接的网络都断开之后,它的网络模式就像 none 网络了。

删除网络

对于没有使用的网络也可以进行删除:

1
docker network rm world_bridge

如图所示:

端口映射

当容器内部应用想要暴露给外部访问,就需要使用到 -P 或者 -p 参数来进行端口映射。

随机映射(-P)

使用 -P 参数能够随机映射一个 32768 以上的端口:

1
2
docker container run -d --rm -P --name demo01 nginx

此时查看容器详情:

能够看到容器内部 nginx 的 80 端口以及被随机映射到了 49153 端口,可以使用宿主机加这个随机端口在浏览器进行访问。

指定映射(-p)

使用 -p 参数能够指定映射的端口,它的格式为:监听IP:宿主机端口:容器端口监听IP::容器端口宿主机端口:容器端口

1
2
3
4
5
6
7
8
9
10
11
# 完整的监听映射
docker container run -d --rm -p "0.0.0.0:8080:80" --name demo01 nginx

# 随机映射
docker container run -d --rm -p "0.0.0.0::80" --name demo02 nginx

# 普通监听映射
docker container run -d --rm -p "8081:80" --name demo03 nginx

# 指定端口协议
docker container run -d --rm -p "0.0.0.0:8080:80/udp" --name demo04 nginx

如图所示:

如果有多个端口需要映射则指定多个 -p 即可。

也可以通过命令反查容器映射了哪些端口:

1
2
docker container port demo01
docker container port demo01 80

如图所示: