Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络,本章重点讨论前一种linux
Docker 安装时会自动在 host 上建立三个网络,咱们可用 docker network ls
命令查看:web
下面咱们分别讨论它们。docker
故名思议,none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其余任何网卡。容器建立时,能够经过 --network=none
指定使用 none 网络。安全
咱们不由会问,这样一个封闭的网络有什么用呢?网络
其实还真有应用场景。封闭意味着隔离,一些对安全性要求高而且不须要联网的应用可使用 none 网络。app
好比某个容器的惟一用途是生成随机密码,就能够放到 none 网络中避免密码被窃取。oop
固然大部分容器是须要网络的,咱们接着看 host 网络。性能
链接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 彻底同样。能够经过 --network=host
指定使用 host 网络。ui
在容器中能够看到 host 的全部网卡,而且连 hostname 也是 host 的。host 网络的使用场景又是什么呢?spa
直接使用 Docker host 的网络最大的好处就是性能,若是容器对网络传输效率有较高要求,则能够选择 host 网络。固然不便之处就是牺牲一些灵活性,好比要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。
Docker host 的另外一个用途是让容器能够直接配置 host 网路。好比某些跨 host 的网络解决方案,其自己也是以容器方式运行的,这些方案须要对网络进行配置,好比管理 iptables,你们将会在后面进阶技术章节看到。
bridge 网络
Docker 安装时会建立一个 命名为 docker0
的 linux bridge。若是不指定--network
,建立的容器默认都会挂到 docker0
上
brctl show #查看bridge网络 yum install bridge-utils
docker network inspect bridge #查看bridge 网络的详细信息
当前 docker0 上没有任何其余网络设备,咱们建立一个容器看看有什么变化。
一个新的网络接口 veth28c57df
被挂到了 docker0
上,veth28c57df
就是新建立容器的虚拟网卡。
下面看一下容器的网络配置。
容器有一个网卡 eth0@if34
。你们可能会问了,为何不是veth28c57df
呢?
实际上 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。
当前容器网络拓扑结构如图所示:
容器建立时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 能够供容器使用。
除了 none, host, bridge 这三个自动建立的网络,用户也能够根据业务须要建立 user-defined 网络,下一节咱们将详细讨论。
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 为 172.22.16.2。
到目前为止,容器的 IP 都是 docker 自动从 subnet 中分配,咱们可否指定一个静态 IP 呢?
答案是:能够,经过--ip
指定。
注:只有使用 --subnet
建立的网络才能指定静态 IP。
my_net
建立时没有指定 --subnet
,若是指定静态 IP 报错以下:
好了,咱们来看看当前 docker host 的网络拓扑结构。
经过前面小节的实践,当前 docker host 的网络拓扑结构以下图所示,今天咱们将讨论这几个容器之间的连通性。
两个 busybox 容器都挂在 my_net2 上,应该可以互通,咱们验证一下:
可见同一网络中的容器、网关之间都是能够通讯的。
my_net2
与默认 bridge 网络能通讯吗?
从拓扑图可知,两个网络属于不一样的网桥,应该不能通讯,咱们经过实验验证一下,让 busybox 容器 ping httpd 容器:
确实 ping 不通,符合预期。
“等等!不一样的网络若是加上路由应该就能够通讯了吧?”我已经听到有读者在建议了。
这是一个很是很是好的想法。
确实,若是 host 上对每一个网络的都有一条路由,同时操做系统上打开了 ip forwarding,host 就成了一个路由器,挂接在不一样网桥上的网络就可以相互通讯。下面咱们来看看 docker host 满不知足这些条件呢?
ip r
查看 host 上的路由表:
# ip r
......
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.22.16.0/24 dev br-5d863e9f78b6 proto kernel scope link src 172.22.16.1
......
172.17.0.0/16 和 172.22.16.0/24 两个网络的路由都定义好了。再看看 ip forwarding:
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
ip forwarding 也已经启用了。
条件都知足,为何不能通行呢?
咱们还得看看 iptables:
# iptables-save
......
-A DOCKER-ISOLATION -i br-5d863e9f78b6 -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-5d863e9f78b6 -j DROP
......
缘由就在这里了:iptables DROP 掉了网桥 docker0 与 br-5d863e9f78b6 之间双向的流量。
从规则的命名 DOCKER-ISOLATION
可知 docker 在设计上就是要隔离不一样的 netwrok。
那么接下来的问题是:怎样才能让 busybox 与 httpd 通讯呢?
答案是:为 httpd 容器添加一块 net_my2 的网卡。这个能够经过docker network connect
命令实现。
咱们在 httpd 容器中查看一下网络配置:
容器中增长了一个网卡 eth1,分配了 my_net2 的 IP 172.22.16.3。如今 busybox 应该可以访问 httpd 了,验证一下:
busybox 可以 ping 到 httpd,而且能够访问 httpd 的 web 服务。当前网络结构如图所示:
下一节咱们讨论容器间通讯的三种方式。
容器之间可经过 IP,Docker DNS Server 或 joined 容器三种方式通讯
从上一节的例子能够得出这样一个结论:两个容器要能通讯,必需要有属于同一个网络的网卡。
知足这个条件后,容器就能够经过 IP 交互了。具体作法是在容器建立时经过 --network
指定相应的网络,或者经过 docker network connect
将现有容器加入到指定网络。可参考上一节 httpd 和 busybox 的例子,这里再也不赘述。
经过 IP 访问容器虽然知足了通讯的需求,但仍是不够灵活。由于咱们在部署应用以前可能没法肯定 IP,部署以后再指定要访问的 IP 会比较麻烦。对于这个问题,能够经过 docker 自带的 DNS 服务解决。
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器能够直接经过“容器名”通讯。方法很简单,只要在启动时用 --name
为容器命名就能够了。
下面启动两个容器 bbox1 和 bbox2:
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 的。下面验证一下:
建立 bbox3 和 bbox4,均链接到 bridge 网络。
docker run -it --name=bbox3 busybox
docker run -it --name=bbox4 busybox
bbox4 没法 ping 到 bbox3。
joined 容器是另外一种实现容器间通讯的方式。
joined 容器很是特别,它可使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间能够经过 127.0.0.1 直接通讯。请看下面的例子:
先建立一个 httpd 容器,名字为 web1。
docker run -d -it --name=web1 httpd 而后建立 busybox 容器并经过 --network=container:web1
指定 jointed 容器为 web1:
请注意 busybox 容器中的网络配置信息,下面咱们查看一下 web1 的网络:
看!busybox 和 web1 的网卡 mac 地址与 IP 彻底同样,它们共享了相同的网络栈。busybox 能够直接用 127.0.0.1 访问 web1 的 http 服务。
joined 容器很是适合如下场景:
不一样容器中的程序但愿经过 loopback 高效快速地通讯,好比 web server 与 app server。
但愿监控其余容器的网络流量,好比运行在独立容器中的网络监控程序。
容器之间的通讯咱们已经搞清楚了,接下来要考虑的是容器如何与外部世界通讯?这将是下一节的主题。