Docker:网络
- Docker 网络架构
- CNM
- Libnetwork
- 驱动
- 网络类型
- 命令
- docker network ls
- docker network inspect
- docker network create
- docker network connect
- docker network disconnect
- docker network prune
- docker network rm
- 网络操作
- bridge
- host
- container
- none
Docker 网络架构
Docker
容器实现环境隔离,为内部进程提供一个独立的环境,这里面就包含网络环境的隔离。从容器内部来看,就好像自己独占一个主机,拥有一套完整的网络配置,包括IP
协议栈、端口、套接字、防火墙等等。
为了实现这样的容器网络,采用的架构由三部分组成:CNM
、Libnetwork
和驱动
。
CNM
CNM
是Docker
采用的网络设计规范,其又分为三个组成要素:SandBox
、Endpoint
、Network
。
SandBox
:提供独立的网络协议栈,用于隔离容器网络与宿主机网络,形成了完全独立的容器网络环境Network
:Docker
内部的虚拟子网,使网络内的逻辑主机可以通信Endpoint
:虚拟网络的接口,通过该接口容器可以接入Network
上图中包含三个容器,每个容器都有自己的SandBox
实现自己的独立网络协议栈。这些容器通过endpoint
连接到network
,再通过network
连接到宿主机的网络,最后通过宿主机接入互连网。
Libnetwork
Libnetwork
是CNM
的一个实现,其完成了CNM
定义的三个核心功能,并拓展了容器负载均衡,网络控制以及管理。
驱动
容器具有独立完整的网络协议栈,协议栈的上三层应用层
、运输层
、网络层
都没有变化。因为容器使用TCP/IP
协议,宿主机也是用TCP/IP
协议,这些协议在上三层实现,所以上三层没有修改的必要,直接复用宿主机的实现。
这里不是说容器和宿主机使用同样的网络栈,只是它们的网络栈上三层使用相同实现方案。
协议栈的最底层是数据链路层,数据链路层通过主机的驱动程序实现。为了实现网络的隔离性,那么就需要让不同容器之间,容器与宿主机之间的网络隔离。这就依赖于数据链路层的驱动程序,比如说Endpoint
就是在数据链路层的驱动中实现的。
除去实现基本的隔离性,Docker
还提供了多种类型的网络,并分别实现相应的驱动程序。比如桥接网络驱动Dridge Driver
、宿主机网络驱动Host Driver
等等。
网络类型
刚才提到,Docker
实现了多个驱动程序,来支持不同类型的网络,在操作容器时,就可以选择不同的网络类型:
Bridge
网络:桥接网络,驱动在主机上创建一个网桥,多个容器可以接入网桥,相互通信,也可以通过网桥与互连网通信Host
网络:宿主机网络,相当于移除了宿主机与容器之间的网络隔离,让容器可以直接使用宿主机的网络,此时容器直接使用宿主机的网络Container
网络:容器网络,让一个容器共享另一个容器的网络,此时两个容器共享ip
,端口号等等,也就是共享网络none
网络:容器的网络依然隔离,但是不再拥有自己的网络,此时无法进行网络通信
接下来先讲解Docker
的网络命令,再深入讲解以上四种网络的操作。
命令
docker network ls
- 功能:查看
docker
的所有网络
语法:
docker network ls [option]
示例:
在这台主机上的Docker
,共有四个网络,其中brideg
、host
、none
这三个网络,是Docker
自带的网络,它们的网络名称和驱动名称相同。
后面的DRIVER
是该网络使用的驱动程序,也就是先前提到的几种驱动程序之一。
第四个网络是用户自建的网络,使用的驱动是bridge
。
docker network inspect
- 功能:输出网络的详细信息
语法:
docker network inspect [option]
查看默认的bridge
网络:
此处可以看到其使用的驱动程序,是否使用IPv6
。再比如Subnet
表示可分配的子网,所有连接到这个网络的容器,可以分配到这些子网。Gateway
表示网关,即这个网络通过172.17.0.1
连接到宿主机。
使用ifconfig
查看宿主机网络:
可以看到一个docker0
网络,这是Docker
安装时,自动配置的网络,也是所有容器的默认网络。其地址为172.17.0.1
,也就是bridge
网络使用的网关。
所有的Docker
容器启动时,如果没有指定网络,默认使用bridge
网络,bridge
使用docker0
,因此docker0
是所有容器的默认网络。
docker network create
- 功能:创建一个网络
语法:
docker network create [option] network_name
选项:
--driver, -d
:指定网络驱动,默认是bridge
--subnet
:指定网络的子网--gateway
:指定网络的默认网关--ip-range
:指定分配给容器的连续 IP 地址范围--enable-ipv6
:启用 IPv6 网络
此处注意区别--subnet
和 --ip-range
,前者指定的是子网,这个会影响网络的广播,IP
地址的范围等。而 --ip-range
只限制地址范围,容器分配到的IP
地址不会超过这个范围。
例如,指定子网为 172.17.0.0/16
并设置 IP
范围为 172.17.1.0/24
,这意味着 Docker
将只从 172.17.1.0
到 172.17.1.255
这个范围内为容器分配 IP
地址,但是容器的广播域是172.17.0.0/16
。
创建一个桥接网络test1
:
以上命令创建了一个test1
桥接网络,并且分配了子网192.168.0.1
。
通过ifconfig
查看主机网络:
此时多出一个br-8c5013a75f1d0
网络,前面的br
是bridge
简写,表示桥接网络,后面是一段随机生成的字符。这个网络的地址为192.168.0.1
,就是先前指定的子网。
docker network connect
- 功能:将容器连接到网络
语法:
docker network connect [option] network container
选项:
--ip
:指定IP
地址--ip6
:指定IPv6
地址
示例:
以上命令,创建了一个busybox
容器,并且执行以下命令分配网络:
docker network connect --ip 192.168.66.66 test1 bx1
分配的IP
地址是192.168.66.66
,来自网络test1
,这是先前创建的一个桥接网络。
进入容器后,执行ifconfig
,可以看到三个网络。
eth0
:由于在docker run
的时候,没有指定网络,默认使用bridge
网络,也就是eth0
,为其分配的地址为172.17.0.2
eth1
:这是通过以上指令添加的网络,通过子网掩码可以看出所属子网为192.168.0.0/16
,也就是test1
可分配的子网,而分配的地址是192.168.66.66
lo
:一个本地环回地址
docker network disconnect
- 功能:断开容器与网络的连接
语法:
docker newwork disconnect
docker network prune
- 功能:删除所有不使用的网络
语法:
docker newwork prune
示例:
此处删除了两个用户自定的网络,因为这两个网络没有任何一个容器正在使用。
docker network rm
- 功能:删除指定网络
语法:
docker newwork rm
网络操作
bridge
bridge
是默认的网络配置,如果创建容器时,不配置网络属性,那么就会使用bridge
网络。
创建一个容器,不配置任何网络:
查看网络信息,可以看到两个两个网络接口,分别是lo
本地环回,以及eth0
接口用于对外通信。
eth0
的地址为172.17.0.2
,子网掩码为255.255.0.0
,可见其属于子网172.17.0.0/16
。
这个地址,其实属于宿主机的docker0
:
可以看到,docker0
接口的地址为172.17.0.1
,当一个容器创建时,不指定网络,就会通过docker0
搭建网桥,与宿主机通信,最后通过宿主机的eth0
与互连网通信。
从容器内部看,好像自己使用eth0
就可以直接与互连网直接通信,自己独占一个主机的网络接口。
但是其实eth0
本质是连接了docker0
这个网桥,而宿主机的eth0
也连接了docker0
这个网桥。因此容器内部的eth0
与宿主机的eth0
可以通过网桥通信,进而让容器可以与外界网络通信。
在此处,docker0
就类似于物理上的路由器或者说交换机,所有连接到这个网桥的接口都可以相互通信,实现的是宿主机与容器之间的通信。
同一个网桥上的任意两个接口都可以相互通信,那么同一网桥内的两个容器之间也能可以通信,并且还会自带DNS
的解析服务。
创建一个桥接网络test_net
:
在两个终端运行两个busybox
容器bx3
和bx4
,都连接到test_net
网络:
在docker run
时加上参数--network
,可以直接指定网络,无需通过docker network connect
。
bx3
容器分配到的IP
地址为172.18.0.2
。
bx4
容器分配到的IP
地址为172.18.0.3
。
在bx3
中ping 172.18.0.3
:
成功了,说明bx3
和bx4
可以通过网桥test_net
进行通信。
不仅如此,还可以直接ping 容器名
:
在bx3
中ping bx4
也成功了,这是因为处于同一网桥下的容器,网桥会对域名进行DNS
解析,容器名就是域名,bx4
直接被解析为了172.18.0.3
。
不过这个DNS
解析只有用户自己创建的桥接网络才有,docker0
不带有这个功能。
host
host
是一种非常简单粗暴的容器网络模式,这种模式下,容器不会进行网络隔离,直接使用宿主机的网络配置。
上图中,container A
使用host
网络,其箭头直接指向整个宿主机网络,宿主机有什么接口,container A
就有什么接口。
创建一个busybox
容器,使用host
网络:
其包含的网络接口与宿主机完全一致,比如docker0
、etho
以及一个br-xxxx
网络,这个是先前创建的test_net
网络的接口。
虽然这种方式很简单粗暴,但是相比于bridge
,不需要通过网桥进行数据转发,网络效率会更高。缺点是与宿主机共用网络,可能会有端口冲突等问题。
container
container
网络模式下,一个容器会直接使用另一个容器的网络,两个容器共享网络栈。
上图中,container A
使用的就是container
模式的网络,其与container B
共用一份网络。
创建容器bx6
,使用test_net
桥接网络:
其分配到的地址为172.18.0.2
。
再创建一个容器bx7
,使用container
网络,与bx6
共享网络:
在docker run
是添加以下参数,则为container
网络:
--network container:被共享网络的容器
--network container:bx6
表示bx7
共享了bx6
的网络,其eth0
接口的地址也为172.18.0.2
。
对于被共享网络的容器,不一定就是桥接网络,也有可能是host
等其它网络。不论是什么网络,都支持被container
网络进行容器之间的共享。
container
网络的实现原理很简单,容器网络通过net namespace
进行隔离,每个容器都会创建自己的net namespace
。当一个容器使用container
网络时,不会创建自己的net namespace
,而是直接进入到别的容器的net namespace
中,那么这两个容器的网络就不会被隔离,于是新容器就可以使用别的容器的网络了。
none
none
就是没有网络,可以在docker run
时通过--network=none
来指定。
在这种模式下,只有一个lo
本地环回接口,无法与外部进行网络连接。
对于一些不需要互连网的应用,或者安全要求高的内容,可以使用none
网络进行。
最后,再总结一下以上四种网络:
bridge
:容器通过net namespace
隔离网络环境,再使用一个接口连接到网桥与外部通信host
:容器不创建自己的net namespace
,直接与宿主机共享网络container
:容器不创建自己的net namespace
,直接使用其他容器的net namespace
none
:容器通过net namespace
隔离网络环境,但是不开放接口,形成一个完全独立,无法联网的网络环境