容器的网络架构
在宿主机上执行ifconfig命令查看机器上的网络设备,可以看到有个docker0的网络接口。Docker守护进程就是通过docker0为docker的容器提供网络连接的各种服务。docker0实际上就是linux的虚拟网桥。什么是网桥呢,根据OSI七层网络模型,网桥是数据链路层的一种设备,用来通过MAC地址(网络的物理地址)来对网络进行划分,在不同的网络之间,进行传递数据。
Linux的网桥有些特点:可以设置ip地址,按道理,ip地址不应该出现在二层设备上,但是Linux中的虚拟网桥,是通用网络设备抽象的一种,只要是网络设备,就能够设定ip地址。当虚拟网桥拥有ip后,linux便可以通过路由表,或者IP表规则,网络层定位网桥,这就相当于拥有了一个隐藏的虚拟网卡,而这个网卡的名字就是虚拟网桥的名字,也就是我们看到的docker0。
Docker0的地址划分:
Ubuntu中的网桥管理工具为bridge-utils,可以通过apt-get工具进行安装:
通过"brctl show"命令来查看网桥设备:
此时通过docker run命令启动一个ubuntu的容器,然后再执行"brctl show"命令:
可以看到docker0有了一个接口“vethda1c8d9”,这个就是在容器创建时,为容器连接docker0所创建的一个网络接口,同样通过ifconfig命令也可以查看到这个网络接口。
当docker0所提供的默认ip地址不能满足我们期望给容器所分配的地址时,我们可以通过linux自带的ifconfig命令修改ip的地址,将其改为我们希望使用的网段。
sudo ifconfig docker0 192.168.200.1 netmask 255.255.0.0
sodo service docker restart
在docker重启完成后,重新运行一个ubuntu的容器,然后查看容器的ip,我们可以看到,ip地址被重新分配为与docker0处于同一个网段。
使用自定义虚拟网桥方法:
容器间的互联
在同宿主机下,docker的容器是通过虚拟网桥来进行连接的,在默认情况下,在同一宿主机下运行的容器都是可以互相连接的,docker提供了一个容器间是否允许连接的选项:“-icc=true”代表这docker允许容器的互相连接。
如下命令演示了使用镜像cc创建了两个容器,分别是cct1和cct2。查看cct1的ip为172.17.0.6,cct2的ip为172.17.0.7。
在cct1中执行ping命令到cct2,可以ping通,同理,在cct2中也可以ping通cct1。
当容器重新启动时,其ip会发生变化,如果想在ip改变时仍能正常访问,可以在创建容器时使用link参数。
docker run -it --name cct3 --link=cct1:webtest cct
在cct3创建完毕后,执行ping命令,可以ping通到cct1,如“ping webtest”。
如果要拒绝容器间连接,只需把-cc的参数设置为false即可。
vim /etc/default/docker
DOCKER_OPTS="-cc=false"
wq!
sudo service docker restart
如果只允许特定的容器间连接,则需要三个配置:
1)icc参数设为false。如“--icc=false”
2)iptables参数设置为true,如“--iptables=true”
3)在创建docker容器时使用link参数。
Docker利用iptables的机制,在icc等于false时,阻断所有的容器间的数据访问,仅仅允许利用link选项配置的容器进行相互的访问。
在容器都启动后,我们可以使用“sudo iptables -L -n”命令,查看iptable的信息,可以看到Docker链中有两组转发链路如下:
外部网络到容器的访问
首先,我们来了解两个概念,一个是ipforward,一个是iptables,这两个因素决定了docker容器与外部网络的连接。ipforward本身是linux系统中的一个变量,它的值决定了系统是否会转发流量,在docker的守护进程的默认参数中,也有ipforward的选项,该选项的默认值是true。当使用这个默认值时,docker会在守护进程启动时,将系统的ipforward设置为1,也就是允许流量转发,这时我们就可以使用系统自带的工具来查看系统转发是否开启。
ps -ef | grep docker
sudo sysctrl net.ipv4.conf.all.forwarding
如上命令首先查看docker的守护进程是否开启,在开启的情况下查看系统的转发参数值,通常可以看到,该值为1,代表转发已经开启。
iptables是与linux内核集成的包过滤防火墙系统,几乎所有的linux发行版本都会包含iptables的功能。下图为iptables所包含的功能,图中给出了在iptables中网络的数据包是如何传递的,每个矩形方框为一个链(Chain),每一个链就是数据处理的一个环节,在每个环节中又包含了不同的操作,比如prerouting中就包含了3个操作:nat、mangle和raw。
该图包含了数据的三种流动方式。第一个路径是以本机为目的地,当一个数据包进入网卡时,首先进入了PREROUTING链,如果判断出数据包的目的地是本地ip,则数据包将会进入下面的INPUT链,在处理之后再传给本机的程序。第二个路径,是数据发送的路径,数据从本机发出,经过OUTPUT链、POSTROUTING链,出去。第三个路径是转发路径,在数据进入PREROUTING后,发现目的地不是本机,则会进入FORWARD链,然后再通过POSTROUTING发出。
iptables规划了几个概念,第一种是表,第二种是链,第三种是规则。iptables将同样的操作抽象为了iptables中的表,也就是nat、mangle、filter、raw等,所有同样的这些操作都在iptables中的以这些名字命名的表中。iptables中的链,就代表了数据处理中的不同环节,或者说是不通的阶段。iptables中的规则,定义了每个链下操作。
在这里我们主要使用了filter表,以及INPUT、OUTPUT、FORWARD链。如下为执行查看iptable的命令结果。
从图中可以看出,除了INPUT、OUTPUT和FORWARD链外,还有DOCKER链,实际上docker链是FORWARD链的子链。
外部网络到容器的访问方法一就是通过端口映射的方式:
docker run -it -p 80 --name cct5 cct
nginx
ctrl+Q+P
docker port cct5
通过docker port命令可以查看到端口映射信息如下。
然后我们就可以通过“curl 127.0.0.1:49153”命令访问docker所提供的服务。我们只所以能访问,是因为在FORWARD链中的filter表中自动添加了一条转发规则,允许任何源ip到指定docker容器的转发。
如果想阻止特定ip到容器的访问,可以增加相应的规则来实现,如下为演示截图。