一、理解容器之间的连通性

上次创建了两个 busybox 容器都挂在 my_net2 上,应该能够互通,我们验证一下:

docker restart 两个容器 docker两个容器通信_docker

 

docker restart 两个容器 docker两个容器通信_DNS_02

可见同一网络中的容器、网关之间都是可以通信的,但是注意,my_net2 与默认 bridge 网络能通信吗?两个网络属于不同的网桥,应该不能通信,我们通过实验验证一下:

docker restart 两个容器 docker两个容器通信_DNS_03

确实 ping 不通,符合预期

确实,如果 host 上对每个网络的都有一条路由,同时操作系统上打开了 ip forwarding,host 就成了一个路由器,挂接在不同网桥上的网络就能够相互通信。下面我们来看看 docker host 满不满足这些条件呢?

通过命令ip r 查看 host 上的路由表

docker restart 两个容器 docker两个容器通信_IP_04

再通过命令sysctl net.ipv4.ip_forward看看 ip forwarding:

docker restart 两个容器 docker两个容器通信_docker_05

 ip forwarding 也已经启用了,条件都满足,为什么不能通行呢?

我们还得通过命令iptables-save看看 iptables:

docker restart 两个容器 docker两个容器通信_DNS_06

 原因就在这里了:iptables DROP 掉了网桥 docker0 与br-9006f40ea8d1之间双向的流量;

从规则的命名 DOCKER-ISOLATION 可知 docker 在设计上就是要隔离不同的 netwrok。

那么接下来的问题是:怎样才能让 busybox 与 httpd 通信呢?

答案是:为 httpd 容器添加一块 net_my2 的网卡。这个可以通过docker network connect 命令实现;

docker restart 两个容器 docker两个容器通信_IP_07

 进入到httpd容器中,通过ip a查看网络配置:

docker restart 两个容器 docker两个容器通信_docker_08

 容器中增加了一个网卡 eth1,分配了 my_net2 的 IP 172.22.16.3,现在 busybox 应该能够访问 httpd 了,验证一下:

docker restart 两个容器 docker两个容器通信_IP_09

busybox 能够 ping 到 httpd,并且可以访问 httpd 的 web 服务;

二、容器间通信的三种方式

容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信;

IP 通信 两个容器要能通信,必须要有属于同一个网络的网卡,满足这个条件后,容器就可以通过 IP 交互了;

做法是在容器创建时通过 --network 指定相应的网络,或通过 docker network connect 将现有容器加入到指定网络;

Docker DNS Server

通过 IP 访问容器虽然满足了通信的需求,但还是不够灵活,因为我们在部署应用之前可能无法确定 IP,部署之后再指定要访问的 IP 会比较麻烦,此时,可以通过 docker 自带的 DNS 服务解决;

Docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过“容器名”通信,方法很简单,只要在启动时用 --name 为容器命名就可以了,下面启动两个容器 bbox1 和 bbox2:

通过命令启动两个容器,使用网络my_net2:

docker run -it --network=my_net2 --name=bbox1 busybox
docker run -it --network=my_net2 --name=bbox2 busybox

 具体结果如下所示:

docker restart 两个容器 docker两个容器通信_docker restart 两个容器_10

 

docker restart 两个容器 docker两个容器通信_IP_11

 使用 docker DNS 有个限制:只能在 user-defined 网络中使用,也就是说,默认的 bridge 网络是无法使用 DNS 的;

下面验证一下:

创建 bbox3 和 bbox4,均连接到 bridge 网络:

docker run -it --name=bbox3 busybox
docker run -it --name=bbox4 busybox

具体结果如下所示:

docker restart 两个容器 docker两个容器通信_docker restart 两个容器_12

joined 容器

joined 容器是另一种实现容器间通信的方式,joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信,请看下面的例子:

先创建一个 httpd 容器,名字为 web1:

docker restart 两个容器 docker两个容器通信_IP_13

然后创建 busybox 容器并通过 --network=container:web1 指定 jointed 容器为 web1

docker restart 两个容器 docker两个容器通信_docker restart 两个容器_14

注意 busybox 容器中的网络配置信息,下面我们查看一下 web1 的网络:

docker restart 两个容器 docker两个容器通信_docker_15

busybox 和 web1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接用 127.0.0.1 访问 web1 的 http 服务:

docker restart 两个容器 docker两个容器通信_IP_16

joined 容器非常适合以下场景:

  1. 不同容器中的程序希望通过 loopback 高效快速地通信,比如 web server 与 app server。
  2. 希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。