三种默认的网络模式、容器间的通信方式、容器与外部网络互连

第 5 章 Docker 网络
5.1 none 和 host 网络的适用场景
Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络,本章将主要讨论前一种,即单个 host 上的容器网络。
Docker 安装时会自动在 host 上创建三个网络,可以用docker network ls
命令查看:

5.1.1 none 网络
顾名思义,none 网络就是什么都没有的网络。挂在 none 网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过--network=none
指定使用 none 网络:

这种封闭的网络同时也意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。
5.1.2 host 网络
连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过--network=host
指定使用 host 网络:

在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。
Docker host 的另一个用途是让容器可以直接配置 host 网络。比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables。
5.2 默认的 bridge 网络
Docker 安装时会创建一个名为 docker0
的 Linux bridge。如果不指定--network
,那么创建的容器默认都会挂到docker0
上:

当前docker0
上没有任何其他网络设备,下面创建一个容器看看有什么变化:

一个新的网络接口veth28c57df
被挂到了docker0
上,veth28c57df
就是新创建容器的虚拟网卡。之后来看容器的网络配置:

容器有一个网卡eth0@if34
。实际上eth0@if34
和veth28c57df
是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头eth0@if34
在容器中,另一头veth28c57df
挂在网桥docker0
上,其效果就是将eth0@if34
也挂在了docker0
上。
我们还可以看到,eth0@if34
已经配置了 IP 172.17.0.2
。通过docker network inspect bridge
查看 bridge 网络的配置信息:

易知,bridge 网络配置的 subnet 就是172.17.0.0/16
,并且网关是172.17.0.1
。这个网关正是docker0
的 IP 地址:

最后,当前容器网络拓扑结构如下图所示:

容器创建时,docker 会自动从172.17.0.0/16
中分配一个 IP,这里使用 16 位的掩码可以保证有足够多的 IP 地址可供容器使用。
5.3 user-defined 自定义容器网络
除了 none, host, bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络。
Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。overlay 和 macvlan 用于创建跨主机的网络,后面的章节将单独讨论。
可通过 bridge 驱动创建类似前面默认的 bridge 网络:

查看一下当前 host 的网络结构变化:

可以看到新增了一个网桥br-eaed97dc9a77
,这里eaed97dc9a77
就是新建 bridge 网络my_net
的短ID。
执行docker network inspect
查看一下my_net
的配置信息:

这里172.18.0.0/16
是 Docker 自动分配的 IP 网段。
也可以自己指定 IP 网段,只需在创建网段时指定--subnet
和--gateway
参数:

这里创建了新的 bridge 网络my_net2
,网段为172.22.16.0/24
,网关为172.22.16.1
。与之前一样,网关在my_net2
对应的网桥br-5d863e9f78b6
上:

容器要使用新的网络,需要在启动时通过--network
指定:

到目前为止,容器的 IP 都是 Docker 自动从 subnet 中分配的。除此之外,我们还可以通过--ip
参数来指定容器使用静态 IP 地址:

只有使用
--subnet
创建的网络才能指定静态 IP,否则 Docker 将会报错。
当前 docker host 的网络拓扑如下:

5.4 理解容器之间的连通性
推荐阅读理解容器之间的连通性 - 每天5分钟玩转 Docker 容器技术(34)
5.5 容器间通信的三种方式
容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信。
5.5.1 IP 通信
两个容器要能通信,必须要有属于同一个网络的网卡。满足这个条件后,容器就可以通过 IP 交互了。具体做法是在容器创建时通过--network
指定相应的网络,或者通过docker network connect
将现有容器加入到指定网络。
5.5.2 Docker DNS Server
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名通信:
docker run -it --network=my_net2 --name=bbox1 busybox
docker run -it --network=my_net2 --name=bbox2 busybox
然后,bbox2
就可以直接 ping 到bbox1
了:

使用 docker DNS 有个限制:只能在 user-defined 网络中使用,默认的 bridge 网络是无法使用 DNS 的。
5.5.3 joined 容器
joined 容器是另一种实现容器间通信的方式。
它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过127.0.0.1
直接通信。
joined 容器非常适合以下场景:
- 不同容器中的程序希望通过 loopback 高效快速地通信,比如 web server 与 app server。
- 希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。
5.6 容器如何访问外部世界
容器默认就能访问外网(容器网络以外的网络),这是通过网桥的 NAT(网络地址转换)实现的。
具体参考容器如何访问外部世界?- 每天5分钟玩转 Docker 容器技术(36)
5.7 外部世界如何访问容器
外部网络通过端口映射访问到容器内部。
Docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p
参数映射端口:

容器启动后,可通过docker ps
或docker port
查看动态映射到 host 的端口。
除了映射动态端口,也可以在-p
中指定映射到 host 某个特定端口,例如可将容器的 80 端口映射到 host 的 8080 端口:

每一个映射的端口,host 都会启动一个docker-proxy
进程来处理访问容器的流量:

以0.0.0.0:32773->80/tcp
为例:

docker-proxy
监听 host 的 32773 端口- 当 curl 访问
10.0.2.15:32773
时,docker-proxy
转发给容器172.17.0.2:80
- httpd 容器响应请求并返回结果