Ceph RGW整体结构,最全干货在这!

2020-05-20 10:40:32 浏览数 (1)

小新 职场新人,存储小白 立志成为职场老鸟,存储专家;

影视迷,东野迷。

  友情提醒:以下内容有点干,请自备快乐水~

一、前言

Ceph中的对象存储网关RadosGW和Ceph RBD以及CephFS一样,构建在librados之上,主要提供的命令工具有如下:

  • radosgw : 用来启动radosgw服务,并且提供restful的访问方式,也是下文讨论的对象
  • radosgw-admin : 用来提供admin的管理服务,如创建user等

另还有radosgw-es、radosgw-token和radosgw-object-expirer

目前RadosGW(下称RGW)主要提供两种接口:Amazon S3 RESTful接口和OpenStack的Swift接口。其中S3接口是由Amazon提出的标准化的对象存储接口,可以使用该接口标准对接其他支持S3标准的对象存储系统,OpenStack Swift本身就可以提供分布式对象存储,使用的是Swift接口,RGW为了可以对接OpenStack,也支持Swift接口。

由Ceph的存储架构可知,RGW、RBD和CephFS可以使用同一套Rados集群,所以可以同时提供上述的三种服务。对于RGW而言,S3和Swift两个接口类型可以使用同一个存储空间(如.rgw.data),因此,可以使用两种接口对Object数据进行读写。

目前使用的Ceph版本为14.2.8(Nautilus),RGW相比L版本新增的特性有如下:

1、默认frontend从civetweb换成beast,整体性能有所提升

2、支持在placement中设置storage class,并支持lifecycle跨placement进行数据处理

3、支持对象锁,新增6个对象锁的API

4、新的发布/订阅基础设施以支持serverless框架(如knative)或数据管道(如kafka)

5、支持List Object V2等。。。

二、整体架构

RGW的总体启动流程和作用如下:

1、启动frontend(即一个响应http请求的web服务器)

2、然后接受http请求,将http请求进行封装为RGWRequest

3、然后根据请求的uri和method等rest请求信息(包含在RGWProcessEnv)

4、获取到相应的RGWOp和RGWHandler_Rest

5、然后由Scheduler调度某个Thread来执行相应Handler的op操作

6、并根据结果封装返回体进行返回

下面主要从代码的角度,分别从启动过程和启动之后处理请求两个阶段来进行讨论。

1、RGW启动时架构

下面是针对N版本的RGW的启动整体架构图:

架构说明  

RGW主要由上述的三部分组成,首先是Frontend,简单理解就是HTTP服务器,响应前端http请求;然后是REST APis,主要就是采用的REST Api的格式,以及对应API处理的MGR;以及RGWProcess,将RGWRados等信息封装到一起交由RGWProcess进行处理。

Frontend  

用来提供HTTP服务请求的监听,并根据请求的相关信息,封装参数、转换请求(http request -> RGWRequest)、调度任务、转发给Handler进行处理。Frontend目前支持的类型包括Civetweb(也是N版之前默认的Frontend)、Beast(当前N版的默认Frontend)、Loadgen和FastCGI。在配置文件中可以同时配置多个Frontend,每个Frontend对应有自己的配置,存储在RGWFrontendConfig中,使用multimap来存储每个Frontend以及对应的RGWFrontendConfig。

Civetweb来自于Mongoose,是一款强大的嵌入式Web网络服务器,简单和性能之间的均衡,在RGW中的实现类是RGWCivetWebFrontend。Beast是新加入进来的Frontend,主要是采用异步IO的方式来实现请求的处理,相比Civetweb而言,性能上有所提升(有待详细对比),在RGW中的实现类是RGWAsioFrontend。另外的Loadgen和FastCGI(FCGI)相对而言在RGW中使用的较少,在RGW中的实现类分别是RGWLoadGenFrontend和RGWFCGXFrontend,具体可以参考详细的使用说明。

总结:

  • Frontend Config ( multimap<string, RGWFrontendConfig *> )
  • Civetweb ( RGWCivetWebFrontend )
  • Beast ( RGWAsioFrontend )
  • Loadgen ( RGWLoadGenFrontend )
  • FastCGI/FCGI ( RGWFCGXFrontend )

Rest Apis 

主要体现在具体可以接收和处理的API的类型,通过RGWREST来进行API资源的注册,同样可以同时支持多个API类型,目前版本所支持的API类型有:

  • S3 (Amazon的S3标准API,重点讨论)
  • Swift (对接Openstack的API)
  • Swift Auth (Swift的授权认证API)
  • Admin (提供Admin的API访问,例如创建user等操作)

每个API类型对应一个主MGR(可以理解为该API类型的处理方法集),主MGR还可以注册多个子MGR(此处的‘主’和‘子’只是我在理解上的逻辑分类,实际代码中没有明确的主次之分),因此每个API可以对应有多个MGR;每个MGR中维护该MRG所支持的Resource(可以理解为所支持的具体的handler列表),可以将每个MGR看作是一种Resource;每个Resource同时还可以包含有多个Handler(即处理相应资源请求的类,例如处理Bucket的Handler),每个Handler中还有多个OP(即具体的操作,例如GetOp等),关系图如下所示:

下面以S3协议类型的API作为例子详细进行解析,其他的API类型基本上跟如下的流程和关系一致,部分区别在有的API的主MGR下面还注册有多个子MGR,如Swift,另外有的API的Handler中通过重写的方式实现部分OP。

下图中的S3的API类型的整体关系图和上面的基本一致,S3的API的MGR只有一个,并且设置为default mgr,下面的详细示例图中选择Obj的相关Handler进行介绍,其中包含的OP基本上就是HTTP请求中常见的几个请求Method,如GET,PUT,DELETE等。每个OP中还包含有一个或多个具体的操作实现类,例如op_get中会有GetObj,GetObjAcl,GetObjTag等,如下图中列举的是op_delete的具体操作。

RGWProcess 

此过程就是封装请求,在RGW启动之后,创建好了Frontend,并运行了起来,用来监听来自前端的HTTP的请求,刚刚上一步完成了API的注册,接下来就是等待请求的到来。此处的RGWProcess主要是体现出在请求来了之后,需要准备的资源和参数,进行封装好之后传递给真正的RGWProcess进行处理,也就是rgw_process.cc/process_request()方法进行处理,之后的处理过程在下面有介绍。

从上述的整体架构图来看,处理一个请求需要的几个重要参数资源,如下:

  • RGWProcessEnv
  • RGWRequest
  • RGWRestfulIO
  • RGWRados
  • Scheduler
RGWProcessEnv  

用来存储在RGWProcess过程中的非常重要的环境变量,例如:RGWRados等,其结构体的定义如下:

代码语言:javascript复制
struct RGWProcessEnv {  RGWRados *store;  RGWREST *rest;  OpsLogSocket *olog;  int port;  std::string uri_prefix;  std::shared_ptr<rgw::auth::StrategyRegistry> auth_registry;};在创建Frontend的时候通过RGWProcessEnv将RGWRados等参数传递给Frontend。
RGWRequest  

来自前端的HTTP请求,需要转换成对应的RGWRequst,这个过程也是RGW处理流程的重要一个流程,主要用来存储维护请求的id、请求的状态信息以及HTTP请求转化为RGWRequest之后的OP句柄等。其中req_state(即请求状态信息)存储整个请求过程中的状态信息,包括有:错误码和错误信息、op和op_type、trans_id和host_id等信息。

RGWRestfulIO 

用来与Rest Client进行交流的接口,除了提供常规的Client的功能之外,还有类似Accounter计数器、AWS Auth V4等功能,通过过滤器的模式来提供不同的功能,屏蔽了复杂的pipeline结构,如下示例。

代码语言:javascript复制
auto real_client_io = rgw::io::add_reordering(    rgw::io::add_buffering(cct,        rgw::io::add_chunking(          rgw::io::add_conlen_controlling(&real_client))));
RGWRados 

RGWRados作为RGW与Rados进行沟通的接口适配器,里面封装了所有RGW需要去操作底层Rados的操作接口,上层的RGW操作最终都是通过该适配器与底层的Rados进行沟通。

Scheduler 

Scheduler主要是用来进行任务的调度,当请求需要处理的时候,会由相应的Scheduler来进行调度,调度的流程大概是:

1、获取调度器所需参数(client,cost等)

2、然后获取Executor

3、接着根据获取的Executor创建Completion(看做是待处理的请求)

4、然后将创建的Completion转换为Request的unique_ptr

5、最后将创建的Request的unique_ptr添加到任务执行的工作队列中

当前版本中的Schedule采用的dmClock(distribute mClock)算法来进行实现的,可以实现QoS功能,dmClock主要包含有Reservation(下限)、Limit(上限)和Weight(权重)三个控制参数,采用基于时间便签的队列模式,将请求映射到时间轴来确定请求应该被处理的时刻。

支撑性组件 

在进行RGW初始化的过程中,需要将系统的一些支撑性的组件完成初始化,例如日志、配置、认证等,为其他的组件在运行的过程中提供资源。

Config 

Config组件,里面存储了所有的参数值,如果没有指定配置,则会给一个默认的参数值,在代码层面可以在src/common/options.cc中查找到是所有配置的默认值和说明,例如,可以获取frontend的配置、apis的配置、log配置等信息。在RGW的启动过程中,可以使用g_conf来获取相应的参数,该方法通过ConfigProxy的方式来进行配置的获取和修改,其中ConfigProxy中采用Seastar来进行实现。

TracePoint 

实现代码的Trace功能,可以追踪程序的执行过程,进行程序的流程分析,通过ceph的上下文进行初始化,在RGW这一层主要提供两种Trace的时间对象,分别用来Trace RGWOp的操作过程,以及Trace RGWRados的操作流程,如下所示:

代码语言:javascript复制
TracepointProvider::Traits rgw_rados_tracepoint_traits("librgw_rados_tp.so", "rgw_rados_tracing");TracepointProvider::Traits rgw_op_tracepoint_traits("librgw_op_tp.so", "rgw_op_tracing");
TracepointProvider::initialize<rgw_rados_tracepoint_traits>(g_ceph_context);TracepointProvider::initialize<rgw_op_tracepoint_traits>(g_ceph_context);
Timer 

主要作用是执行定时任务,Timer在RGW中的使用主要体现初始化超时、日志打印以及Realm重新加载中,主要的功能如下:

1、使用SafeTimer创建相应的定时器

2、将任务(事件event)添加到事件队列中

3、定期轮询事件队列中的事件是否到了处理的时间,如果没有则继续轮询,如果到了则执行

4、也可以取消指定事件或者所有事件等。

SafeTimer中的事件存储采用的multimap的方式,其中key是时间,由multimap的特性可知默认采用升序排序,即multimap中的第一个元素的时间应该是最小的,如下所示。

代码语言:javascript复制
std::multimap<utime_t, Context*> schedule;std::map<Context*, std::multimap<utime_t, Context*>::iterator> events;

说明:

1、schedule这个map存储将要执行的事件信息,时间作为key,实际调度执行的map

2、events是用来检查事件是否正确添加或者取消,例如:是否有重复添加的,或者是否要取消一个不存在的事件等;

因此对于事件处理来说,schedule中存储的事件是按照时间小到大排序,也就是时间越小的越排在前面,因此,在检查事件是否到时间的时候,首先检查第一个事件执行时间是否到了,如果没到,那后面的事件都不用检查了,如果到了,则取出来进行执行。

Log 

在RGW中的日志体现在两个方面,一个是使用dout以及各种日志函数等进行日志的输出,另一个是采用OpsLogSocket实现的操作日志,会记录RGW中的每个操作记录,可以通过参数设置是否将操作的日志输出到Rados中,或者输出到Scoket中进行读取,如下所示:

代码语言:javascript复制
# 启动ops的日志记录rgw enable ops log# 配置是否将ops log记录到raods中rgw ops log rados
# 配置ops log记录的socket地址rgw ops log socket path
# 最少累积多少条ops log才写入socket中rgw ops log data backlog

note: 有些参数不知道其作用的时候,可以在src/common/options.cc中查找到相关说明

Auth 

在RGW初始化的时候进行Auth Registry的创建,在后面创建Frontend的时候会将Auth Registry信息添加到FrontendEnv中,对请求进行验证和授权。

Auth的简要的认证过程如下:

1、通过在不同场景下使用get_swift、get_s3_main或者get_s3_post进行Strategy的获取

2、然后根据具体实现调用相应的Strategy的authenticate方法

3、在authenticate方法中加载相应的Engine进行验证

在验证的Engine方面,根据不同的场景下的不同Strategy有如下类型:

1、S3AnonymousEngine: 响应匿名请求的验证

2、ExternalAuthStrategy:外部验证Engine的引入

▶ EC2Engine:即为Keystone的验证引擎

▶ LDAPEngineA:将S3的Signature转换成相应的LDAP的user/pass向LDAP服务进行验证

3、LocalEngine:直接使用保存在meta pool中的aksk进行验证

4、STSAuthStrategy:是AWS的Web验证服务,根据返回的临时安全Token进行后续验证,可以传入IAM策略;

Signal Hanlder 

在Frontend运行之前会进行系列信号处理Handler的注册,来响应运行过程中的各种系统信号,例如Ctrl C,在RGW中注册的信号有如下几种:

1、SIGTERM:软终止进程,可以阻塞、处理和忽略

2、SIGINT:终止进程,终端输入Ctrl C

3、SIGUSR1:用户自定义信号

4、SIGALRM:时钟的定时信号(如提供给Timer使用)

5、SIGHUP: 终端挂起(断开连接),如&符提交的

并且每个信号有相应的信号处理Handler,在RGW进程进行shutdown的时候,会将已经注册的信号和处理进行Unregister来释放相应的资源,同时还会释放其他的各种资源(如Frontend等)。

2、RGW启动后处理请求的架构

在上述的介绍中可以看到也有RGWProcess,与下面的RGWProcess有所不同,上面的介绍的RGWProcess是在Frontend中封装的各种参数然后传递给process_request,然后请求的处理就离开了Frontend,来到了rgw_process.cc中的process_request,即下面开始介绍的RGWProcess。

架构说明  

结合前面RGW启动时的架构和RGW启动后处理请求的架构可以看出RGWProcess的主要作用就是接收来自Frontend的请求,进行相关的参数处理和验证之后,将请求转发到后端具体Mgr的Handler进行处理,由此就到每个具体功能的处理Handler中进行任务的执行,从而完成整个RGW的请求处理的架构流程。

process_request 

作为Frontend传递请求信息过来的入口,首先,接收来自Frontend的参数信息,

主要包括:

1、RGWRados

2、RGWRestfulIO

3、RGWRequest

4、Scheduler

5、RGWProcessEnv

根据传入的参数得到相应的对象变量,如下所示:

1、RGWRestfulIO  ->  RGWEnv

2、RGWEnv, RGWRequest  ->  RGWUserInfo, req_state

3、RGWRados, req_state  ->  RGWObjectCtx

该部分主要就是进行基本参数的转换,将Frontend传递过来的参数转为接下来所需要的对象。其中RGWEnv中主要包含了请求头的基本参数,如下图所示,因此可以由该对变量生成req_state结构体的对象。

get_handler 

这个部分最主要的作用就是根据上面封装好的参数,获取相应的Mgr,以及处理对应请求的Handler,可以根据Handler获取具体操作的RGWOp对象,然后就可以通过使用RGWOp对象来进行具体的执行,因此这个过程非常重要。

传给get_handler参数除了上述架构图中的之外,还另外有frontend_prefix和Auth模块中的auth_registry,通过frontend_prefix和req_state的参数来匹配具体的Mgr和Handler,用auth_registry进行权限验证,流程如下:

1、其中,在进行preprocess之后,通过RGWREST中的RGWRESTMgr对象调用get_manage获取符合req_state和frontend_prefix匹配条件的RGWRESTMgr

2、然后通过取到的RGWRESTMgr来获取符合req_state和frontend_prefix的请求条件,且通过auth_registry认证通过的RGWHandler_REST

3、对取到的RGWHandler_REST进行init初始化工作;

scheduler_request 

对于Scheduler,其默认的调度类型是throttler,也可以配置使用dmclock,但是当前版本的dmClock还是处于实验阶段,调度的大概流程如下:

1、通过get_handler取到特定的RGWHandler_REST之后,通过handler取到当前请求的RGWOp对象

2、然后对该RGWOp对象调用scheduler_request进行请求处理的调度

3、根据传入的Scheduler进行scheduler_request调度

对于dmclock的调度器而言,其有同步和异步两种实现:AsyncScheduler和SyncScheduler

verify_requester 

此处调用RGWOp的verify_requester,根据不同的API类型来进行请求的权限验证,如下是不同的类型对应的验证方法:

1、S3 API:RGWHandler_REST_S3::authorize

2、Swift API:RGWHandler_REST_SWIFT::authorize

3、Swift Auth API: RGWHandler_SWIFT_Auth::authorize

4、IAM:RGWHandler_REST_IAM::authorize

postauth_init 

此处调用的是RGWHandler_REST的postauth_init,主要校验的对像如下:

1、BucketName: 校验bucket name是否符合Amazon的规格,例如必须字母,数字或下划线开头

2、ObjectName:

  • 主要校验长度是否小于1024字节
  • 编码格式是否是UTF-8

3、Tenants:主要校验Tenant名中是否由非法字符

4、MFA:校验请求头中的HTTP_X_AMZ_MFA

  • 检验长度是否合法
  • 检验User是否含有有关参数
  • 检验MFA值的合法性

rgw_process_authenticated 

最后来到了最终将要执行的地方的,主要涉及到的操作有如下:

 1、RGWHandler_REST::init_permissions : 初始化RGWOp的操作权限   - 如果RGWOp是Create Bucket,则rgw_build_iam_environment   - 如果不是,则do_init_permissions进行权限初始化

2、RGWHandler_REST::read_permissions :根据不同的OP来获取RGWOp的操作权限

  - 如果是只有Bucket才有的操作,则直接忽略   - 如果不是,则通过rgw_build_object_policies获取RGWAccessControlPolicy信息

3、RGWOp::init_processing:初始化RGWOp的执行

  - 获取Bucket的quota   - 获取User的quota

4、RGWOp::verify_op_mask:通过位运算的方式来判断当前操作是否在支持的操作列表中

5、RGWOp::verify_permission: 检验是否对当前操作有权限   - 例如当前用户是否可以list bucket等

6、RGWOp::verify_params: 对于有些OP而言,需要校验参数信息   - 例如Put Obj中Obj的大小是否超出最大限制等

7、RGWOp::pre_exec():根据每个具体RGWOp的pre_exec   - 例如dump一下bucket的状态信息(指的是Bucket相关OP)

8、RGWOp::execute(): 执行具体操作的地方,由每个RGWOp的execute方法完成

9、RGWOp::complete():完成请求的操作,并且根据执行结果封装response

3、总结

上面从RGW启动的过程,和启动之后处理请求的两个阶段讨论了从代码层面来看的RGW架构,至此对RGW的整体结构和大致流程有了初步认识,接下来就应该针对请求的详细处理过程进行分析。

欲知后事如何,且听下回分解。

三、参考

Ceph v14-2-0-nautilus releases

(https://docs.ceph.com/docs/master/releases/nautilus/#v14-2-0-nautilus)

CivetWeb

(http://civetweb.github.io/civetweb/) Background on http frontends(civetweb and beast

https://www.cnblogs.com/dengchj/p/11436758.html

Loadgen

https://www.loadgen.com/ Ceph dmClock介绍

https://docs.ceph.com/docs/master/rados/configuration/osd-config-ref/ Seastar介绍

https://zhuanlan.zhihu.com/p/30738569 Ceph STS Auth介绍

https://docs.ceph.com/docs/master/radosgw/STS/ Ceph LDAP Auth介绍

https://docs.ceph.com/docs/master/radosgw/ldap-auth/

还在为容器时区困扰?送你一剂良药!

玩转K8S AdmissionWebhook

· END ·

记得文末点个在看鸭~


点就完事儿了!

0 人点赞