Docker跨主机通信解决方案探讨

背景

Docker 容器技术正在被企业应用在越来越多的领域中,比如快速部署环境、简化基础设施的配置流程等等。当你开始在真实的生产环境使用 Docker容器去部署应用系统时,你可能需要用到多个容器部署一套复杂的多层应用系统,其中每个容器部署一个特定的应用系统。此时可能就会遇到如下问题:有多台宿主机,我们事先不知道会在哪台宿主机上创建容器,如何保证在这些宿主机上创建的容器们可以互相联通?

本文介绍了Docker容器平台中的跨主机通信方案,包括libnetwork,calico,weave,flannel,Kuryr,并对各个方案的原理进行阐述,最后对Calico,Weave,Flannel,overlay(libnetwork)四种常见网络模式进行了对比,为测试和生产环境的方案选型提供参考。

一、Libnetwork

Libnetwork是从1.6版本开始将docker网络部分抽离出来成为一个单独的项目,libnetwork 的目标是定义一个健壮的容器网络模型(Container Network Model),提供一个一致的编程接口和应用程序的网络抽象。从1.9版本开始,docker已经实现了基于Libnetwork和libkv库的网络模式—多主机的Overlay网络。

1、模型

libnetwork 引入了容器网络模型(CNM)的概念,CNM 定义了三个新的术语,分别是网络沙箱、Endpoint、Network。

 

20161118174139

 

1)Sandbox。Sandbox包含容器网络栈的配置,包括容器接口,路由表,DNS配置等的管理。 linux network namespace是常见的一种sandbox的实现。Sandbox中包含众多网络中的若干Endpoint。

2)Endpoint。Neutron中和Endpoint相对的概念应该是VNIC,也就是虚拟机的虚拟网卡(也可以看成是VIF)。Endpoint的常见实现包括veth pair、Openvswitch的internal port。当Sandbox要和外界通信的时候就是通过Endpoint连接到外界的,最简单的情况就是连接到一个Bridge上。

3)Network。Network是一组可以互相通信的Endpoints集合,常见的实现包括linux bridge,vlan等。libnetwork中的Network大家可以认为是Neutron中的network即可,更加贴切点的话可以认为是Neutron中的一个拥有一个subnet的network。

2、结构体对象

下面我们看下libnetwork为了对外提供这几个概念而暴露的编程结构体:

1)NetworkController。用于获取一个控制器,可以认为通过这个控制器可以对接下来的所有网络操作进行操作。Neutron中并没有这么一个概念,因为Neutron中的网络是由agent通过轮询或者消息的方式来间接操作的,而不是由用户使用docker命令直接在本机进行操作。

2)Driver。这里的Driver类似于Neutron中的core_plugin或者是ml2下的各种driver,表示的是底层网络的实现方法。比如有bridge的driver,也有基于vxlan的overlay的driver等等。这个概念和Neutron中的driver概念基本上是一样的。

3)Network。这里的Network结构体就是对应的上面CNM中的Network,表示建立了一个网络。通过这个结构体可以对建立的网络进行操作。

4)Endpoint。这里的Endpoint结构体就是对应上面CNM中的Endpoint,表示建立了一个VNIC或者是VIF。通过这个结构体可以对Endpoint进行操作。

5)Sandbox。这里的Sandbox结构体就是对应上面CNM中的Sandbox,表示建立了一个独立的名字空间。可以类比Nova的虚拟机或者是Kubernetes的Pod,亦或是独立的Docker容器。

3、API使用步骤

接着我们看下一般使用libnetwork的方法,具体的步骤一般是下面这样的:

(1)获取一个NetworkController对象用于进行下面的操作。获取对象的时候指定Driver。

(2)通过NetworkController对象的NewNetwork()建立一个网络。这里最简单的理解就是现在我们有了一个bridge了。

(3)通过网络的CreateEndpoint()在这个网络上建立Endpoint。这里最简单的理解就是每建立一个Endpoint,我们上面建立的bridge上就会多出一个VIF口等着虚拟机或者Sandbox连上来。假设这里使用的是veth,则veth的一头目前接在了bridge中,另一头还暴露在外面。

(4) 调用上面建立的Endpoint的Join方法,提供容器信息,于是libnetwork的代码就会建立一个Sandbox对象(一般这里的Sandbox就是容器的namespace,所以不会重复建立),然后将第三步建立的veth的一头接入到这个Sandbox中,也就是将其放到Sandbox的namespace中。

(5)当Sandbox的生命周期结束时,调用Endpoint的Leave方法使其从这个Network中解绑。简单的说就是将veth从Sandbox的namespace中拿出来回到物理机上。

(6)如果一个Endpoint无用了,则可以调用Delete方法删除。

(7)如果一个Network无用了,则可以调用Delete方法删除。

4、driver类型

包含四种类型的driver包:

Host:主机网络,只用这种网络的容器会使用主机的网络,这种网络对外界是完全开放的,能够访问到主机,就能访问到容器。

Null:无网络,使用这种网络的容器会完全隔离。

Bridge:桥接网络,除非创建容器的时候指定网络,不然容器就会默认的使用桥接网络。属于这个网络的容器之间可以相互通信,不过外界想要访问到这个网络的容器呢,需使用桥接网络,有点像主机和容器之间的一座桥,对容器有一点隔离作用。

Overlay:Overlay驱动可以实现通过vxlan等重叠网络封装技术跨越多个主机的网络,目前Docker已经自带该驱动。

Remote:Remote驱动包不提供驱动,但是提供一个支持远端传输的方式,驱动的实现可以由你自己喜欢的语言来实现。

二、Calico

Project Calico 是纯三层的 SDN 实现,没有使用重载网络,它基于 BPG 协议和 Linux 自己的路由转发机制,不依赖特殊硬件,没有使用 NAT 或 Tunnel 等技术。能够方便的部署在物理服务器、虚拟机(如 OpenStack)或者容器环境下。同时它自带的基于 Iptables 的 ACL 管理组件非常灵活,能够满足比较复杂的安全隔离需求。

所有的容器均通过配置使用calico-node实现网络互通及访问以太网。

基本原理

 

20161118174200

拓扑图

 

模块间的关系(控制平面):

 

20161118174206

 

Calico把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以Calico方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。对于控制平面,它每个节点上会运行两个主要的程序,一个是Felix,它会监听ECTD中心的存储,从它获取事件,比如说用户在这台机器上加了一个IP,或者是分配了一个容器等。接着会在这台机器上创建出一个容器,并将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。绿色部分是一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,你们路由的时候得到这里来。

由于Calico是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率可能是所有方案中最高的,因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。

 

20161118174213

 

Calico已知问题

(1) 只支持tcp,udp,icmp,icmpv6协议,如果需要支持其他L4协议,需要选择其他网络模式;

(2) 没有对数据路径进行加密,因此对于不信任的网络是不安全的;

(3) 在公众网络上需要使用-ipip选项,该选项采用IP-over-IP的效率很低;

(4) 通常 跑在一个大二层的网络里,对于大二层网络其实就是没有任何三层的网关,所有的机器、宿主机、物理机在二层是可达的,此时任何一个都会有一定的硬件风险会让整个大二层瘫痪;

(5) Calico跑在了一个三层网关的物理网络上时,它需要把所有机器上的路由协议和整个物理网络里面的路由器的三层路由全部用BGP打通。这其实会带来一个问题,这里的容器数量可能是成千上万的,然后你让所有物理的路由学习到这些知识,这会给物理集群里的BGP路由带来一定的压力
Calico既可以单独配置也可以作为docker libnetwork的一个驱动使用。

三、Weave

其中Weave是由Zett.io公司开发的,它能够创建一个虚拟网络,用于连接部署在多台主机上的Docker容器,这样容器就像被接入了同一个网络交换机,那些使用网络的应用程序不必去配置端口映射和链接等信息。

外部设备能够访问Weave网络上的应用程序容器所提供的服务,同时已有的内部系统也能够暴露到应用程序容器上。Weave能够穿透防火墙并运行在部分连接的网络上,另外,Weave的通信支持加密,所以用户可以从一个不受信任的网络连接到主机。

 

20161118174221

 

Weave实现原理

Weave会在主机上创建一个bridge,每一个容器通过veth pair连接到该bridge上,同时bridge上有个Weave router的容器与之连接,该router会通过连接在bridge上的接口来抓取网络包(该接口工作在Promiscuous模式),使用的工具为pcap。

模块间的关系:

 

20161118174228

 

数据通道:

 

20161118174235

 

它在每个容器会起两个网卡,一个网卡连着自己起的可以跟其他宿主机联通的网桥;另一个网卡绑在原生Docker的一个网桥上,并在这个网桥上监听一个DNS的服务,这个DNS实际上嵌在Router里面,即它可以从Router里学习到一些服务的后端的一些配置。所以这时容器如果发起DNS查询,实际上会被路由导到宿主机上,DNS Server上,然后DNS server做一些响应。

 

20161118174241

 

Weave的隔离是子网级的隔离,比如说有两个容器都处在10.0.1-24网段,那么它会在所有的容器里面加一条路由说该网段会走左边的网桥出去,但是所有非此网段的流量会走Docker0,这个时候Docker0和其他是不联通的,所以它就达到一个隔离的效果。

 

20161118174247

 

Weave优点

1)支持主机间通信加密;

2)支持container动态加入或者剥离网络;

3)支持跨主机多子网通信。

缺点

1)不支持服务发现,主机不能动态加入节点网络;

2)只能通过weave launch或者weave connect加入weave网络。

新推出的weave plugin可以对接libnetwork remote driver,可以与docker更好的整合。

注意事项:需要确保内部的MTU比外部MTU小76。

四、flannel

Flannel之前的名字是Rudder,它是由CoreOS团队针对Kubernetes设计的一个重载网络工具,其目的在于帮助每一个使用 Kuberentes 的 CoreOS 主机拥有一个完整的子网。

类似于weave、vxlan,提供了一个可配置的虚拟承载网络。Flannel以一个daemon形式运行,负责子网的分配,flannel使用etcd存储、交换网络配置、状态等信息。

集群的拓扑图示例如下:

 

20161118174255

 

部署模块之间的关系如下(控制平面):

 

20161118174301

 

基本原理

每台主机运行在一个子网内,多台主机共同归属于一个大的子网,主机间通过udp或者vxlan实现跨主机间的通信。

每个机器上面的Flannel进程会监听ETCD,向ETCD申请每个节点可用的IP地址段,并且从ETCD拿到其他所有宿主机的网段信息,这样它就可以做一些路由。对于它的转发平面——转发平面主要表现数据流的流向——它在Docker进来的网桥基础之上,又创建了一个新的叫VXLAN的设备,VXLAN是一个隧道的方案,它可以把一个二层的包,前面加一个包头,然后再把整个包作为物理网络的一个包,去物理网络里面去路由,流转。

 

20161118174308

 

特点

1)非常适合在Kuberntes中使用;

2)由于每个主机一个子网,灵活性不足。

五、Kuryr

Kuryr是一个Docker网络插件,其通过Neutron为Docker容器提供网络服务。Kuryr可以单独或以容器化的方式使用,这使得它们可以使用通用Neutron插件将容器接入到Neutron网络中。

Kuryr主要实现通过将libnetwork映射到neutron api。逻辑图如下:

 

20161118174315

 

原理图如下:

 

20161118174323

 

Kuryr工作流程:

1)Dockers引擎的操作转换成对Libnetwork的Remote驱动的操作;

2)对Remote驱动的操作,通过Kuryr转换成对Neutron API的操作;

3)对Neutron API的操作,通过Neutron插件的机制转换成对具体网络方案驱动的操作

六、对比

 

20161118174330

 

Calico方案因为没有隧道封装的网络开销,会带来相对较高的网络性能,但是不支持多租户,由于没有封装,所有的容器只能通过真实的IP来区分自己,这就要求所有租户的容器统一分配一个地址空间。

Flannel和overlay方案均使用承载网络,承载网络的优势和劣势都是非常明显。

优势有:对底层网络依赖较少,不管底层是物理网络还是虚拟网络,对层叠网络的配置管理影响较少;配置简单,逻辑清晰,易于理解和学习,非常适用于开发测试等对网络性能要求不高的场景。

劣势主要包括:网络封装是一种传输开销,对网络性能会有影响,不适用于对网络性能要求高的生产场景;由于对底层网络结构缺乏了解,无法做到真正有效的流量工程控制,也会对网络性能产生影响;某些情况下也不能完全做到与下层网络无关,例如隧道封装会对网络的MTU限制产生影响。

weave可以穿透防火墙,安全性较高,流量是被加密的,允许主机连接通过一个不被信任的网络,同样会有承载网络的带来的优缺点,不过可以通过Fast datapath来提高性能。

Kuryr项目主要用来建立Docker与neutron的连接,本身不提供网络控制功能,而只是提供下面一层网络控制功能到容器网络的管理接口封装,是作为libnetwork的一个插件,基于neutron模块工作,比较适用于openstack与容器集成的环境。

K8S中文社区微信公众号
分享到:更多 ()

评论 抢沙发

评论前必须登录!