今天我们来聊一聊容器如何跨主机通信,总所周知的是docker有多种网络模式:HOST、BRIDGE、null等,从多主机通信的应用场景出发,来谈已有的的解决方案。
端口映射
将宿主机A的端口P映射到容器C的网络空间监听的端口P’上,仅提供四层及以上应用和服务使用。这样其他主机上的容器通过访问宿主机A的端口P实现与容器C的通信。显然这个方案的应用场景很有局限。
将物理网卡桥接到虚拟网桥,使得容器与宿主机配置在同一网段下
在各个宿主机上都建立一个新虚拟网桥设备br0,将各自物理网卡eth0桥接br0上,eth0的IP地址赋给br0;同时修改Docker daemon的DOCKER_OPTS,设置-b=br0(替代docker0),并限制Container IP地址的分配范围为同物理段地址(–fixed-cidr)。重启各个主机的Docker Daemon后,处于与宿主机在同一网段的Docker容器就可以实现跨主机访问了。这个方案同样存在局限和扩展性差的问题:比如需将物理网段的地址划分 成小块,分布到各个主机上,防止IP冲突;子网划分依赖物理交换机设置;Docker容器的主机地址空间大小依赖物理网络划分等。
第三方SDN:诸如OVS、Flannel、Calico
关于这些第三方方案的细节大家可以参考O’Reilly的《 Docker Cookbook 》 一书。
Docker从1.9版本之后,提供一种原生的跨多主机容器网络的解决方案,该方案的实质是采用了基于 VXLAN 的覆盖网技术。方案的使用有一些前提条件:
1、Linux Kernel版本 >= 3.16;
2、需要一个外部Key-value Store(官方例子中使用的是 consul );
3、各物理主机上的Docker Daemon需要一些特定的启动参数;
4、物理主机允许某些特定TCP/UDP端口可用。
本文将带着大家一起利用Docker 创建一个跨多主机容器网络,并分析基于该网络的容器间通信原理。
拓扑
本次的跨多主机容器网络基于两台在不同子网网段内的物理机承载,基于物理机搭建,目的是简化后续网络通信原理分析。
拓扑图如下:
构建跨主机网络
考虑到kv store在本文并非关键,仅作跨多主机容器网络创建启动的前提条件之用,因此仅用包含一个server节点的”cluster”。
参照拓扑图,我们在10.10.126.101上启动一个consul,
前面提到过,通过Docker 1.9创建跨多主机容器网络需要重新配置每个主机节点上的Docker Daemon的启动参数:
DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4 -H tcp://0.0.0.0:2375
-H unix:///var/run/docker.sock --cluster-advertise eth0:2375
--cluster-store consul://10.10.126.101:8500/network
--storage-driver=devicemapper"
注意:
-H(或–host)配置的是Docker client(包括本地和远程的client)与Docker Daemon的通信媒介,也是Docker REST api的服务端口。默认是/var/run/docker.sock(仅用于本地),当然也可以通过tcp协议通信以方便远程Client访问,就像上面 配置的那样。非加密网通信采用2375端口,而TLS加密连接则用2376端口。这两个端口已经 申请在IANA注册并获批 ,变成了知名端口。-H可以配置多个,就像上面配置的那样。 unix socket便于本地docker client访问本地docker daemon;tcp端口则用于远程client访问。这样一来:docker pull ubuntu,走docker.sock;而docker -H 10.10.126.101:2375 pull ubuntu则走tcp socket。
–cluster-advertise 配置的是本Docker Daemon实例在cluster中的地址;–cluster-store配置的是Cluster的分布式KV store的访问地址;
如果你之前手工修改过iptables的规则,建议重启Docker Daemon之前清理一下iptables规则:sudo iptables -t nat -F, sudo iptables -t filter -F等。
启动后iptables的nat, filter规则与单机Docker网络初始情况并无二致。
在101节点上,创建net1:
$ sudo docker network create -d overlay net1
在71节点上,创建net2:
$ sudo docker network create -d overlay net2
此时,iptables规则也并无变化。
我们分别在net1和net2下面启动两个container,每个节点上各种net1和net2的container各一个:
sudo docker run -itd --name net1c1 --net net1 ubuntu:14.04
sudo docker run -itd --name net2c1 --net net2 ubuntu:14.04
sudo docker run -itd --name net1c2 --net net1 ubuntu:14.04
sudo docker run -itd --name net2c2 --net net2 ubuntu:14.04
启动后,我们就得到如下网络信息(容器的ip地址可能与前面拓扑图中的不一致,每次容器启动ip地址都可能变化):
net1:
net1c1 - 10.0.0.7
net1c2 - 10.0.0.5
net2:
net2c1 - 10.0.0.4
net2c2 - 10.0.0.6
在net1c1中,我们来看看其到net1和net2的连通性:
root@021f14bf3924:/# ping net1c2
root@021f14bf3924:/# ping 10.0.0.4
结果net1中的容器是互通的,但net1和net2这两个overlay net之间是隔离的。
在“单机容器网络”一文中,我们说过容器间的通信以及容器到外部网络的通信是通过docker0网桥并结合iptables实现的。那么在上面已经建立的 跨多主机容器网络里,容器的通信又是如何实现的呢?下面我们一起来理解一下。注意:有了单机容器网络基础后,这里很多网络细节就不再赘述了。
我们先来看看,在net1下的容器的网络配置,以101上的net1c1容器为例:
(该文为转载,保护原创,人人有责)