--昨夜西风凋碧树,独上高楼,望尽天涯路
Docker网络可以分为单个host上的容器网络和跨多个host的网络。
Docker安装时默认在host上创建了三个网络:
下面分别来介绍。
none网络
挂在这个网络下的容器,除了lo没有任何网卡。容器创建时,可以通过--network=none指定none网络。这是一个封闭的网络,一些对安全性要高并且不需要联网的应用可以使用none网络。
host网络
连接host网络的容器共享Docker host的网络栈,容器的网络配置与host完全一样。通过--network=host指定使用host网络:
进入容器可以看到host的所有网卡,hostname都是Docker host的。
使用host网络的最大好处就是性能,对网络传输效率要求高可以选择host网络。缺点是会牺牲灵活性:比如要考虑端口冲突问题(Docker host上已经使用的端口就不能再用了)。
Docker host的另一个用途可以让容器直接配置host网络。
bridge网络
Docker安装时会创建一个网桥docker0。这是默认使用的网桥,使用其他的网络必须用--network指定
通过brctl show查看,没有容器启动时:
启动容器,发现有个新的网络接口被挂到了docker0下,veth403af2c是该容器的虚拟网卡:
进入容器,查看网络配置:
查看容器的网络配置发现容器有一块网卡eth0@if41,和挂到docker0下面的网卡不一样。why?
实际上这两块网卡是一对veth pair。beth pair是一种成对出现的特殊网络设备,可以理解为由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if41)在容器中,另一头(veth403af2c)在网桥docker0,相当于也就是eth0@if41挂在了docker0上。
我们还可以看到IP为172.17.0.2/16,通过docker network inspect bridge查看bridge网络的配置信息,发现,bridge网络配置的subnet为172.17.0.0/16,并且网关是172.17.0.1(这个网关就是docker0)。
容器创建时,docker会自动从172.17.0.0/16中分配一个IP。
user-defined网络
除了自动创建的网络,我们也可以根据业务需要创建user-defined网络。
Docker提供三种user-defined网络驱动:bridge、overlay和macvlan。overlay和macvlan用于创建跨主机的网络,下一章讨论。
通过bridge驱动创建类似前面默认的bridge网络:
查看host的网络结构发现新增了一个网桥br-834b932f1cc,834b932f1cc是新建bridge网络my_net的短id。
产看my_net的配置信息:
172.17.0.0/16是Docker自动分配的IP网段,也可以通过--subnet和--gateway参数自己指定IP网段:
创建新的bridge网络my_net2,网段为172.22.16.0/24,网关为172.22.16.1。网关在my_net2对应的网桥br-e4fae5e8f365上:
通过--network指定使用网络my_net2:
容器自动分配到IP 172.22.16.2,我们也可以通过--ip指定一个静态ip:
注:只有使用--subnet创建的网络才能指定静态IP。
my_net创建时没有指定--subnet,如果指定,报错如下:
现在我们的环境为:
发现有两个busybox挂到my_net2上,进入到容器互相ping,发现可以互通:
得出结论:同一网络中的容器、网关之间可以通信。
现在我们尝试一下my_net2与默认bridge网络是否可以通信,进入到busybox容器pinghttpd容器:
ping不通。如何实现不同网络之间的通信呢?
解决方案:在不同的网络之间加上路由。如果host上对每个网络都有一条路由,同时操作系统上打开了ip forwarding,host就集成了一个路由器,挂接在不同网桥上的网络就能够相互通信。
首先在docker host下通过ip r查看路由表:
172.17.0.0和172.22.16.0这两个网络的路由都定义好了。在查看是否启动ip forwarding:
已启动,最后查看iptables,发现iptables DROP掉了drocker0和br-e4fae5e6f365之间的双向流量:
docker在设计上就是要隔离不同的network。现在知道原因之后,我们只需要给httpd容器添加一块my_net2的网卡,就可以实现通信:
现在busybox可以访问httpd了:
容器间通信
容器之间的通信可以通过IP、Docker DNS Server或joined容器三种方式通信。
(1)IP通信
两个容器必须要有属于同一个网络的网卡才能通过IP交互。
具体做法就是在容器创建时通过--network指定相应的网络,或者通过docker network connect将现有的容器加入到指定网络(前面已经讲过了)。
(2)Docker DNS Server
IP访问的弊端是不够灵活。在部署应用之前可能无法确定IP,部署之后在指定要访问的IP会比较麻烦,对于这个问题,可以通过docker自带的DNS服务解决。
Docker1.0版本开始,docker daemon就实现了一个内嵌的DNS Server,使容器可以直接通过容器名通信(前提是要查到默认的容器名或者是使用--name自己命名)。
先通过docker run -it --network=my_net2 --name bb1 busybox,创建一个名为bb1的容器,然后创建bb2通过名称交互:
docker DNS只能子啊user-defined网络中使用。也就是说,默认的bridge网络是无法使用DNS的。
创建一个bb3,然后进入bb4 ping bb3 ,发现ping不通:
(3)joined容器
joined容器可以使两个或者多个容器共享一个网络栈,共享网卡和配置信息,joined容器之间可以通过127.0.0.1直接通信。
创建一个httpd容器:
docker run -d -it --name=web1 httpd
创建busybox容器并通过--network=container:web1指定joined容器为web1:
查看busybox和web1的哇改名卡mac地址和IP地址一摸一样,它们共享了相同的网络栈。
busybox可以直接使用127.0.0.1访问web1的http服务:
joined容器非常适合以下场景:
1.不同的容器中的程序希望通过loopback高校快速的通信,比如Web Server与App Server。
2.希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。
容器访问外网
注:这里的外网指的是容器网络以外的网络,并非特指Internet
(1)容器访问外网
现在Docker host是可以访问外网的:
进入busybox发现容器也可以访问外网:
上面的busybox位于docker0这个私有的bridge网络中(172.17.0.0/16),当busybox从容器向外ping时会发生什么呢?
首先查看Docker Host的iptables规则:
发现,当网桥收到来自172.17.0.0/16网段的外出包,把它交给MASQUEREAD处理。而MASQUEREAD的处理方式是将包的源地址替换成host的地址发送出去,即做了一次网络地址转换(NAT)。
下面我们通过tcpdump查看地址是如何转换的。先查看docker host的路由表:
默认路由通过ens33发出去,所以我们同时监控ens33和docker0上的icmp(ping)数据包。
当busybox ping www.baidu.com时,tcpdump输出如下:
docker0收到busybox的ping包,源地址为容器IP172.17.0.2,之后交给MASQUEREAD处理。
ping包的源地址变成了ens33的IP 192.168.xx.xxx。
总结一下这个过程:
1.busybox发送ping包:172.17.0.2>www.baidu.com
2.docker0收到包,发现说发送到外网的,交给NAT处理
3.NAT将源地址换成ens33的IP:192.168.xx.xxx>www.baidu.com
4.ping包从ens33发送出去,到达www.baidu.com
(2)外网访问容器
docker是通过端口映射的方式另外网访问容器的。
docker可将容器对外提供服务的端口映射到host的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口。
容器启动后可通过docker ps或者docker port查看host映射的端口,发现容器的80端口映射到了host的32768端口。
之后通过<host ip>:<映射端口>访问web服务 :
curl hostip:32768
除了映射动态端口,也可以通过-p指定映射到host某个特定端口:
每一个映射的端口,host都会启动一个docker-proxy进程来处理访问容器的流量:
以32768端口分析:
1.docker-proxy监听host的32768端口
2.当curl访问hostip:32768时,docker-proxy转发给容器172.17.0.2:80
3.httpd容器响应请求并返回结果