您的位置:首页 > 运维架构

Nova Data Flow(Newton版)

2017-08-10 17:21 218 查看


一、简介





首先看看Nova的架构图,里面包含了Nova的主要组件,各组件的关键功能:

API :进入Nova的HTTP接口;
Scheduler:从可用池中选择最合适的计算节点(物理机)来创建虚拟机实例;
Conductor:为数据库的访问提供一层安全保障,避免Compute直接访问数据库;
Compute:管理虚拟机的生命周期;

还有其他的几个组件,可以通过systemctl |grep nova查看:





nova-novncproxy:用户可以通过多种方式访问虚机的控制台,该种是基于Web浏览器的VNC访问;
nova-consoleauth:负责对访问虚机控制台提供Token认证;
nova-cert:提供x509证书支持


二、API服务

在nova/setup.cfg中,可以看到nova-api=nova.cmd.api:main,api的入口为cmd目录下的api.py文件中的main()函数:

config.parse_args(sys.argv)



解析参数

具体的实现是:

这里主要是一些参数的初始化设置,同时把nova-api运行时需要的参数传递给cfg,接下来我们逐行分析:

首先看CONF=nova.conf.CONF,在conf/__init__.py中,大致看一眼是什么:



CONF是在oslo.config中定义的一个类,作为参数传入nova,作为在代码各处注册配置项的实例,和上图相似:

CONF实例化后被传入,调用到log库,会注册log相关的一些配置项

这部分通过判断[glance]组下的debug配置线,在log中选择启用glanceclient的报警类别

最终调用到下面这个:

可以看到这个就是设置下cfg文件中的_transport_opts参数,即默认的exchange是叫做nova。

追入代码能够知道该行是设置oslo.middleware的默认配置项,接下来:

其中argv是parse_args方法传入的第一个参数,即sys.argv,作为启动服务时命令行传入的参数,我们依据systemctl start openstack-nova-api的命令追溯到openstack-nova-api的老巢:

很可惜,ExecStart执行的命令后面没有跟--config-file /etc/nova/nova.conf这样预想的内容,所以argv[1:]即对应openstack-nova-api,default_config_files为None,在oslo.config中,实例化CONF时,会判断如果default_config_files为空的时候,会依据目录位置及项目名,找到nova.conf文件,这步将nova的CONF进行实例化。

这两步将rpc和db的配置项进行了初始化。可见参数的解析也有一定内容。


为nova设置logging

进入oslo.log可以看到,setup方法首先判断Nova是否有log相关的配置文件的传入,如果没有传入则使用nova.conf,然后对nova创建异常hook,当nova-api产生异常时,则会被hook,从而进行log的输出。这里不详细分析excepthook如何创建及工作,对应方法为_create_logging_excepthook(product_name)


monkey patch

monkey patch指的是在运行时动态替换,一般是在startup的时候.

用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉.这样我们在后面使用socket的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了.

之前做的一个游戏服务器,很多地方用的import json,后来发现ujson比自带json快了N倍,于是问题来了,难道几十个文件要一个个把import json改成import ujson as json吗?

其实只需要在进程startup的地方monkey patch就行了,是影响整个进程空间的。同一进程空间中一个module只会被运行一次,给出一个demo,容易理解:

再回到程序中,这里会判断配置文件中是否开启,如果开启了,会读取配置文件中的monkey_patch_modules内容,然后进行替换。默认是不开启的


注册所有的objects

这里也可以撇一眼具体内容:



__import__ 和import功能相同,不同的是作为函数,传入的是字符串格式,将nova.objects下的所有内容import进来

nova/objects目录下每一个类都对应数据库中的一个表,是操作数据库的最终接口,conductor、api操作数据库时都是经过objects


获取服务的启动器

追溯到process_launcher()方法的源头,在oslo.service中:

可以看到这段代码是为了获取一个ProcessLauncher的对象,这个ProcessLancher应该指的就是目前的这个进程,其后它会启动一些worker,并作为这些worker的父进程。这里的信号量主要就是捕获signal.SIGTERM和signal.SIGINT,如果得到了这两个信号的话就会设置ProcessLauncher的running属性为false。


启动对应的api

这里的CONF.enabled_apis对应配置文件的内容:enabled_apis = ec2, osapi_compute, metadata弄清这些是什么之前我们先看看service.WSGIService是什么:

该初始化方法通过paste库生成WSGI的app,不过这里的app只是其一个属性,这里的wsgi.Server会监听对应的端口,也就是说这个时候wsgi app已经起来了,实际在后面真正启动,我们看看刚才的enabled_apis具体是啥(运行代码时调试打印):

name属性即这个WSGIService的名字

manager只有metadata有,在配置文件中进行配置,搜索规则如下:

loader是用于解析paste.ini生成的app,在这里都是用的默认的;

app即paste生成的WSGI的app,请求来了都发往这些app

host和port即监听路径及端口

worker则是具体的工作数,一般默认是一个CPU核对应一个,原因后续分析。继续看代码:

这个地方比较关键,在__init__中,可以看到paste读取的配置项定义在CONF.wsgi.api_paste_config中,值为api-paste.ini,即load_app方法中会去/etc/nova/api-paste.ini文件中加载对应app,该文件解析不再详述。继续看:

_check_service_base检查传入的service类型是否为ServiceBase,warp为普通结构体,主要初始化了self.service、self.workers、self.children、self.forktimes这些内容,主要看self._start_child()方法:

可以看到,这里主要就是做一个fork的工作。fork的返回值如果是0的话那么当前的上下文就是子进程。否则就是在父进程中。

首先先看父进程的,父进程做的事情比较简单:启动子进程,然后记录一些信息(更新wrap),然后返回子进程的PID。

然后再看子进程的,子进程的话先通过launcher = self._child_process(wrap.service)启动对应的服务,然后就是while循环等待信号。如果有终止的信号量就结束,否则则重新启动进程。所以我们来看下launcher = self._child_process(wrap.service)做了什么吧,这里的service就是我们上面的WSGIService对象(有WSGI app在里边):

首先是设置一些信号量,然后定义一个Launcher对象。这个对象和eventlet有关,可以看成就是启动一个线程。其最终会调用WSGIService的start方法(入口在service的add方法中self.tg.add_thread(self.run_service,
service, self.done)的self.run_service里面):

self.server.start()即启动了一个WSGI的服务。

这里和普通的多线程模型下的HTTP server有一个很大的不同:普通的HTTP server在启动的时候只有一个线程在那里监听一个端口,来了一个请求才会fork一个线程去做独立的处理(也就是说如果请求很多的话,线程个数也会很多)。但这里由于使用了eventlet的绿化(本质就是协程,可以看这里),因此对于协程来说,一个协程只能运行在一个CPU核上,并且不存在来个请求就fork这种东西,所以这里会根据CPU的核的个数去建立对应的协程(worker)。当一个请求来了过后呢其就会交给某一个协程去处理。不管请求个数多少,协程的个数是固定的。协程在HTTP
server方面的效率比较高。


监听并服务

代码中,我们知道这里就是看自己的子进程(worker)是否挂了,挂了的话启动起来就行了。当然啦如果是收到了停止的信号量,那么就kill所有的子进程,然后launcher这个父进程也退出循环并结束了。


总结

nova-api的启动过程就是读取配置文件,生成TRANSPORT和NOTIFIER这两个全局对象用于消息操作。同时启动n个wsgi server,每个server对应配置文件中的一个api。另外根据系统的CPU核心数n,每个wsgi server都会有n个worker协程去处理请求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息