OpenStack对接ODL之ODL-Networking详解

作者简介:蒋暕青@上海宽带技术及应用工程研究中心,SDN技术实践者,对OpenStack有深入了解

OpenStack支持多种SDN控制器的对接,有一个对接方案是将一个mechanism driver的python文件放在neutron ml2目录下面,使得OpenStack对neutron的操作可以转发给SDN控制器。但是这样的处理方式存在许多问题,后来发展的driver被命名为xxx-networking项目,主要是为了更好地对接HA部署的OpenStack,因为Neutron server分布在多个物理机上的时候会出现更加复杂的情况,而导致SDN控制器接受到错误的消息,这篇文章以ODL-Networking为例介绍ODL是如何实现,自研控制器的对接也可以参考ODL的设计思想。

ODL的ODL-Networking项目位于下面的链接:https://github.com/openstack/networking-odl。

ODL-Networking 介绍

OpenStack networking-odl是一个二进制文件的plugin,它的作用是集成OpenStack的Neutron API和OpenDaylight SDN控制器。networking-odl有ML2 driver和L3 plugin的模块,可以支持OpenStack Neutron L2和L3的API,再转发数据到OpenDaylight控制器上。

ODL驱动架构v1

ODL-Networking分为v1 driver和v2 driver,v1 driver会把OpenStack上的网络更改同步到ODL控制器上。举个例子,用户创建了一个网络,首先这个操作会被写进OpenStack数据库中,然后ODL driver会发送一个POST请求到ODL控制器 。虽然这个操作看起来很简单,但是却有一些隐含的问题:

  • ODL并不是实时反应的,即使REST call成功给到了ODL,ODL也不一定可以马上响应;
  • 在负载很大的时候(可能是odl或者openstack),两边的同步可能会存在瓶颈;
  • 在遇到odl与openstack同步失败时,v1 driver在下次的call REST API时会尝试去“full sync”整个neutron DB,所以会导致下次的REST API call花费很长的时间;
  • 在多个rest api call竞争的情况下可能会出问题:
    1.如,创建subnet时再创建port的情况,neutron的HA部署的时候,这2个创建的消息会同步的通过v1 driver发给ODL,导致创建port失败;
    2.在前面的“full sync”操作的时候,数据库如果正在删除资源,那么此时同步,会把下一时刻被删除的资源,同步到odl这边,形成openstack和odl的资源不同步。

ODL驱动架构v2

面对前面的这些问题,社区又重新设计了v2 driver:

  • v2 driver最重要的机制是引入了journaling(记录)机制,v2 driver会把openstack传给open daylight的数据记录在一个journal表中,journal表使用一堆队列来构成的。而不是直接把openstack的数据传递给odl;
  • v2 driver中会起一个journal的线程,它会周期性地检查journal表中是否有新的数据,然后处理新的数据,另外的,openstack neutron的postcommmit的操作也会触发这个线程工作;
  • 用户在创建一个网络资源的时候,这条数据会一开始存在neutron DB中,v2 driver再把这条数据存在“journal entry”里面,这个时候触发journal线程来处理这条数据;
  • 数据在pre-commit的时候就已经被记录在journal entry中了,以防止这个commit失败的时候,journal entry也可以马上中止,实现消息的同步。

Journal Entry架构

  • journal entry一开始创建的时候,状态置为“pending”等待状态,在这种状态下,entry在等待线程来处理,很多线程在处理entry的时候,只有一个线程可以“选中”这个entry;
  • 当这个entry被选中时,状态会被置为“processing”状态,并且被lock住,防止其他的线程来处理这个entry;
  • 当线程开始处理的时候,它先检查这个entry的依赖关系,如果这个entry依赖其他的entry,那么这个线程会把这个entry放回原位,然后再处理下一个entry;
  • 当这个entry没有其他依赖关系的时候,这个线程会吧entry里面的消息传递给opendaylight,具体是PUT,POST,DELETE这些操作,当这个REST call成功的时候,那么把这个entry的状态置为“completed”;
  • 当出现错误的时候,线程首先判断这个是“expected”意料之中的错误(如网络连通性等问题)还是“unexpected”意料之外的错误。对于那些意料之外的错误,会引入一个计数器,并对这个计数器设置一个数字,这个entry会被再次处理,处理的次数是这个计数器的数字。对于那些意料之中的错误,不会改变计数器的数字,当entry再次处理,直到计数器数字的次数后,这个entry就被设置为“failed”状态。否则,这个entry重新被标记为“pending”等待,等待下次被处理。

Entry依赖检查机制

以下是社区Entry依赖检查机制BP,Entry依赖检查机制将会被提前到entry创建的时候进行(https://blueprints.launchpad.net/networking-odl/+spec/dep-validations-on-create)。当前v2 driver的Entry依赖检查机制发生在entry被处理的时候,但是将会被改为entry被创建的时候就进行依赖检查机制 。当原先Entry依赖检查机制发生在entry被处理的时候,如果依赖检查机制fail,那么这个entry就会被送回队列,这样的处理方式可能会有以下几个问题:

  • entry依赖检查机制fail但是不知道哪里出了问题;
  • 很难去debug;
  • 当循环依赖的情况发生的话很难定位问题;
  • 一个entry可能会被反复进行依赖检查,导致CPU浪费;
  • 过多地检查依赖关系的话会导致数据库压力剧增。

因此,Entry依赖检查机制将会被提前到entry创建时就进行,在entry被创建的时候,journal就会检查这个entry是否有依赖其他的entry,如果有的话,那么把这个entry放在一个链表中;这样的话,journal在选择entry的时候就会只去选择没有其他依赖的entry,当entry被处理后,无论是成功还是失败,依赖这个entry的链表都会被清空,这样可以使得后面的entry不会依赖到它 。

下面是journal依赖的表,一个entry如果有依赖的话,那么下面的表中的行列就是这个entry的外键,entry被删除的时候,这些外键也需要被同时删除。entry在被处理的时候,这个entry所依赖的parent被存入parent_id中,依赖这个entry的被存入dependent_id中,在journal处理这个entry的时候,必须要保证这个entry没有与parent有依赖关系(当parent被处理完之后会自动断开所有依赖),这样的话就保证了不会存在parent没处理完,而直接去处理这个entry的情况。

Entry恢复机制

以下是社区的Entry恢复机制BP(https://docs.openstack.org/developer/networking-odl/specs/completed/ocata/journal-recovery.html),Journal entry在处理失败的情况,entry需要再被处理 ,entry可能由于以下几个原因处理失败

  • 在最大尝试次数条件下,一直处理失败;
  • ODL中的数据和OpenStack Neutron数据库不一致;
  • Neutron和ODL中的数据同步失败;
    目前失败的entry就一直会存在journal表中,这样会影响journal表的性能,大量的失败的entry存在journal表中的话对性能的影响很大。

现在需要解决的问题是如何处理entry中标记为fail的entry,下面是社区BP的流程图:

entry中标记为fail状态的entry不能对后面进来的entry产生影响
BP的实现可以分为两个部分,第一个部分,当我们检查到entry处于fail状态的时候,这个entry的动作可能是create或者update操作,如果这个资源再ODL中并没有存在、生效的话,那么我们就创建一个新的”create resource”这样的entry出来,其中,处理这个fail的entry是由一个专门的线程来处理,称为”maintenance thread”。

代码分析

v2的driver相比以前只有一个python文件来说,代码量大了许多,主要内容在networking_odl目录下,其中bgpvpn,fwaas,lbaas,qos,sfc这几个高级功能都是针对ODL的北向接口开发的,因此参考意义不大。主要的内容还是journal机制的实现以及l2和l3的实现:

在判断pending还是processing的时候,通过session.query去数据库中查找:

上述的2个机制分别位于2个类中,实现了描述的功能。Journal Entry架构:class OpendaylightJournalThread(object):
Entry恢复机制:class MaintenanceThread(object):
而L2和L3 的转发分别位于以下2个类中:

在性能方面,ODL-Networking对对entry做cache缓存,有timeout和value属性,应该会有不小的性能提升;
class CacheEntry(collections.namedtuple(‘CacheEntry’, [‘timeout’, ‘values’])):为了更好的支持ODL增加的北向接口,ODL-Networking还特定会起一个start_odl_websocket_thread,位于class OpendaylightWebsocketClient(object)类中。

参考文献

https://docs.openstack.org/developer/networking-odl/


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接https://www.sdnlab.com/19459.html
分享到:
相关文章
条评论

登录后才可以评论

蒋暕青 发表于17-06-23
2