
OpenDaylight(ODL)作为一款开源SDN控制器,在网络界具有超高的人气并拥有大量的开发者,经过3年的发展目前已经演进到第四个正式版本,铍(Beryllium)版本。作为一个开放的网络平台,ODL不仅提供丰富的北向接口,支持多种南向协议,并致力于提供一套网络的抽象服务。开发者能够从这一整套的网络技术平台上,相对容易的对各种网络技术进行整合,开发出更具实用价值的APP。
面对ODL这一网络开发平台的庞然大物,APP的开发面临很多的问题和不确定性:一方面ODL架构复杂开发门槛较高,导致前期的学习和开发储备,以及选择适合的APP开发架构会花费大量的精力;另一方面ODL提供了大量的南向协议和复杂的模块特性映射,常导致开发人员无从下手;再加上各个模块的成熟度不一,很多模块还处于持续的演进过程中,在开发文档相对滞后的情况,又需要开发者具有很强的探索和二次开发能力;
本文以ODL架构为基础,针对广域网APP开发的特点从APP架构选择、南向协议开发两方面进行描述,分享广域网APP开发的一些实践。
OpenDayLight架构
ODL采用OSGi Karaf框架,与大多数的控制器设计一样采用分层的南北向接口机制,其内部架构上分为应用服务层、控制器平面层、南向接口层和数据平面层四个层次。ODL采用模型驱动的方式定义功能型网络,通过在数据存储、消息基础设施上共享的YANG数据结构,ODL能够很容易组合版本中提供的丰富特性和服务,来解决复杂的网络问题。在ODL的模型驱动服务抽象层(MD-SAL),任何APP或者功能特性可以被绑定成一个业务中在控制器上加载。通过灵活的配置和弹性的组合构建出各种可能性的网络需求。
ODL支持丰富的南向协议,包括OpenFlow、NetConf、BGP、BGP-LS、PCEP、OVSDB、SNMP、LISP等。这些协议以插件的形式为上层应用提供了增强的网络编程能力,从而解决更广泛的用户需求。
在SDN架构下,用户关心如何获得更具灵活性的网络编程能力和抽象,ODL通过各种类型的北向接口,可以给业务提供开发标准、丰富灵活的API,让APP开发者可以不需要关心底层协议实现细节,创建出抽象的北向或者南向控制器应用。在ODL里为开发提供了多种操作方式,例如ALTO、Group Based Policy以及Network Intent Composition。
在这里不得不提及ODL的最新铍版本,上图即为该版本架构图。长久以来ODL的性能和可扩展性一直是被开发者诟病的,例如:内存数据库性能达不到预期;最基础YangTools模块在高并发操作时频发异常;AKKA分布式集群基础对象开发者无法访问等问题层出不穷。ODL的社区也意识到这一点,根据官方的说明,铍版本除了在特性的增强和功能的丰富上下足了功夫,更是在性能和可扩展性进行了重大的改进,“新的网络服务在集群和高可用性上有较大提升,数据处理、消息传输方面也有很大的提高,并提供了更好的网络模型抽象”。作为一个广域网APP的开发者,很高兴看到了社区在这方面的努力,很大程度上解决日益突出的大量的数据存储和复杂计算的基本困境,让开发者更专注于业务本身。
APP架构选择
进行ODL的APP开发,首先需要弄清楚的是什么是YANG,什么是MD-SAL。正因为这两个关键技术点的融合,让ODL有别于其他开源SDN控制器,在该领域快速崛起,成为开发者的首选平台。
YANG(RFC 6020)是一种用于NETCONF的建模语言。一个YANG模型定义了一个数据的层次结构,并把其模型化为一棵树,树中每个节点都有名称,且要么有一个值要么有一个子节点集。YANG还可以用于基于NETCONF的操作,包括Remote Procedure Calls(RPCs)和Notifications等。下图为铍版本依赖关系图,可以看到在最底层YangTools模块,几乎被所有的模块所依赖,是整个ODL项目的核心。YangTools通过YANG Schema来规范数据格式,大大简化了开发者的代码编写。配合Genertor可以直接生成Java代码,保证了调用模块间数据格式一致,并且可以直接通过java对象传递。
SAL的全称为Service Abstraction Layer(服务抽象层),分为MD(Model-Driven模型驱动)与AD(Application-Driven应用驱动)两种。从ODL的第二个版本氦版本开始,AD方式已经被逐渐淘汰并在铍版本上彻底废弃,目前可以认为SAL就是MD-SAL。MD-SAL采用模型驱动的方式对业务进行抽象,为南向和北向APIs的统一以及在控制器内部对不同业务功能组件的数据结构定义提供了便利。为了完成这一功能,MD-SAL使用YANG作为其建模语言来对业务和数据进行抽象,并引入了consumer和provider的概念,SAL中的APIs由Model提供并自动生成。SAL层并不定义具体的Model。用户可以自定义新的Model,并添加到MD-SAL中即可。MD-SAL不感知插件之间的具体数据信息。MD-SAL中提供三种核心服务:用于数据库存储的Data Store服务;用于模块间调用的RPC/Service routing服务;以及模块间的通知订阅和发布服务。
正如上图所示,在定义好YANG模型后通过YangTools生成Java接口和类,开发者可以使用模型定义好的数据结构和代码框架,编写插件代码,再由Maven工具生成OSGI框架下的API Bundle和插件Bundle,最后打包成一个Feature部署到控制器中。
对于ODL的APP开发而言,通常分为三个层次:基于的REST API的上层应用开发;基于MD-SAL的北向APP插件开发以及基于MD-SAL的南向协议插件开发。
● 基于的REST API的上层应用开发:该方式的关注点更多的集中在WEB可视化界面的开发上。另外,针对已有非OSGI框架、非Java开发语言、使用独立数据库的北向编排系统系统,如果需要与ODL进行对接,也采用该方式;
● 基于MD-SAL的北向APP插件开发:在MD-SAL内部进行开发,一方面可以借助MD-SAL提供的模块间高效和灵活的访问方式,对ODL现有网络特性进行特色整合;另一方面,通过YANG模型的建模,提高开发效率,另外合理的使用MD-SAL自带的Data store内存数据库,可以很容易的实现数据持久化和非持久化数据的存储,甚至开发出分层次集群特性;
● 基于MD-SAL的南向协议插件开发:由于ODL支持丰富的南向协议,目前南向插件的开发主要集中在协议的增强以及北向APP插件或外部APP的适配上,通常并不是APP开发的重点。
基于以三种APP开发方式,上图给出了一种广域网APP开发的综合性的架构用于参考:在控制器内部,开发北向APP插件(例如,开发拓扑、流量调度、协议配置等插件),用于整合南向协议和现有北向服务,通过MD-SAL为插件自动生成RESTCONF APIs接口为上层WAN编排模块进行调用。另外,通过YANG扩展,在不需要对ODL现有模块进行修改的前提下,为南向协议模块编写扩展插件,增强其协议功能。
南向协议相关模块
对于广域网APP的开发而言,ODL的BGPCEP项目有着举足轻重的作用,该模块长久以来都是ODL项目中的一个开发热点。BGP模块提供BGP基础协议和PCEP协议的支持,使得控制器能够使用到更加广泛的底层网络,并能部署到更多的场景中。同时BGP模块支持BGP-LS、BMP、FlowSpec等扩展,以及PCEP扩展了协议来承载Segment Routing信息。可以说该模块所提供的这些特性都是广域网SDN应用中必不可少的。本章节对BGP模块的基础架构和实现细节进行介绍,并给出一些对该模块进行二次开发的建议。
BGP模块基础架构
上图为BGP模块依赖结构树。首先底层YangTools以及dependencies,是以YANG模型为基础用于module的解析;左侧bgp-inet、bgp-parser以及bgp-flowspec用于定义BGP协议报文结构;中间bgp-rib、bgp-linkstate用于定义路由RIB数据库;右侧bgp-rib-impl完成主要的业务功能实现。从该依赖关系不难看出,BGP模块的主要工作就维护BGP状态机,解析对等体(Peer)发送来的BGP UPDATE消息封装,然后存入RIB数据库中,最后由BGP-LS对应RIB数据库中的对象转换为拓扑数据对象保存到network-topology模块中。
RIB处理
RIB(Route Information Base,路由信息库)是在RFC4271中定义的一个概念。但RFC中并没有定义应该如何去实现它。在BGP模块中路由都在MD-SAL的Data Store中进行存储,RIB是整个BGP模块的核心。
每一种类型的路由都需要提供一个RIBSupport的实现。RIBSupport告诉RIB如何将binding-aware数据(BGP Update消息)解析成binding-independent数据(datastore格式数据),因此,在对个BGP模块的协议族扩展开发中,需要为每个协议族扩展对应的RIBSupport,以实现数据库的存储,和报文的构造。
上图描述了BGPPeer对BGP UPDATE消息的存储数据流,报文从InboundBGPPeer入,最终保存到RIB数据库或者通过OutboundBGPPeer向Peer外发送出去。
● AdjRibInWriter:代表将路由数据写入数据存储的第一步。每当Peer收到一条Update消息后该写入该表,该消息会被转换成binding-independent格式并存储到adj-rib-in中。每个Peer在RIB中都有一个节点相对应;
● EffectiveRibInWriter:监听adj-rib-in表的变化,每当adj-rib-in更新时,触发对应的操作。使用入策略对路由数据进行过滤,并将它们存储到effective-rib-in中。该RIB也同样与一个Peer相关联;
● LocRibWriter:该表监听所有effective-rib-in的变化,当任何一个effective-rib-in更新时,都会触发对应的操作。它会执行最优路径选择过滤并将路由存储到loc-rib中。它还会决定哪些路由需要被通告(例如:ibgp-ebgp之间路由通告、RR路由通告)并将之填入也是每个对等体一个的adj-rib-out中;
● AdjRibOutListener:监听adj-rib-out的变化,将路由转换为BGPUpdate消息,并将这些消息发送至相关联的Peer上
Module说明
通过31-bgp.xml(定义基本解析和RIB支持)和41-bgp-example.xml(定制化部署的简单配置),BGP模块在启动过程中会启动相应的module(MD-SAL所有插件的启动过程都是从module开始)。下面列举BGP模块关键module:
● base-bgp-parser:用于注册的报文解析对象,例如ipv4/ipv6地址族的对应的报文解析对象;
● base-bgp-rib:注册不同地址族的RIB对应消息的组装对象,例如ipv4地址族的adj-rib-out需要对外发布update报文的通过注册的ipv4-surport对象来组装updae报文;
● bgp-dispatcher-impl:创建BGPDispatcherImpl对象,用于初始化netty连接的server/client端;
● timed-reconnect-strategy-factory:定义BGP协议连接参数,例如connect时间、sleep时间等;
● bgp-peer-acceptor:用于配置BGP服务器端参数,用于启动netty server端监听;
● bgp-peer:用于创建BGPPeer对象,初始化一个netty client端,向Peer主动发起TCP连接,BGP会话建立完成后,在RIB表中为该对象创建一个节点,并初始化rib-in表保存Peer发布的路由,初始化rib-out表保存本端发送的路由,effective-rib-in表保存Peer优选路由;
● rib-impl:用于创建一个全局RIB对象,并在DataStore中创建RIB数据库,该RIB对象包括rib-id、local-rib,Peer等节点。
开发建议
● BGP-LS形成的拓扑最终会写入ODL拓扑模块,并且实时的反应设备上报的网络拓扑的变化情况,APP插件可以通过监听LinkState Topology数据库的变化,构建自己所需的业务拓扑,以实现更多的业务逻辑;
● BGP模块RIB通过配置子系统(configuration subsystem)注册到Data Store中,对路由数据的访问,都可以通过直接访问Data Store完成,并且所有BGP定义的module,都可以通过REST接口进行访问;
● BGP模块向配置子系统注册了多个服务(例如:Rib、peer-registry等),对于一些高级业务特性的开发,可以在APP中通过引用这些服务,在模块中通过配置子系统获取对应的服务实例,从而实现更加灵活高效的跨模块访问;
● 由于路由数据在RIB多个表中进行监听和复制,在收集大量路由的情况下处理效率较低,而且占用大量的内存存储空间,上层应用可以根据业务需要进行裁剪和优化;
● BGP模块目前支持IPv4单播、IPv6单播,linkstake和flowspec地址族,应用开发者人员可以考虑做协议族的增强开发;
● BGP模块从架构上是支持集群功能的,但BUG较多,建议集群不同节点配置使用不同的RIB表,进行数据库的隔离。
结束语
ODL作为一个平台,其创立的初衷并不是要提供一个大而全“绝对最好的”网络解决方案,更多的是找到一个方法和机制来结合业界资源建立一整套技术平台,以便让更多的人去开发和使用,从而给不同的个人和组织提供他们自己的产品。面对广域网APP开发,ODL为开发者提供了充足的“弹药”,为用户构建一个应用驱动的广域网APP,ODL已经做好了准备。最后,用APP开发过程遇到的两个陷阱,结束本文的分享,以给自己和本文读者一个小小的警醒:
● ODL虽然采用OSGI Karaf框架,但由于一个模块YANG模型的Java绑定被应用到其他引用的模块,在加上MD-SAL的基础服务也需要Java绑定,导致目前Feature的uninstall和refresh失败,也就是说目前版本的ODL的MD-SAL插件无法支持热升级,需要通过其他手段完成;
● 对于Data Store数据库,数据库API的DOM方式与Binding-Aware相比,数据库监听DataTreeChangeListener与DataChangListener相比,前者都要比后者更为可靠和高效数倍,但是面对日益复杂的Yang定义,其使用复杂度太高、陷阱太多,在性能要求不高的情况下只能采用简单的处理方式进行折中。