searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Neutron Tricircle

2023-05-08 08:46:32
34
0

1 前言

       本文针对开源openstack tricircle组件做如下的反向设计,以帮助理解该组件的设计原理。

2 总体架构

整体上会部署一个Tricircle集群(通常3个节点做集群即可)、多个Openstack集群。统一一套keystone集群、glance集群(不是必须)。每个openstack集群有各自的db、mq,Tricircle集群有独立的db、mq。所有neutron资源的操作都直接调用tricircle集群上的openstack neutron标准接口,其他资源(如nova)操作则调用具体的openstack集群上的openstack标准接口。

       下面是tricircle服务引入的重要组件说明:

  • Tricircle节点

该节点上主要有如下几个重要组件:

  • Tricircle Central Neutron Plugin(简称: central plugin)

负责neutron资源的集中管理,包含network、subnet、port、router、floatingip、security_group、qos等网络资源。提供标准openstack neutron api接口给上层应用操作资源的CRUD。同时也存在central plugin之间通过restapi接口调用local plugin的情况,例如查询一下bottom资源信息时。

这里会启动一个neutron server进程。

  • Admin API

对外暴露restapi,用于操作job、pod、resource routing三个重要的数据模型的CRUD。其中job用于表示一个异步任务、pod用于表示一个openstack集群、resource routing用于记录tricircle节点上的neutron资源与pod节点上的neutron资源之间的映射关系

这里会启动一个admin api的进程。

  • XJob

维护xjob异步任务。这里会启动一个xjob进程,xjob进程会作为rpc server端处理由rpc client发来的xjob异步任务。xjob进程作为rpc server端在处理异步任务的时候会调用pod的openstack neutron接口。

  • RPC

在Tricircle节点上,唯一用到的RPC通信的地方主要在于rpc client(有:central plugin及admin api)和rpc server(xjob进程)之间的交互,只有单向通信,即: rpc client -> rpc server。

  • DB

数据库服务,用于存储数据,这些数据包含: openstack neutron资源以及tricircle服务引入的三个重要数据模型:pod、job、resource routing。

  • POD节点

该节点上只需增加Tricircle Local Neutron Plugin即可:

  • Tricircle Plugin Neutron Plugin(简称: local plugin)

该local plugin相当于嵌入在neutron server和core plugin(当前使用ml2 plugin)中间,从而实现一些neutron资源处理的特殊拦截,通过拦截钩子函数实现特殊化处理。例如:create_port的时候,由于此时local plugin并未创建对应的network、subnet等相关联的资源,这种情况下,通过local plugin拦截请求,通过调用central plugin的restapi接口将network、subnet等相关资源同步创建在该local neutron中。

这里继续寄宿在原有的neutron server进程中。

       根据上述内容的描述,总结数据的交互方式有如下:

  • rpc交互方式

rpc client(central plugin/admin api) -> rpc server(xjob进程)

  • restapi交互方式

xjob(进程) ->central plugin、local plugin <-> central plugin

  • db交互方式

central plugin、admin api、xjob都需要访问db

3 数据模型

这里只介绍tricircle服务新引入的数据模型,对于已有的neutron相关数据模型则不再赘述。

3.1 pod数据模型

此数据模型用于表示一个openstack集群实例。pod/az/region_name在tricircle服务中的关系如下:

       一个az可以包含一个或多个pod。

       一个pod和一个region_name一一对应。

pod数据模型包含如下的信息:

Name

Type

Description

pod_id

string

pod对象的uuid。作为主键

region_name

string

对应openstack集群所属的region name。此region name必须在keystone里已经注册了。

az_name

string

pod所属的az。对于central neutron而言,az_name为空。如果az_name不为空,说明此pod属于此az。一个az允许包含多个pod。因此多个pod里的az_name属性值是存在相同的情况。

pod_az_name

string

实际源码中未使用该字段来参与资源调度。

dc_name

string

pod所处的机房名称。根据华为合营的做法:一个region包含多个az,一个az包含多个dc、一个dc包含多个pod。实际源码中未使用该字段来参与资源调度,估计只是用于展示而已。

 

3.2 job数据模型

       此数据模型用于表示一个异步任务。有两个数据结构,如下:

  • AsyncJob

这个数据结构用于存储异步任务,主要包含status=new、running、fail这三种状态的job信息。当前也会短时间存储一些success的job(因为是先更新job为success,然后再执行删除,因此存在比较短的时间)。包含如下的信息:

Name

Type

Description

id

string

对象的uuid。作为主键

project_id

string

租户uuid

resource_id

string

job数据。可能包含一个或多个资源id信息,如果是多个则用#分割开,例如:'%s#%s#%s' % (pod_id, router_id, net_id)

type

string

job 类型。具体有哪些类型,参加下面

timestamp

timestamp

时间戳

status

string

状态。包含:new、running、success、fail

extra_id

string

额外的uuid。主要用于实现多个worker互斥处理同一个资源。具体原理参见下文

       这个表中,虽然id作为主键,但是(type、status、resource_id、extra_id)组合有唯一性的约束条件。这样做的目的就是为了实现多worker创建new job时,只允许其中一个worker成功插入,其他worker等待。

  • AsyncJobLog

这个数据结构只用于存储status=success的job。感觉只是用于查看,其他作用在源码中暂时未看到。包含如下的信息:

Name

Type

Description

id

string

对象的uuid。作为主键

project_id

string

租户uuid

resource_id

string

job数据。可能包含一个或多个资源id信息,如果是多个则用#分割开,例如:'%s#%s#%s' % (pod_id, router_id, net_id)

type

string

job 类型。具体有哪些类型,参加下面

timestamp

timestamp

时间戳

              JT_CONFIGURE_ROUTE = 'configure_route'

JT_ROUTER_SETUP = 'router_setup'

JT_PORT_DELETE = 'port_delete'

JT_SEG_RULE_SETUP = 'seg_rule_setup'

JT_NETWORK_UPDATE = 'update_network'

JT_SUBNET_UPDATE = 'subnet_update'

JT_SHADOW_PORT_SETUP = 'shadow_port_setup'

JT_TRUNK_SYNC = 'trunk_sync'

JT_SFC_SYNC = 'sfc_sync'

JT_RESOURCE_RECYCLE = 'resource_recycle'

JT_QOS_CREATE = 'qos_create'

JT_QOS_UPDATE = 'qos_update'

JT_QOS_DELETE = 'qos_delete'

JT_SYNC_QOS_RULE = 'sync_qos_rule'

3.3 routing数据模型

该数据模型主要用于映射central neutron与local neutron之间的资源,例如:同一个network资源,在central neutron创建的uuid为id1,而在local neutron创建的uuid为id2,这样通过routing数据模型来记录id1和id2之间的映射关系,从而辅助资源的全局调度。

       包含如下的信息:

Name

Type

Description

id

biginteger

对象的uuid,作为主键。

top_id

string

在central neutron中记录的资源uuid

bottom_id

string

在local neutron中记录的资源uuid

pod_id

string

pod uuid

project_id

string

租户uuid

resource_type

string

资源类型。具体包含有哪些资源,参见下面

created_at

timestamp

创建的时间戳

updated_at

timestamp

更新的时间戳

       虽然id作为主键,但是要求(top_id、pod_id、resource_type)三者组合有唯一性的约束。这样做的目的是为了防止在local neutron中创建冗余的资源(因为涉及多worker并发处理的情况)。

RT_NETWORK = 'network'

RT_SD_NETWORK = 'shadow_network'

RT_SUBNET = 'subnet'

RT_SD_SUBNET = 'shadow_subnet'

RT_PORT = 'port'

RT_TRUNK = 'trunk'

RT_SD_PORT = 'shadow_port'

RT_PORT_PAIR = 'port_pair'

RT_PORT_PAIR_GROUP = 'port_pair_group'

RT_FLOW_CLASSIFIER = 'flow_classifier'

RT_PORT_CHAIN = 'port_chain'

RT_ROUTER = 'router'

RT_NS_ROUTER = 'ns_router'

RT_SG = 'security_group'

RT_FIP = 'floatingip'

RT_QOS = 'qos_policy'

3.4 shadow agent数据模型

shadown agent设计的目的是为了欺骗local neutron的l2population机制,从而为跨pod实现二层vxlan互通提供理论基础。包含如下的信息:

Name

Type

Description

id

string

对象的uuid。作为主键

pod_id

string

pod uuid

host

string

host id,通常为主机名。

type

string

agent类型,对我们而言对应ovs agent

tunnel_ip

string

隧道ip,即:vtep ip地址

       虽然id作为主键,但是要求(host,type)组合有唯一性约束。

3.5 endpoint数据模型

该数据模型用于同步存储keystone里记录的各个pod里各个neutron service的信息。保存的信息示例如下图所示(个人觉得只会用到neutron及keystone相关,其他在central neutron中应该用不上):

该数据模型存储了各个pod下的neutron service的endpoint url信息(public类型),借助这些url信息就可以构造neutron client,进而调用对应pod的neutron restapi接口。

       包含如下的信息:

Name

Type

Description

service_id

string

service uuid。在keystone唯一标识某个service

pod_id

string

pod uuid

service_type

string

service type,例如’neutron’

service_url

string

service url,记录的是service public url。利用这个url就可以调用对应pod的neutron restapi接口

 

3.6 其他

这个数据模型和sfc(服务功能链)相关,暂时用不上,不再赘述。

4 源码解读

       整个源码目录可分为如下几个部分:

  • tricircle/db

源码文件

说明

api.py

提供CRUD方法操作第3节所述的资源,调用core.py的接口操作db

core.py

抽象封装,适用于所有资源的CRUD db操作

migration_helpers.py

数据库升级相关,可忽略

opts.py

无重要内容,忽略

models.py

定义第3节所述的资源对应的数据库表信息,包含表的各个字段、唯一性约束、主键等信息

 

  • tricircle/api

源码文件

说明

app.py

配置wsgi app

wsgi.py

启动wsgi app入口

opts.py

无重要内容,忽略

controllers/root.py

顶层 rest接口

controllers/pod.py

pod资源相关的rest接口定义,会调用tricircle/db/api.py的接口处理

controllers/job.py

job资源相关的rest接口定义,会调用tricircle/db/api.py的接口处理

controllers/routing.py

resource routing资源相关的rest接口定义,会调用tricircle/db/api.py的接口处理

 

  • tricircle/cmd

源码文件

说明

api.py

admin api进程入口

xjob.py

xjob进程入口

manage.py

tricircle-db-manage命令执行的入口。与部署的时候创建tricircle相关的数据库表有关。

 

  • tricircle/common

源码文件

说明

baserpc.py

个人觉得无实际作用,可忽略

client.py

一个很重要的基础文件。具体说明参见4.1节

config.py

注册一些配置项,同时初始化一些mq的连接

constants.py

定义一些常量

context.py

-

exceptions.py

定义一些异常类

httpclient.py

实际中未用到,可忽略

lock_handle.py

一个很重要的基础文件。具体说明参见4.3节

opts.py

-

policy.py

-

request_source.py

实际中未用到,可忽略

resource_handle.py

一个很重要的基础文件。具体说明参见4.2节

restapp

配合admin api使用,不太重要

rpc.py

mq通道的一些初始化操作

serializer.py

配合rpc.py初始化mq通道

topics.py

定义xjob rpc topic常量

utils.py

一些工具类方法,不重要

version.py

-

xrpcapi.py

作为xjob rpc client接口,重要的基础文件,具体说明参见4.4节

 

  • tricircle/xjob

源码文件

说明

opts.py

-

xservice.py

创建service,启动service,监听rpc topic

xmanager.py

xjob rpc server回调。重要的基础文件。具体说明参见4.5节

 

  • tricircle/network

源码文件

说明

drivers/type_flat.py

继承原生的FlatTypeDriver类,处理flat网络类型

drivers/type_local.py

Local网络,我们不用,可以忽略

drivers/type_vlan.py

继承原生的VlanTypeDriver类,处理vlan网络类型

drivers/type_vxlan.py

继承原生的VxlanTypeDriver类,处理vxlan网络类型

central_fc_driver.py

和sfc相关,可以忽略

central_sfc_driver.py

和sfc相关,可以忽略

central_sfc_plugin.py

和sfc相关,可以忽略

central_trunk_plugin.py

和vlan trunk相关,可以忽略

central_qos_plugin.py

继承原生的QoSPlugin类,处理qos。

central_plugin.py

central neutron plugin, 重要的文件。

exceptions.py

异常相关

helper.py

一些辅助的方法。比较重要的基础文件。

local_l3_plugin.py

基础原生L3RouterPlugin类,只复写了get_router_for_floatingip方法,其他和原生的L3逻辑一致。

managers.py

继承原生TypeManager类。根据配置的type_dirvers注册各个type drivers。

qos_driver.py

qos driver

security_groups.py

继承原生SecurityGroupDbMixin类,处理安全组资源

local_plugin.py

local neutron plugin. 重要的文件

 

4.1 tricircle/common/client.py解读

       此文件中提供了class Client,该类主要是openstack service client的操作封装,通过该类的对象,可以操作openstack的所有资源,包含CRUD操作。

       虽然该类可以适用于所有的openstack services,但是在tricircle组件中,该类只用于openstack neutron service,因此该类的对象也可以简单看成是一个openstack neutron client,从而能够操控neutron资源的CRUD。

       首先来看下该类的__init__初始化工作,具体说明可参见图中的注释,这里不再赘述。

上图中的handle_obj.support_resource的信息如下:

support_resource = {

        'network': LIST | CREATE | DELETE | GET | UPDATE,

        'subnet': LIST | CREATE | DELETE | GET | UPDATE,

        'port': LIST | CREATE | DELETE | GET | UPDATE,

        'router': LIST | CREATE | DELETE | ACTION | GET | UPDATE,

        'security_group': LIST | CREATE | GET,

        'security_group_rule': LIST | CREATE | DELETE,

        'floatingip': LIST | CREATE | UPDATE | DELETE,

        'trunk': LIST | CREATE | UPDATE | GET | DELETE | ACTION,

        'port_chain': LIST | CREATE | DELETE | GET | UPDATE,

        'port_pair_group': LIST | CREATE | DELETE | GET | UPDATE,

        'port_pair': LIST | CREATE | DELETE | GET | UPDATE,

        'flow_classifier': LIST | CREATE | DELETE | GET | UPDATE,

        'qos_policy': LIST | CREATE | DELETE | GET | UPDATE,

        'bandwidth_limit_rule': LIST | CREATE | DELETE | GET | UPDATE,

        'dscp_marking_rule': LIST | CREATE | DELETE | GET | UPDATE,

        'minimum_bandwidth_rule': LIST | CREATE | DELETE | GET | UPDATE}

       实例化Client对象之后,后面就可以用该对象来操作资源了,无论是CRUD什么类型的资源,最终都会调用该对象中的create/update/delete/get/list/action_resources方法,其中第一个参数resource则为资源的类型。方法里会继续调用到resource_handle.py中的NeutronResourceHandle类对象的handle_create/update/delete/get/list/action方法。

       还有个细节需要注意:Client对象里的create/update/delete/get/list/action_resources方法都使用了@_safe_operation作标注,有这个标注的方法,在调用的时候会执行下面的函数(client.py文件最上面的函数):

除了图中给出的注释外,还有如下几个重要的函数需要说明:

  • _ensure_endpoint_set

上图中最终确保resource_handle对象配置上self.endpoint_url=region_name对应neutron service的public url,从而能够构造出该region_name的openstack neutron client调用neutron接口。

  • _update_endpoint_from_keystone

该方法首先会从keystone db中获取所有的service及endpoint信息,最后构造出:region_service_endpoint_map[region_id][service_name] = url(public url)。接着会根据region_service_endpoint_map的信息更新或创建对应的cached endpoint表项。

总结:使用该类的对象,执行如下的方法

create/update/delete/get/list/action_resources---》接着调用resource handle中的方法handle_create/update/delete/get/list/action---》接着使用_get_client方法获取到openstack neutron client(具体某个pod的,使用region_name查询),最后用openstack neutron client调用neutron资源接口(CRUD)。

4.2 tricircle/common/resource_handle.py解读

       该文件中存在两个类,父类为:ResourceHandle,基类为:NeutronResourceHandle。

       这个文件在central neutron端使用时会结合4.1节的Client类来使用,这个时候,父类ResourceHandle中的成员self. endpoint_url=具体pod对应neutron service的public url;当这个文件在local neutron端使用时,会直接设置父类ResourceHandle中的成员self. endpoint_url=central_neutron_url(从配置解析出来的)。

       父类为:ResourceHandle中的成员self.auth_url为keystone的认证url。因此该文件中提供了一些获取keystone token的方法。

       基类为:NeutronResourceHandle中的方法_get_client可用于获取一个openstack neutron client,从而用这个client来调用neutorn资源的CRUD接口。

       该文件中还存在各种handle_xxx方法,这些方法里最后都调用openstack neutron client来调用具体的接口。

4.3 tricircle/common/lock_handle.py解读

       这里有两个重要的函数,如下:

  • get_or_create_route

       说明见图中注释。

  • get_or_create_element函数

说明参见图中注释。

4.4 tricircle/common/xrpcapi.py

       该文件中针对每一个job type会对应一个处理方法,这些方法中会调用invoke_method方法,invoke_method方法中首先会插入一个new状态的job entry,然后通过RPC通知xjob进程(rpc server端)。

4.5 tricircle/xjob/xmanager.py解读

       首先要分析下文件中最顶层的方法:_job_handle,直接分析while True里面的内容即可。

图中注释已说明。

对于get_latest_job方法,获取某个status下的资源,可能会获取到多个,因为存在多个worker同时向db中插入某个status且相同资源的情况。处理只需返回最新的job即可。其他旧的job依然存在db中,待最新的job成功处理之后,会将这些冗余的job都删除掉。

对于register_job方法,有个特殊的地方是:extra_id使用的是全0的uuid,这样设计的原因是为了防止多个worker同时注册同一个job,由于(type,status,resource_id,extra_id)组合存在唯一性,而extra_id全0,因此对于同一个job而言,最终只能有一个worker注册成功。

说明参见图中注释。

上图中,finish_job方法需要说明下:

说明参见图中注释。

       上图注释中说到:对于fail的job会有周期的任务再次触发尝试,这个周期任务函数如下:

注意添加了注解:“@periodic_task.periodic_task”。

xmanager作为xjob rpc server回调,各个回调方法都使用了注解:@_job_handle,例如:

同时,xmanager还会同时承担xjob rpc client,因为在处理rpc server回调方法时,需要处理那些未明确指定pod的逻辑,对于这种情况,本地查询出所有关联的pod,然后再通过xrpcapi将job分发到具体的pod,例如:

总结:xmanager主要的工作内容是处理xjob rpc server回调函数的处理,同时会启用周期任务处理一些fail job。xmanager会同时承担xjob server和xjob client的角色。

5 设计考虑

5.1 DHCP考虑

       为了避免多个local neutron自动创建dhcp port,出现dhcp port ip地址冲突的问题,由central neutron统一管理dhcp port。当central neutron处理subnet创建时,会同时创建一个dhcp port,即:为该子网预留一个dhcp ip。当local neutron创建对应的subnet时,local neutron会调用central neutron的接口查询预留的dhcp port,然后会使用该port的ip创建local neutron subnet的dhcp port。

       因此,对于一个subnet而言,对应的dhcp ip在所有的local neutron中都是完全相同的。

       Dhcp ip地址完全相同,这样会出现一个pod的vm的dhcp请求会发往其他pod的dhcp吗?对于这个问题,可以利用l2 population机制下发的单播流表来拦截。因为其他pod的dhcp port在本地的pod不存在,也不会创建对应的shadow port(参见5.6),因此通过单播流表可以实现dhcp的请求被发往到其他pod。

       当配置dhcp_agents_per_network=xxx多个dhcp实例时,central neutron是否会创建多个IP的dhcp port?local neutron会对应创建多个dhcp namespace?这个能力有待验证。

5.2 Qouter默认网关考虑

       当network需要跨越多个pod时,这个时候需要在不同的pod上给subnet创建默认网关ip。Tricircle当前的设计是:不同的pod下,同一个subnet,默认网关ip是不同的。它这样做的目的是为了能够在本地pod下完成三层路由转发,而如果每个pod的默认网关ip都相同,则会出现三层路由转发的流量走到其他pod上,这样路径并非是最佳同时也是不可预测的。

       为了实现上述的目标,在local neutron创建subnet之前,会先去调用central neutron的接口创建默认网关port,local neutron会使用这个port的ip作为在local neutron的subnet默认网关IP。在central neutron创建的默认网关port的命名方式包含了:local neutron所属的region_name以及subnet uuid,从而实现同一个subnet,不同pod有不同的默认网关IP。

       其实,如果用分布式qrouter,不同的pod下,同一个subnet使用不同的默认网关IP也是没问题的,三层路由转发的流量不会跑到其他pod(因为br-tun上的流表已经做了限制,不允许请求qrouter的流量跑到其他计算节点,也就不会出现三层路由的流量跑到其他pod了)。

5.3 安全组考虑

       对于安全组而言,一个难点就是支持remote_group_id特性。该特性实现的目标是:让关联了安全组id=remote_group_id的vm之间能够相互访问。

       但是考虑两个vm,vm1在pod1,vm2在pod2,这个时候,我们不能在这两个pod下都创建安全组,拷贝central neutron中的所有rules到这两个pod。因为在pod1上并没有vm2的port,不能通过remote_group_id的特性配置iptables规则,让vm2能够访问vm1.

       为了支持remote_group_id,考虑有如下几个方法:

       方法1:在pod下都创建其他pod的port,因此也需要对应创建network、subnet。这样做会带来很多不必要的资源消耗。

       方法2:扩展remote_group_id的rule,将这种rule转换为多个remote_ip_prefix rules。在central neutron中收集到所有成员vm的ip地址,然后对应创建多个remote_ip_prefix rules。但是这种方法会带来严重的性能瓶颈。严重违背了remote_group_id的设计初衷。可靠性也比较难做。

       面对上述这些难题,tricircle则直接不支持remote_group_id特性,对于其他rule因为可以直接的拷贝到local neutron中创建并应用。

5.4 xjob异步管理

       首先一个xjob唯一性约束由(type, status, resource_id, extra_id)组合控制。

xjob rpc client会利用xrpcapi,创建一个new状态的job,保存在db里,由于创建的new状态的job,其extra_id是随机生成的,因此会出现相同资源,多个new状态的job存储在db中。xjob rpc server在处理的时候只会选择时间戳最新的new job进行处理。

其他的考虑可以参考4.5节源码的注释,这里就不再赘述了。

5.5 并发性考虑

       Tricircle负责在central neutron和local neutron之间创建对应的neutron资源,如果处理不恰当,则会出现在local neutron中创建额外的资源。为了避免这种问题,进行了一些设计考虑,具体可以参见4.3节源码的注释,这里就不再赘述了。

5.6 shadow agent/port考虑

为了实现neutron network跨越多个pod,这里设计了shadow agent/port。

       在一个pod里,会为处于其他pod的port(一般只考虑vm port)相应地创建shadow agent/port,然后欺骗本地的l2 population,让l2 population认为这些shadow agent/port是本地的,然后由l2 population机制在相应的计算节点及网络节点上生成vxlan隧道口、单播流表、arp代答流表,最终实现network跨越多个pod实现二层互通。

       但是,当计算节点规模比较庞大时,这种设计方式必然会出现性能瓶颈(vxlan隧道数量大、mq消息量大)。为了解决这个性能瓶颈问题,可以引入l2gw转发机制,如下所示:

采用l2gw机制后,本地的pod只需和本地的l2gw及其他pod的l2gw建立vxlan即可,从而大大减少vxlan隧道的数量。

但是依然还使用了l2 population机制,mq的消息量大的问题依然还存在。

 

 

0条评论
0 / 1000
黄****远
19文章数
0粉丝数
黄****远
19 文章 | 0 粉丝
原创

Neutron Tricircle

2023-05-08 08:46:32
34
0

1 前言

       本文针对开源openstack tricircle组件做如下的反向设计,以帮助理解该组件的设计原理。

2 总体架构

整体上会部署一个Tricircle集群(通常3个节点做集群即可)、多个Openstack集群。统一一套keystone集群、glance集群(不是必须)。每个openstack集群有各自的db、mq,Tricircle集群有独立的db、mq。所有neutron资源的操作都直接调用tricircle集群上的openstack neutron标准接口,其他资源(如nova)操作则调用具体的openstack集群上的openstack标准接口。

       下面是tricircle服务引入的重要组件说明:

  • Tricircle节点

该节点上主要有如下几个重要组件:

  • Tricircle Central Neutron Plugin(简称: central plugin)

负责neutron资源的集中管理,包含network、subnet、port、router、floatingip、security_group、qos等网络资源。提供标准openstack neutron api接口给上层应用操作资源的CRUD。同时也存在central plugin之间通过restapi接口调用local plugin的情况,例如查询一下bottom资源信息时。

这里会启动一个neutron server进程。

  • Admin API

对外暴露restapi,用于操作job、pod、resource routing三个重要的数据模型的CRUD。其中job用于表示一个异步任务、pod用于表示一个openstack集群、resource routing用于记录tricircle节点上的neutron资源与pod节点上的neutron资源之间的映射关系

这里会启动一个admin api的进程。

  • XJob

维护xjob异步任务。这里会启动一个xjob进程,xjob进程会作为rpc server端处理由rpc client发来的xjob异步任务。xjob进程作为rpc server端在处理异步任务的时候会调用pod的openstack neutron接口。

  • RPC

在Tricircle节点上,唯一用到的RPC通信的地方主要在于rpc client(有:central plugin及admin api)和rpc server(xjob进程)之间的交互,只有单向通信,即: rpc client -> rpc server。

  • DB

数据库服务,用于存储数据,这些数据包含: openstack neutron资源以及tricircle服务引入的三个重要数据模型:pod、job、resource routing。

  • POD节点

该节点上只需增加Tricircle Local Neutron Plugin即可:

  • Tricircle Plugin Neutron Plugin(简称: local plugin)

该local plugin相当于嵌入在neutron server和core plugin(当前使用ml2 plugin)中间,从而实现一些neutron资源处理的特殊拦截,通过拦截钩子函数实现特殊化处理。例如:create_port的时候,由于此时local plugin并未创建对应的network、subnet等相关联的资源,这种情况下,通过local plugin拦截请求,通过调用central plugin的restapi接口将network、subnet等相关资源同步创建在该local neutron中。

这里继续寄宿在原有的neutron server进程中。

       根据上述内容的描述,总结数据的交互方式有如下:

  • rpc交互方式

rpc client(central plugin/admin api) -> rpc server(xjob进程)

  • restapi交互方式

xjob(进程) ->central plugin、local plugin <-> central plugin

  • db交互方式

central plugin、admin api、xjob都需要访问db

3 数据模型

这里只介绍tricircle服务新引入的数据模型,对于已有的neutron相关数据模型则不再赘述。

3.1 pod数据模型

此数据模型用于表示一个openstack集群实例。pod/az/region_name在tricircle服务中的关系如下:

       一个az可以包含一个或多个pod。

       一个pod和一个region_name一一对应。

pod数据模型包含如下的信息:

Name

Type

Description

pod_id

string

pod对象的uuid。作为主键

region_name

string

对应openstack集群所属的region name。此region name必须在keystone里已经注册了。

az_name

string

pod所属的az。对于central neutron而言,az_name为空。如果az_name不为空,说明此pod属于此az。一个az允许包含多个pod。因此多个pod里的az_name属性值是存在相同的情况。

pod_az_name

string

实际源码中未使用该字段来参与资源调度。

dc_name

string

pod所处的机房名称。根据华为合营的做法:一个region包含多个az,一个az包含多个dc、一个dc包含多个pod。实际源码中未使用该字段来参与资源调度,估计只是用于展示而已。

 

3.2 job数据模型

       此数据模型用于表示一个异步任务。有两个数据结构,如下:

  • AsyncJob

这个数据结构用于存储异步任务,主要包含status=new、running、fail这三种状态的job信息。当前也会短时间存储一些success的job(因为是先更新job为success,然后再执行删除,因此存在比较短的时间)。包含如下的信息:

Name

Type

Description

id

string

对象的uuid。作为主键

project_id

string

租户uuid

resource_id

string

job数据。可能包含一个或多个资源id信息,如果是多个则用#分割开,例如:'%s#%s#%s' % (pod_id, router_id, net_id)

type

string

job 类型。具体有哪些类型,参加下面

timestamp

timestamp

时间戳

status

string

状态。包含:new、running、success、fail

extra_id

string

额外的uuid。主要用于实现多个worker互斥处理同一个资源。具体原理参见下文

       这个表中,虽然id作为主键,但是(type、status、resource_id、extra_id)组合有唯一性的约束条件。这样做的目的就是为了实现多worker创建new job时,只允许其中一个worker成功插入,其他worker等待。

  • AsyncJobLog

这个数据结构只用于存储status=success的job。感觉只是用于查看,其他作用在源码中暂时未看到。包含如下的信息:

Name

Type

Description

id

string

对象的uuid。作为主键

project_id

string

租户uuid

resource_id

string

job数据。可能包含一个或多个资源id信息,如果是多个则用#分割开,例如:'%s#%s#%s' % (pod_id, router_id, net_id)

type

string

job 类型。具体有哪些类型,参加下面

timestamp

timestamp

时间戳

              JT_CONFIGURE_ROUTE = 'configure_route'

JT_ROUTER_SETUP = 'router_setup'

JT_PORT_DELETE = 'port_delete'

JT_SEG_RULE_SETUP = 'seg_rule_setup'

JT_NETWORK_UPDATE = 'update_network'

JT_SUBNET_UPDATE = 'subnet_update'

JT_SHADOW_PORT_SETUP = 'shadow_port_setup'

JT_TRUNK_SYNC = 'trunk_sync'

JT_SFC_SYNC = 'sfc_sync'

JT_RESOURCE_RECYCLE = 'resource_recycle'

JT_QOS_CREATE = 'qos_create'

JT_QOS_UPDATE = 'qos_update'

JT_QOS_DELETE = 'qos_delete'

JT_SYNC_QOS_RULE = 'sync_qos_rule'

3.3 routing数据模型

该数据模型主要用于映射central neutron与local neutron之间的资源,例如:同一个network资源,在central neutron创建的uuid为id1,而在local neutron创建的uuid为id2,这样通过routing数据模型来记录id1和id2之间的映射关系,从而辅助资源的全局调度。

       包含如下的信息:

Name

Type

Description

id

biginteger

对象的uuid,作为主键。

top_id

string

在central neutron中记录的资源uuid

bottom_id

string

在local neutron中记录的资源uuid

pod_id

string

pod uuid

project_id

string

租户uuid

resource_type

string

资源类型。具体包含有哪些资源,参见下面

created_at

timestamp

创建的时间戳

updated_at

timestamp

更新的时间戳

       虽然id作为主键,但是要求(top_id、pod_id、resource_type)三者组合有唯一性的约束。这样做的目的是为了防止在local neutron中创建冗余的资源(因为涉及多worker并发处理的情况)。

RT_NETWORK = 'network'

RT_SD_NETWORK = 'shadow_network'

RT_SUBNET = 'subnet'

RT_SD_SUBNET = 'shadow_subnet'

RT_PORT = 'port'

RT_TRUNK = 'trunk'

RT_SD_PORT = 'shadow_port'

RT_PORT_PAIR = 'port_pair'

RT_PORT_PAIR_GROUP = 'port_pair_group'

RT_FLOW_CLASSIFIER = 'flow_classifier'

RT_PORT_CHAIN = 'port_chain'

RT_ROUTER = 'router'

RT_NS_ROUTER = 'ns_router'

RT_SG = 'security_group'

RT_FIP = 'floatingip'

RT_QOS = 'qos_policy'

3.4 shadow agent数据模型

shadown agent设计的目的是为了欺骗local neutron的l2population机制,从而为跨pod实现二层vxlan互通提供理论基础。包含如下的信息:

Name

Type

Description

id

string

对象的uuid。作为主键

pod_id

string

pod uuid

host

string

host id,通常为主机名。

type

string

agent类型,对我们而言对应ovs agent

tunnel_ip

string

隧道ip,即:vtep ip地址

       虽然id作为主键,但是要求(host,type)组合有唯一性约束。

3.5 endpoint数据模型

该数据模型用于同步存储keystone里记录的各个pod里各个neutron service的信息。保存的信息示例如下图所示(个人觉得只会用到neutron及keystone相关,其他在central neutron中应该用不上):

该数据模型存储了各个pod下的neutron service的endpoint url信息(public类型),借助这些url信息就可以构造neutron client,进而调用对应pod的neutron restapi接口。

       包含如下的信息:

Name

Type

Description

service_id

string

service uuid。在keystone唯一标识某个service

pod_id

string

pod uuid

service_type

string

service type,例如’neutron’

service_url

string

service url,记录的是service public url。利用这个url就可以调用对应pod的neutron restapi接口

 

3.6 其他

这个数据模型和sfc(服务功能链)相关,暂时用不上,不再赘述。

4 源码解读

       整个源码目录可分为如下几个部分:

  • tricircle/db

源码文件

说明

api.py

提供CRUD方法操作第3节所述的资源,调用core.py的接口操作db

core.py

抽象封装,适用于所有资源的CRUD db操作

migration_helpers.py

数据库升级相关,可忽略

opts.py

无重要内容,忽略

models.py

定义第3节所述的资源对应的数据库表信息,包含表的各个字段、唯一性约束、主键等信息

 

  • tricircle/api

源码文件

说明

app.py

配置wsgi app

wsgi.py

启动wsgi app入口

opts.py

无重要内容,忽略

controllers/root.py

顶层 rest接口

controllers/pod.py

pod资源相关的rest接口定义,会调用tricircle/db/api.py的接口处理

controllers/job.py

job资源相关的rest接口定义,会调用tricircle/db/api.py的接口处理

controllers/routing.py

resource routing资源相关的rest接口定义,会调用tricircle/db/api.py的接口处理

 

  • tricircle/cmd

源码文件

说明

api.py

admin api进程入口

xjob.py

xjob进程入口

manage.py

tricircle-db-manage命令执行的入口。与部署的时候创建tricircle相关的数据库表有关。

 

  • tricircle/common

源码文件

说明

baserpc.py

个人觉得无实际作用,可忽略

client.py

一个很重要的基础文件。具体说明参见4.1节

config.py

注册一些配置项,同时初始化一些mq的连接

constants.py

定义一些常量

context.py

-

exceptions.py

定义一些异常类

httpclient.py

实际中未用到,可忽略

lock_handle.py

一个很重要的基础文件。具体说明参见4.3节

opts.py

-

policy.py

-

request_source.py

实际中未用到,可忽略

resource_handle.py

一个很重要的基础文件。具体说明参见4.2节

restapp

配合admin api使用,不太重要

rpc.py

mq通道的一些初始化操作

serializer.py

配合rpc.py初始化mq通道

topics.py

定义xjob rpc topic常量

utils.py

一些工具类方法,不重要

version.py

-

xrpcapi.py

作为xjob rpc client接口,重要的基础文件,具体说明参见4.4节

 

  • tricircle/xjob

源码文件

说明

opts.py

-

xservice.py

创建service,启动service,监听rpc topic

xmanager.py

xjob rpc server回调。重要的基础文件。具体说明参见4.5节

 

  • tricircle/network

源码文件

说明

drivers/type_flat.py

继承原生的FlatTypeDriver类,处理flat网络类型

drivers/type_local.py

Local网络,我们不用,可以忽略

drivers/type_vlan.py

继承原生的VlanTypeDriver类,处理vlan网络类型

drivers/type_vxlan.py

继承原生的VxlanTypeDriver类,处理vxlan网络类型

central_fc_driver.py

和sfc相关,可以忽略

central_sfc_driver.py

和sfc相关,可以忽略

central_sfc_plugin.py

和sfc相关,可以忽略

central_trunk_plugin.py

和vlan trunk相关,可以忽略

central_qos_plugin.py

继承原生的QoSPlugin类,处理qos。

central_plugin.py

central neutron plugin, 重要的文件。

exceptions.py

异常相关

helper.py

一些辅助的方法。比较重要的基础文件。

local_l3_plugin.py

基础原生L3RouterPlugin类,只复写了get_router_for_floatingip方法,其他和原生的L3逻辑一致。

managers.py

继承原生TypeManager类。根据配置的type_dirvers注册各个type drivers。

qos_driver.py

qos driver

security_groups.py

继承原生SecurityGroupDbMixin类,处理安全组资源

local_plugin.py

local neutron plugin. 重要的文件

 

4.1 tricircle/common/client.py解读

       此文件中提供了class Client,该类主要是openstack service client的操作封装,通过该类的对象,可以操作openstack的所有资源,包含CRUD操作。

       虽然该类可以适用于所有的openstack services,但是在tricircle组件中,该类只用于openstack neutron service,因此该类的对象也可以简单看成是一个openstack neutron client,从而能够操控neutron资源的CRUD。

       首先来看下该类的__init__初始化工作,具体说明可参见图中的注释,这里不再赘述。

上图中的handle_obj.support_resource的信息如下:

support_resource = {

        'network': LIST | CREATE | DELETE | GET | UPDATE,

        'subnet': LIST | CREATE | DELETE | GET | UPDATE,

        'port': LIST | CREATE | DELETE | GET | UPDATE,

        'router': LIST | CREATE | DELETE | ACTION | GET | UPDATE,

        'security_group': LIST | CREATE | GET,

        'security_group_rule': LIST | CREATE | DELETE,

        'floatingip': LIST | CREATE | UPDATE | DELETE,

        'trunk': LIST | CREATE | UPDATE | GET | DELETE | ACTION,

        'port_chain': LIST | CREATE | DELETE | GET | UPDATE,

        'port_pair_group': LIST | CREATE | DELETE | GET | UPDATE,

        'port_pair': LIST | CREATE | DELETE | GET | UPDATE,

        'flow_classifier': LIST | CREATE | DELETE | GET | UPDATE,

        'qos_policy': LIST | CREATE | DELETE | GET | UPDATE,

        'bandwidth_limit_rule': LIST | CREATE | DELETE | GET | UPDATE,

        'dscp_marking_rule': LIST | CREATE | DELETE | GET | UPDATE,

        'minimum_bandwidth_rule': LIST | CREATE | DELETE | GET | UPDATE}

       实例化Client对象之后,后面就可以用该对象来操作资源了,无论是CRUD什么类型的资源,最终都会调用该对象中的create/update/delete/get/list/action_resources方法,其中第一个参数resource则为资源的类型。方法里会继续调用到resource_handle.py中的NeutronResourceHandle类对象的handle_create/update/delete/get/list/action方法。

       还有个细节需要注意:Client对象里的create/update/delete/get/list/action_resources方法都使用了@_safe_operation作标注,有这个标注的方法,在调用的时候会执行下面的函数(client.py文件最上面的函数):

除了图中给出的注释外,还有如下几个重要的函数需要说明:

  • _ensure_endpoint_set

上图中最终确保resource_handle对象配置上self.endpoint_url=region_name对应neutron service的public url,从而能够构造出该region_name的openstack neutron client调用neutron接口。

  • _update_endpoint_from_keystone

该方法首先会从keystone db中获取所有的service及endpoint信息,最后构造出:region_service_endpoint_map[region_id][service_name] = url(public url)。接着会根据region_service_endpoint_map的信息更新或创建对应的cached endpoint表项。

总结:使用该类的对象,执行如下的方法

create/update/delete/get/list/action_resources---》接着调用resource handle中的方法handle_create/update/delete/get/list/action---》接着使用_get_client方法获取到openstack neutron client(具体某个pod的,使用region_name查询),最后用openstack neutron client调用neutron资源接口(CRUD)。

4.2 tricircle/common/resource_handle.py解读

       该文件中存在两个类,父类为:ResourceHandle,基类为:NeutronResourceHandle。

       这个文件在central neutron端使用时会结合4.1节的Client类来使用,这个时候,父类ResourceHandle中的成员self. endpoint_url=具体pod对应neutron service的public url;当这个文件在local neutron端使用时,会直接设置父类ResourceHandle中的成员self. endpoint_url=central_neutron_url(从配置解析出来的)。

       父类为:ResourceHandle中的成员self.auth_url为keystone的认证url。因此该文件中提供了一些获取keystone token的方法。

       基类为:NeutronResourceHandle中的方法_get_client可用于获取一个openstack neutron client,从而用这个client来调用neutorn资源的CRUD接口。

       该文件中还存在各种handle_xxx方法,这些方法里最后都调用openstack neutron client来调用具体的接口。

4.3 tricircle/common/lock_handle.py解读

       这里有两个重要的函数,如下:

  • get_or_create_route

       说明见图中注释。

  • get_or_create_element函数

说明参见图中注释。

4.4 tricircle/common/xrpcapi.py

       该文件中针对每一个job type会对应一个处理方法,这些方法中会调用invoke_method方法,invoke_method方法中首先会插入一个new状态的job entry,然后通过RPC通知xjob进程(rpc server端)。

4.5 tricircle/xjob/xmanager.py解读

       首先要分析下文件中最顶层的方法:_job_handle,直接分析while True里面的内容即可。

图中注释已说明。

对于get_latest_job方法,获取某个status下的资源,可能会获取到多个,因为存在多个worker同时向db中插入某个status且相同资源的情况。处理只需返回最新的job即可。其他旧的job依然存在db中,待最新的job成功处理之后,会将这些冗余的job都删除掉。

对于register_job方法,有个特殊的地方是:extra_id使用的是全0的uuid,这样设计的原因是为了防止多个worker同时注册同一个job,由于(type,status,resource_id,extra_id)组合存在唯一性,而extra_id全0,因此对于同一个job而言,最终只能有一个worker注册成功。

说明参见图中注释。

上图中,finish_job方法需要说明下:

说明参见图中注释。

       上图注释中说到:对于fail的job会有周期的任务再次触发尝试,这个周期任务函数如下:

注意添加了注解:“@periodic_task.periodic_task”。

xmanager作为xjob rpc server回调,各个回调方法都使用了注解:@_job_handle,例如:

同时,xmanager还会同时承担xjob rpc client,因为在处理rpc server回调方法时,需要处理那些未明确指定pod的逻辑,对于这种情况,本地查询出所有关联的pod,然后再通过xrpcapi将job分发到具体的pod,例如:

总结:xmanager主要的工作内容是处理xjob rpc server回调函数的处理,同时会启用周期任务处理一些fail job。xmanager会同时承担xjob server和xjob client的角色。

5 设计考虑

5.1 DHCP考虑

       为了避免多个local neutron自动创建dhcp port,出现dhcp port ip地址冲突的问题,由central neutron统一管理dhcp port。当central neutron处理subnet创建时,会同时创建一个dhcp port,即:为该子网预留一个dhcp ip。当local neutron创建对应的subnet时,local neutron会调用central neutron的接口查询预留的dhcp port,然后会使用该port的ip创建local neutron subnet的dhcp port。

       因此,对于一个subnet而言,对应的dhcp ip在所有的local neutron中都是完全相同的。

       Dhcp ip地址完全相同,这样会出现一个pod的vm的dhcp请求会发往其他pod的dhcp吗?对于这个问题,可以利用l2 population机制下发的单播流表来拦截。因为其他pod的dhcp port在本地的pod不存在,也不会创建对应的shadow port(参见5.6),因此通过单播流表可以实现dhcp的请求被发往到其他pod。

       当配置dhcp_agents_per_network=xxx多个dhcp实例时,central neutron是否会创建多个IP的dhcp port?local neutron会对应创建多个dhcp namespace?这个能力有待验证。

5.2 Qouter默认网关考虑

       当network需要跨越多个pod时,这个时候需要在不同的pod上给subnet创建默认网关ip。Tricircle当前的设计是:不同的pod下,同一个subnet,默认网关ip是不同的。它这样做的目的是为了能够在本地pod下完成三层路由转发,而如果每个pod的默认网关ip都相同,则会出现三层路由转发的流量走到其他pod上,这样路径并非是最佳同时也是不可预测的。

       为了实现上述的目标,在local neutron创建subnet之前,会先去调用central neutron的接口创建默认网关port,local neutron会使用这个port的ip作为在local neutron的subnet默认网关IP。在central neutron创建的默认网关port的命名方式包含了:local neutron所属的region_name以及subnet uuid,从而实现同一个subnet,不同pod有不同的默认网关IP。

       其实,如果用分布式qrouter,不同的pod下,同一个subnet使用不同的默认网关IP也是没问题的,三层路由转发的流量不会跑到其他pod(因为br-tun上的流表已经做了限制,不允许请求qrouter的流量跑到其他计算节点,也就不会出现三层路由的流量跑到其他pod了)。

5.3 安全组考虑

       对于安全组而言,一个难点就是支持remote_group_id特性。该特性实现的目标是:让关联了安全组id=remote_group_id的vm之间能够相互访问。

       但是考虑两个vm,vm1在pod1,vm2在pod2,这个时候,我们不能在这两个pod下都创建安全组,拷贝central neutron中的所有rules到这两个pod。因为在pod1上并没有vm2的port,不能通过remote_group_id的特性配置iptables规则,让vm2能够访问vm1.

       为了支持remote_group_id,考虑有如下几个方法:

       方法1:在pod下都创建其他pod的port,因此也需要对应创建network、subnet。这样做会带来很多不必要的资源消耗。

       方法2:扩展remote_group_id的rule,将这种rule转换为多个remote_ip_prefix rules。在central neutron中收集到所有成员vm的ip地址,然后对应创建多个remote_ip_prefix rules。但是这种方法会带来严重的性能瓶颈。严重违背了remote_group_id的设计初衷。可靠性也比较难做。

       面对上述这些难题,tricircle则直接不支持remote_group_id特性,对于其他rule因为可以直接的拷贝到local neutron中创建并应用。

5.4 xjob异步管理

       首先一个xjob唯一性约束由(type, status, resource_id, extra_id)组合控制。

xjob rpc client会利用xrpcapi,创建一个new状态的job,保存在db里,由于创建的new状态的job,其extra_id是随机生成的,因此会出现相同资源,多个new状态的job存储在db中。xjob rpc server在处理的时候只会选择时间戳最新的new job进行处理。

其他的考虑可以参考4.5节源码的注释,这里就不再赘述了。

5.5 并发性考虑

       Tricircle负责在central neutron和local neutron之间创建对应的neutron资源,如果处理不恰当,则会出现在local neutron中创建额外的资源。为了避免这种问题,进行了一些设计考虑,具体可以参见4.3节源码的注释,这里就不再赘述了。

5.6 shadow agent/port考虑

为了实现neutron network跨越多个pod,这里设计了shadow agent/port。

       在一个pod里,会为处于其他pod的port(一般只考虑vm port)相应地创建shadow agent/port,然后欺骗本地的l2 population,让l2 population认为这些shadow agent/port是本地的,然后由l2 population机制在相应的计算节点及网络节点上生成vxlan隧道口、单播流表、arp代答流表,最终实现network跨越多个pod实现二层互通。

       但是,当计算节点规模比较庞大时,这种设计方式必然会出现性能瓶颈(vxlan隧道数量大、mq消息量大)。为了解决这个性能瓶颈问题,可以引入l2gw转发机制,如下所示:

采用l2gw机制后,本地的pod只需和本地的l2gw及其他pod的l2gw建立vxlan即可,从而大大减少vxlan隧道的数量。

但是依然还使用了l2 population机制,mq的消息量大的问题依然还存在。

 

 

文章来自个人专栏
云网络
19 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0