分享

Neutron的软件架构(https://www.ustack.com/blog/neutron/)

 BaldEagle 2015-03-20

综述

OpenStack 是目前开源界第二大的项目,参与的厂商之多可谓少见,实属发展的分布式系统软件大作,面对这么一个项目,我用自己在网络方面的经验与大家分享以 Neutron 为例的 OpenStack 软件设计。

Neutron 简介

Neutron 是 OpenStack 的虚拟网络组件,用洋气点的话说,就是一个 SDN 控制器。为什么我们需要虚拟网络?过去我们只给客户提供虚拟机,你花钱,我租你一台,想连接上就再买个公网 IP,就像很多人在 DigitalOcean 做得一样(当然 DigitalOcean 现在也有虚拟网络)。那有了虚拟网络可以干什么呢?我们来看一下 UnitedStack 控制面板里的一张图:

neutron 1

这是一个所见即所得网络架构!我们可以在上面随意的增加、修改和删除2、3层网络、路由器和虚拟机。而且通过虚拟网络,为用户提供自助式的安全组、VPN、负载均衡设备等高级服务也将方便许多。

此外,从软件设计角度来看 Neutron,它做到了 Unified API、Small Core、Pluggable Open Architecture 和Extensible,可以说是一个设计精良的系统。

架构

Neutron 的组件设计

作为一个分布式系统,我们需要在不同的节点上运行不同的组件,然后再将之组合起来,形成一个有机的整体,首先我给出 Neutron 在 OpenStack 中宏观的位置:

neutron 2

这个图是一个比较宏观的图,针对 Neutron 呢,它反映了一些事实:

  • 需要借助 Keystone 完成认证服务
  • 由 neutron-server 接受请求
  • 组件间用消息中间件完成交流
  • 消息会在 Server、Plugin 和 Agent 间传递
  • Agent 和 Plugin 与数据库交互
  • Agent 需要调用 Provider 完成功能

OK,下面我们微观的看一下 Neutron:

neutron 3

Neutron Server

再上面的图里我们可以看到,Neutron 的服务端是个很复杂的模块,他的复杂之处主要体现在它在”微内核“的基础上实现了 Plugin、Service Plugin、Extension 的插拔,及其与 DB 的交互。

先从请求的入口说起。简单的说,Neutron Server 就是一个 WSGI Application,WSGI 的好处相信不必我多说,它可以方便的增加或者抽取 Middleware,就像一个洋葱一样,把最核心的东西放在里面,别的各种类似 Filter 的东西一层一层叠在外面就好了。借用一个 Pylon 官方文档的图:

neutron 4

只说核心 App,APIRouter,顾名思义,它是用来路由 API 请求的,把 API 请求真正发往那些实现功能的函数,在这里 Neutron 会加载 core plugin 和 plugin extension,扩展它的 Resource map,针对每一个资源集合(比如 networks、subnets、ports 等)建立 Controller,以后 API 将被路由到 Controller 去,Controller 再去调用对应的 create、get、update、delete 方法。同时在这里还可以做一些通用的检查,比如权限、数据有效性、自动转化数据格式等等。

如何清晰的表达一个资源?首先要说明这是一个扩展的资源,而不是新的资源,然后它扩展 的是 port,接着是否允许 post、put,需要怎么转换数据格式,用什么做数据验证,是否要做权限检查,默认值多少,在 get 资源时是否显示,就算不懂 Neutron 的人也能看懂这些语句的意思。这里体现了 Neutron/OpenStack 的代码框架设计的优雅高明的地方,节省了程序员写 dirty code 的时间,减少了初学者的学习门槛。

Core Plugin and Extensions

为什么在Middleware里要加 Extension 相关的 Filter?很简单,因为 Extension 扩展了 Resource Map,我们需要配置其相应的 URL,那么基础资源是哪些呢?在 Neutron 里,经过一些考虑后,我们把三类资源定义为核心资源,这三类资源分别是network、subnet、port。这三类资源的实现呢,对应的要求由 Core Plugin 来实现。

用户将可以在配置文件里自由的设置 Plugin,然后再由NeutronManager加载。其结构也很清晰,CommonDbMixin完成基础的 DB 操作,比如在查询时增加 hook,设置 filter 等,NeutronDbPluginV2做与 Core Plugin 相关的数据库操作,最后 Core Plugin 继承NeutronDbPluginV2以及扩展的类。那么_supported_extension_aliases是做什么的呢,我们前边说 Server 有一层 Filter 去加载 Extension 的 Resource Map,它在查找 Extension 时呢,是先根据目录进行查找的,查找 extensions 目录下的所有 Python 文件,然后每个 extension 都会有一个 alias 名字,extension manager 再去查我们当前加载的 Core Plugin 是否支持这个 Extension,如果支持则加载,否则在日志里记录一下这个”extension not supported by plugin”。

Service Plugin

刚才只提到了加载 Core Plugin 和 Extensions,其实加载 Core Plugin 之后就加载 Service Plugin 了,而且因为 Core Plugin 和 Service Plugin 有很多相似的地方,所以 Core Plugin 很多地方也都按照 Service Plugin 去处理,就比如说 Resource Map,我们核心资源是写在RESOURCE_ATTRIBUTE_MAP里的,Plugin Extension 由 Extension Manager 加载,那 Service Plugin 呢?刚才我们说 Extension Manager,并没有区分什么 Plugin Extension Manager 或者 Service Extension Manager,就是因为这两个确实是不做区分的,而且很重要的一点是我们统一的只由 Extension 去扩展资源,就如前面 Filter 只有 Extension 但没有别的,所以如果你需要扩展 Resource Map,那么写一个扩展,然后把它加在你的 Service Plugin 的 supported_extension 里,Server 就可以接受相应的请求并路由了。

Agent

把服务端做得优雅,微内核之后,我们就要将调用发到 Agent 端去干活了,首先我们要解决的是 RPC 远程调用的问题。为了简化设计,多应用已成熟的东西,Neutron 或者别的 OpenStack 组件都是使用现有的 MessageQueue 服务完成的,而且 MessageQqueue 的调用有单独抽象出来一个模块叫做 Oslo.Messaging,于是对于 Neutron、Nova 这些服务它们也就不需要去考虑底层的 MQ 支持哪些,该如何调用这些问题了。目前 Oslo.Messaging 支持的 MQ 组件有 RabbitMQ、ZeroMQ 和 Qpid。我们平时用的比较多的 MQ Backend 主要是 RabbitMQ,其对 Topic 类型消息的处理过程大概如图所示:

neutron 5

现在消息能发能收了,但可以通讯还不够,我们需要实现远程过程调用,俗称 RPC,在 Neutron 里这主要涉及到两个类:

neutron 6

右边的Rpcroxy是服务端联系 Agent 用的,而RpcCallback是 Agent 联系服务端用的。在无论消息段还是服务端,都会运行来自oslo.messaging的MessageHandlingServer或类似的类,也就是一 个个用绿色线程启的Rpcorker,当RpcWoker收到任务时会去执行相应的调用,在一些 Agent (比如 L3 Agent)里还实现了优先队列,以保证高优先级的任务被首先执行。

总结

将分散在全世界的数百名来自不同厂家,专注不同业务的工程师集合起来,完成一个目前没 有成熟标准和现成学习者的软件,不得不说开源社区的力量很强大,它既要保证代码的优秀、自然,还要兼顾不同厂商的利益与差异,能在 3 年多的时间完成一个现在我们已经敢把它部署在 UnitedStack 线上,同时运营着共有云和托管云,支撑数千名客户的稳定高效使用的虚拟网络组件已经表现不俗。在此希望所有对网络虚拟化感兴趣的同学加入进 UnitedStack,我们并肩打造一个更加优秀的云计算平台!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多