杨友山 2019-06-29
docker在1.9版本之后,引入了一套docker network子命令来管理docker网络。这允许用户可以根据他们应用的拓扑结构创建虚拟网络并将容器接入其所对应的网络。
具体来说,docker网络是通过一个叫libnetwork的东西来进行管理的,用docker network命令对docker网络进行操作实际上是与libnetwork Api进行交互。
libnetwork内置有五种网络驱动,分别是:bridge驱动、host驱动、overlay驱动、remote驱动、null驱动。这些网络驱动为docker提供了多种网络模式。下面我们简单说说各驱动的作用:
bridge驱动即为通常说的桥接模式,此驱动为Docker的默认驱动,该驱动的作用为将Docker容器连接到docker默认创建的docker0网桥上面,如果未指定--network选项,创建的容器将默认指向docker0,该驱动即对应docker的bridge网络模式,通过NAT地址转换使容器和外界进行通信。
可使用如下命令查看docker0网桥,该网桥会在docker安装后自动配置:
ip addr
我们可以看到默认的docker0网桥ip为172.17.0.1/16,在某些情况下假如我们宿主机网段与docker0网段相同,我们用bridge模式默认的docker0网桥会与宿主机产生网段冲突问题,这时候只需要更改docker0默认的网段或者使用docker容器的--network选项选择用户自定义的网桥即可解决。
该驱动将docker容器与宿主机网络处于同一个network namespace下,docker容器将使用宿主机的网卡、ip和端口信息,故容器与外界不进行NAT地址转换,增加了网络性能,但是缺点是宿主机上已使用的端口docker容器不能再使用,该驱动对应docker的host网络模式。
此驱动采用IETE标准的VXLAN方式,具体VXLAN实现方式这里暂不说明,该驱动主要用于创建容器的跨主机网络,对应docker的user-defined网络模式。
这个驱动用于用户向libnetwork提供自定义的网络插件,并没有实现任何网络功能。
null驱动即为docker容器的none网络模式,该模式并为docker容器提供任何网络配置,只有docker容器自身的lo网卡,该模式一般用于一些特定的情况。
我们常用的docker网络模式一般都是bridge模式,下面我们来分析docker是如何实现桥接网络的。
首先我们可以通过如下命令查看docker0网关:
route -n
从如上命令可以看出所有指向ip为172.17.0.0/16的数据包都从docker0网卡转发。
然后我们用docker run命令启动一个容器名为demo,进入容器demo,同样用ip addr或ifconfig命令查看网卡,我们可以看到如下两个网卡:
lo网卡和eth0网卡。lo网卡即为容器自身的回环网卡,eth0网卡ip为172.17.0.3/16,和宿主机网卡docker0在同一网段,这时我们用route -n命令查看demo容器的网关如下:
可以看出demo的eth0网卡与宿主机的docker0网卡是相互连通的。
这时退出demo容器再来用ip addr查看宿主机的网络设备,会发现有一块以“veth”开头的网卡,如veth5653371,我们可以猜测veth5653371与demo容器内的eth0网卡是一对veth设备(veth pair总是成对出现),veth设备的作用为用来连接两个不同的network namespace,故docker0不是单纯的网卡而是网桥了,这就是docker的bridge网络模式的实现方式,通过连接docker0网桥来实现宿主机和容器的网络互通。
我们知道了docker的bridge网络模式的网络互通方式,那么docker是如何实现宿主机和容器的端口映射的呢?这一切都靠配置iptables规则来实现,具体表现如下:
Docker安装完成后,将默认在宿主机系统上增加一些iptables规则,用iptables-save命令查看以下规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
上面这条规则关系着Docker容器和外界的通信,含义是:将源地址为172.17.0.0/16的数据包(即Docker容器发出的数据),当不是从docker0网卡发出时做SNAT。这样docker容器访问外部的流量会认为是宿主机发出的。
同理,docker容器和宿主机的端口映射也是通过iptalbes映射来实现的,假设我们启动一个容器将其端口8080映射到宿主机端口8080上,我们可以看到如下规则:
-A POSTROUTING -s 172.17.0.3/16 -d 172.17.0.3/16 -p tcp -m tcp --dport 8080 -j MASQUERADE -A DOCKER -d 172.17.0.3/16 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8080 -j ACCEPT
该规则将访问宿主机8080端口的流量转发到172.17.0.3/16(即demo容器的etho网卡)上,所以docker是通过配置iptalbes规则来实现端口映射的。
到目前为止,我们所讲的docker网络环境仅仅是对单机而言的,要实现不同宿主机的网络互通常常有多种方式,我这里选用的是flannel来实现不同宿主机容器的网络互通,这也是kubernetes实现网络互通的方式,要想知道flannel是如何实现网络互通的,请查看我的下一篇博文:flannel解读