当前位置: 首页 > 科技观察

Openstack源码阅读的正确姿势

时间:2023-03-13 04:50:12 科技观察

1。谈谈Openstack的发展历程OpenStack是一个面向IaaS层的云管理平台开源项目,用于实现公有云和私有云的部署和管理。一开始,Openstack只有两个组件,即提供计算服务的Nova项目和提供对象存储服务的Swift。Nova不仅提供虚拟机服务,还包括网络服务、块存储服务、镜像服务、裸机管理服务。后来随着项目的不断发展,Nova被拆分成多个独立的项目提供不同的服务,比如Cinder项目提供块存储服务,Glance项目提供镜像服务,nova-network提供块存储服务.它是neutron的前身,裸机管理也作为Ironic项目从Nova中分离出来。起初,容器服务也是由Nova支持,作为Nova的驱动程序之一实现,然后迁移到Heat,现在已经分离成一个独立的项目Magnum。后来Magnum主要提供容器编排服务,纯容器服务由Zun项目提供。负责任的。一开始Openstack是没有认证功能的,从E版开始加入了认证服务Keystone,这样Openstack的六大核心服务终于聚集在一起了。Keystone身份验证服务。Glance镜像服务。新星计算服务。煤渣块存储服务。Neutorn网络服务。Swift对象存储服务。E版本之后,在这些核心服务之上,新的服务不断涌现,比如面板服务Horizo??n、服务编排服务Heat、数据库服务Trove、文件共享服务Manila、大数据服务Sahara,以及前面提到的Magnum等。几乎都依赖于上述核心服务。比如Sahara大数据服务首先会调用Heat模板服务,Heat会调用Nova创建虚拟机,调用Glance获取镜像,调用Cinder创建数据卷,调用Neutron创建网络。也有一些围绕Openstack部署的项目,如Puppet-openstack、Kolla、TripleO、Fuel等项目。截止到现在(2016年11月27日),Openstack已经走过了6年半的时间。最新发布的版本是第14版,代号Newton,Ocata版本已经在快速开发中。Openstack服务变得越来越复杂,并且不断变化和发展。以Nova为例,从一开始就使用nova-conductor来代理数据库访问以增强安全性,并引入了objects对象模型以支持对象版本控制。现在开发Cell项目支持大规模集群部署,Nova-EC2项目分离。截至目前,Nova包含nova-api、nova-conductor、nova-scheduler、nova-compute、nova-cell、nova-console等十余个组件。如此庞大的分布式系统,需要深入了解其工作原理,理清它们之间的交互并不容易,尤其是对于新手来说。2、工欲善其事,必先利其器。因为Openstack是使用python语言开发的,而python是动态类型语言,所以从代码中不容易看出参数类型。因此,您首先需要部署一个allinoneOpenstack开发测试环境。推荐使用RDOdeployment:Packstackquickstart,当然愿意折腾用devstack也没问题。其次,你需要安装一个科学读码工具。图形界面使用pycharm是没有问题的,但是通常虚拟机中是没有图形界面的。***vim需要简单的配置就可以支持代码跳转和代码搜索。可以参考我的vim配置GitHub-int32bit/dotfiles:一套vim、zsh、git、tmux的配置文件。掌握python的调试技巧,推荐pdb、ipdb、ptpdb,其中ptpdb最好,但需要手动安装。在断点之前,需要注意代码执行时属于哪个服务组件。nova-api的代码,在nova-compute中断点运行肯定没用。另外需要注意的是,设置断点后的服务必须运行在前端,不能运行在后台。比如我们在nova/compute/manager.py中设置断点,我们需要杀掉后台进程:systemctlstopopenstack-nova-compute然后直接在终端运行nova-compute即可。su-c'nova-compute'nova3。教你正确的阅读姿势。学习Openstack的第一步是:看文档,部署allineone,用起来折腾,怒部署多节点,深入使用,深入吐槽,看源码,混社区,参与社区开发中阅读源码的首要问题是对代码的结构有清晰的认识。需要强调的是,Openstack项目的目录结构不是按组件划分的,而是按功能划分的。以nova为例,compute目录不一定是nova-compute节点上运行的代码,主要是实现compute(虚拟机运行)相关的功能。同样,scheduler目录代码也不是全部运行在scheduler服务节点上,但主要是和调度相关的。代码。好在目录结构并不是完全乱七八糟,还是有规律的。通常一个服务目录会包含api.py、rpcapi.py、manager.py,这三个是最重要的模块。api.py:一般是供其他组件调用的库。换句话说,这个模块通常不会被这个模块调用。比如compute目录下的api.py,通常会被nova-api服务的controller调用。rpcapi.py:这是对RPC请求的封装,或者说是RPC实现的客户端。该模块封装了RPC请求调用。manager.py:这是真正服务的功能实现,也是RPC的server端,即处理RPC请求的入口。实现方法通常对应rpcapi实现的方法。前面说过,Openstack项目的目录结构是按功能划分的,不是按服务组件划分的,所以并不是所有的目录都能有对应的组件。仍以nova为例:cmd:这是服务的启动脚本,也就是所有服务的主要功能。要查看服务是如何初始化的,请从这里开始。db:封装数据库访问,目前支持的驱动是sqlalchemy。conf:Nova的配置项声明在这里。locale:本地化处理。image:封装Glance调用接口。network:封装网络服务接口,根据配置的不同,可能会调用nova-network或者neutron。volume:封装了数据卷的访问接口,通常是Cinder的客户端封装。virt:这个是所有支持的hypervisordriver,主流的如libvirt,xen等。objects:对象模型,封装了所有实体对象的CURD操作,比之前直接调用db的模型更安全,支持版本控制。政策:政策验证实施。测试:单元测试和功能测试代码。按照流程看源码不是一个好习惯,因为服务如何初始化、如何通信、如何发送心跳等不太容易理解,而且各种高级包太复杂了。而我认为阅读源码更好的方法是跟踪一个任务的执行过程,比如跟踪启动虚拟机的整个过程。不管做什么操作,都必须先从API入手。RESTFulAPI是Openstack服务的唯一入口。也就是说,你可以从API开始阅读源码。api组件也是按照entity来划分的。不同的entity对应不同的controller,比如servers、flavors、keypairs等。controller通常有以下方法:index:获取资源列表,一般RESTFulAPI对应的URL为“GET/resources”,如“GET/servers”作为获取虚拟机列表的API。get:获取一个资源,比如返回一个虚拟机的详细信息。API是“GET/servers/uuid”。create:创建一个新的资源,通常对应一个POST请求。比如创建一个虚拟机为“POST/servers”,当然POST的数据就是虚拟机信息。delete:删除指定资源,通常对应一个DELETE请求,如“DELETE/servers/uuid”删除一个虚拟机。update:更新资源信息,通常对应一个PUT请求。比如更新一个虚拟机资源是“PUT/servers/uuid,body是虚拟机数据。理解代码结构,找到入口,使用智能跳转,阅读源码势必会得到两倍的结果事半功倍有不懂的可以随时加断点,一步步调试四、案例分析下面以创建虚拟机为例,分析整个过程工作流程和操作顺序按照组件的划分一步步进行。请再复习一遍api.py、rpcapi.py、manager.py和api下的controller结构,否则看完会越来越糊涂。S1nova-api入口是nova/api/openstack/compute/servers.py的create方法。检查了一堆参数和策略后,这个方法调用了compute_api的create方法。这里的compute_api就是上面说的nova/compute/api。.py模块的API。compute_api会创建数据库记录,检查参数等,然后调用compute_task_api的build_instances方法,compute_task_api是conductor的api.py。Conductor的api不做任何操作,直接调用conductor_compute_rpcapi的build_instances方法。该方法立即从conductorRPC调用api,即nova/conductor/rpcapi.py模块。这个方法除了一堆版本检查,剩下的就是RPCCall的封装,代码只有两行:cctxt=self.client.prepare(version=version)cctxt.cast(context,'build_instances',**kw)其中cast表示异步调用,build_instances是远程调用的方法,kw是传递的参数。参数是字典类型,没有复杂的对象结构,所以不需要特殊的序列化操作。截至目前,虽然该目录由api->compute->conductor组成,但在执行cast方法之前,它仍然运行在nova-api进程中。由于该方法是异步调用,nova-api不会等待远程方法调用的结果,直接返回结束。S2nova-conductor是对nova-conductor的RPC调用,前面说到接收端必须是manager.py,所以流程跳转到nova-conductor服务,入口是nova/conductor/的build_instances方法经理.py。该方法首先调用_schedule_instances方法,后者调用scheduler_client的select_destinations方法。scheduler_client、compute_api、compute_task_api是同一个客户端调用service(即api.py),但是scheduler没有api.py,而是有一个单独的client目录,client目录在__init__.py中实现客户端目录的模块。这里只是调用query.py下SchedulerQueryClient的select_destinations实现,然后直接调用scheduler_rpcapi的select_destinations方法,最后到达RPC调用环节。毫无疑问,调度器的rpcapi中也实现了RPC封装。该方法的RPC调用代码如下:returnccctxt.call(ctxt,'select_destinations',**msg_args)注意这里调用的call方法是同步调用。此时nova-conductor不会退出,而是会阻塞等待nova-scheduler返回。S3nova-scheduler同样找到scheduler的manager.py模块的select_destinations方法。该方法将调用驱动程序的相应方法。这里的driver其实就是调度算法的实现,由配置文件决定。filter_scheduler通常用的比较多,对应filter_scheduler.py模块,该模块首先通过host_manager获取所有的计算节点信息,然后通过filters过滤掉不符合条件的计算节点,计算剩余节点的权重通过权值法,最终选出权值最高的作为返回的候选计算节点。nova-scheduler进程结束。S4nova-conductor返回scheduler/manager.py的build_instances方法,nova-conductor等待nova-scheduler返回,获取调度的计算节点列表,然后调用compute_rpcapi的build_and_run_instance方法。看到xxxrpc,马上就会想到对应的代码位置,位于compute/rpcapi模块中。这个方法向nova-compute发起RPC请求:cctxt.cast(ctxt,'build_and_run_instance',...)可以看出发起了一个异步RPC,所以nova-conductor就结束了,接下来就是最后的轮到nova-compute出现。S5nova-compute到达nova-compute服务,入口为compute/manager.py,找到build_and_run_instance方法,该方法调用driver的spawn方法,其中driver是各种hypervisor的实现,所有实现的驱动都是在virt目录下,入口为driver.py,例如libvirt驱动的实现对应virt/libvirt/driver.py,找到spawn方法,该方法拉取镜像创建根盘,生成xml文件,定义域,启动域等。***虚拟机创建完成。nova-compute服务结束。一张图总结了以上就是创建虚拟机的各个服务的交互过程和调用关系。需要注意的是,所有的数据库操作,比如instance.save()和update操作,如果你配置use_local为false,会发送nova-conductor发起RPC调用,由nova-conductor代理完成数据库更新,而不是通过nova-compute直接访问数据库。上面的分析省略了这里的RPC调用过程。整个过程用一张图来表示:【本文为专栏作家“傅广平”原创文章,如需转载请get联系】点击查看更多本作者好文

猜你喜欢