在之前的文章中已经介绍过单主机Docker容器的网络互联,但是实际生产中我们很多时候都是多台主机部署Docker环境,且每台主机上都运行数量不等的容器,如果需要这些容器共同提供服务,就需要解决跨主机容器间的网络通信问题,所以这里就来记录一下常用的跨主机容器间的网络通信方案。
Docker主机之间容器通信解决方案:
桥接宿主机网络
端口映射
Docker网络驱动
○ Overlay:基于VXLAN封装实现Docker原生Overlay网络
○ Macvlan:Docker主机网卡接口逻辑上分为多个子接口,每个子接口标识一个VLAN。容器接口直接连接Docker主机网卡接口,通过路由策略转发到另一台Docker主机
第三方网络项目
○ 隧道方案
☑ Flannel:支持UDP和VXLAN封装传输方式
☑ Weave:支持UDP(sleeve模式)和VXLAN(优先fastdp模式)
☑ OpenvSwitch:支持VXLAN和GRE协议
○ 路由方案
☑ Calico:支持BGP协议和IPIP隧道。每台宿主机作为虚拟路由,通过BGP协议实现不同主机容器间通信
环境准备:
IP地址
主机名
Docker版本
系统(内核版本)
节点1
192.168.49.41
docker01.contoso.com
1.13.1
CentOS 7(3.10.0-693.el7.x86_64)
节点2
192.168.49.42
docker02.contoso.com
CentOS 7(3.10.0-693.el7.x86_64)
桥接模式:
对桥接模式而言,宿主机位于同一个局域网,将每一个宿主机上的容器网络桥接到宿主机网络中,容器和宿主机同在一个局域网中,因而可以互相通信。
1)在宿主机上创建网桥(如不特别指明,以下命令均需在两台主机上执行)
2)给网桥配置IP地址
3)查看网桥信息
4)配置Docker启动选项
5)重启docker服务
6)创建容器并验证
在Docker01上执行:
在Docker02上执行:
在Docker01上上验证:
在Docker02上上验证:
结论:桥接模式是跨主机容器网络互联较简答的一种方式,直接将容器网络桥接到局域网中,这样容器和宿主机就在同一网段,方便进行容器操作。但因为每台主机上的容器都直接从局域网中获取IP地址,却没有统一对不同主机上容器获取的IP地址进行登记,很容易导致IP地址冲突,虽然可以通过使用--fixed-cidr来指定,但是这样隔离网段也不太方便,网络划分需注意。
端口映射:
端口映射,顾名思义就是将容器的服务所运行的端口映射到宿主机的某一个端口,然后其他的容器通过宿主机的对应端口进行访问。
1)创建带端口映射的容器
因为使用端口映射的方式,所以不需要单独创建容器网络,我们使用默认的docker0网络即可。
2)在宿主机的防火墙中查看端口映射
3)测试容器间通信
在nat01上执行:
在nat02上执行:
结论:使用端口映射的方式,只是将容器的端口通过NAT方式映射到宿主机网络的某一个端口,只要宿主机能互相通信,容器之间就能通过宿主机的指定端口进行通信。但是这种方式需要对每一个容器都映射端口,而且宿主机的端口也是有限的(不可复用),所以会有一定的局限性。
Overlay网络:
overlay 网络驱动程序在多个 Docker 守护进程主机之间创建一个分布式网络。这个网络在允许容器连接并进行安全通信的主机专用网络之上(overlay 覆盖在上面)。Docker 透明地处理每个 Docker 守护进程与目标容器之间的数据包的路由。
Docker通过Overlay网络驱动程序支持多主机容器网络通信。要想使用Docker原生Overlay网络,需要满足以下任意条件:
Docker运行在Swarm模式
使用键值存储的Docker主机集群
这里我选择第二种方式,需满足以下条件:
1)集群中主机连接到键值存储,Docker支持Consul、Etcd和Zookeeper
2)集群中主机运行一个Docker守护进程
3)集群中主机必须具有唯一的主机名,因为键值存储使用主机名来标识集群成员
4)集群中Linux主机内核版本3.12+,支持VXLAN数据包处理,否则可能无法通信
节点1:docker01 192.168.49.41 键值存储
节点2:docker02 192.168.49.42
实现过程:
1)在节点1上下载并安装consul
2)在节点1上启动consul服务
3)修改节点1上的docker守护进程
4)同样的方法修改节点2的docker守护进程
5)到consul的UI界面查看节点是否添加
6)在docker主机上创建overlay网络
7)分别在两个节点上创建容器
检测容器是否可以互相通信:
工作原理:
在讲解原理之前,先看看容器上的网络信息,这会给我们容器间通信的一些提示。
节点1上的容器网络:
节点2上的容器网络:
我们看到每个容器都有2张网卡,而这个172.20.0.0/24的网段是哪里来的呢?再来看看docker主机的网络信息:
在节点1上:
在节点2上:
或许我们查看网桥信息会更直接一些:
我们再查看namespace:
由于容器和overlay的网络的网络命名空间文件不在操作系统默认的/var/run/netns下,只能手动通过软连接的方式查看。
可以看见两个节点主机上都有这个“1-”开头的的namespace
查看这个namespace中的网卡信息(以其中一台为例):
我们看到namespace中有一个网桥br0,再查看这个网桥的信息:
查看VNI(Vxlan Network identifier):
查看vlan设备上的静态mac地址表:
综上,overlay网络的拓补如下:
这里数据包的发送流程如下(以从左侧的容器发送到右侧的容器为例):
容器Container1会通过Container eth0 将这个数据包发送到 10.0.0.1 的网关。
网关将数据包发送出去后到达br0网桥。
br0网桥针对VXLAN设备,主要用于捕获对外的数据包通过VETP进行数据包封装。
封装好将VXLAN格式数据包交给eth0,通过UDP方式交给Container2的eth0。
Container2收到数据包后通过VETP将数据包解封装。
网桥通过网关将解封装的数据包转发给Container eth0,完毕通信。
因此,Docker容器的overlay网络的实现原理是:
1、docker会为每个overlay网络创建个单独的命名空间,在这个命名空间里创建了个br0的bridge。
2、在这个命名空间内创建两张网卡并挂载到br0上,创建一对veth pair端口 和vxlan设备。
3、veth pair一端接在namespace的br0上,另一端接在container上。
4、vxlan设备用于建立vxlan tunnel,vxlan端口的vni由docker-daemon在创建时分配,具有相同vni的设备才能通信。
5、docker主机集群通过key/value存储(我们这里用的是consul)共享数据,在7946端口上,相互之间通过gossip协议学习各个宿主机上运行了哪些容器。守护进程根据这些数据来在vxlan设备上生成静态MAC转发表。
6、vxlan设备根据静态mac转发表,通过host上的4789端口将数据发到目标节点。
7、根据流量包中的vxlan隧道ID,将流量转发到对端宿主机的overlay网络的网络命名空间中。
8、对端宿主机的overlay网络的网络命名空间中br0网桥,起到虚拟交换机的作用,将流量根据MAC地址转发到对应容器内部。
(上述原理分析部分参考:https://www.bladewan.com/2017/11/17/docker_network_overlay/)
补充:如需详细了解docker overlay网络的实现过程,可以参考:http://chenchun.github.io/docker/2015/12/29/km-docker-overlay
Macvlan网络:
macvlan本身是linxu kernel的模块,本质上是一种网卡虚拟化技术。其功能是允许在同一个物理网卡上虚拟出多个网卡,通过不同的MAC地址在数据链路层进行网络数据的转发,一块网卡上配置多个 MAC 地址(即多个 interface),每个interface可以配置自己的IP,Docker的macvlan网络实际上就是使用了Linux提供的macvlan驱动.在物理网络看来,每张虚拟网卡都是一个单独的接口。
1)创建macvlan网络
节点1:
节点2:
2)创建容器并指定IP地址
3)测试容器通信
Macvlan VLAN Bridge模式:
因为之前的操作都是在VMware Workstation虚拟机中进行,自己尝试在VMware中的Centos7中做macvlan网卡子接口vlan网络容器互通的测试,但是无一例外全部都无法通信,即使是开启了网卡的混杂模式,也都不行。网上给出的方案是使用VirtualBox,但是个人使用VirtualBox+CentOS7仍然无法通信,所以改用VirtualBox+Ubuntu 16.04,测试成功,所以下面改用Ubuntu 16.04进行演示。
172.16.3.11
docker01
17.05.0-ce
Ubuntu 16.04.3 LTS(4.4.0-87-generic)
172.16.3.22
docker02
1)创建vlan
在两个节点上操作:
2)创建macvlan网络
3)使用macvlan创建容器
4)测试网络是否能通信
这里,同一个vlan的两个节点上的容器可以通信,不同vlan的容器(无论是同一个节点还是不同节点)均不能通信,当然可以通过添加路由的方式让不同vlan的容器通信,但是vlan划分的目的就是隔绝网络,所以这里不再演示让不同vlan容器通信的方法,网上类似的教程也很多,大家自行查找即可。