OpenStack-----Nova源码分析之创建虚拟机

Nova源码解析学习:

一、浏览代码的工具:

  1. Vim+各种插件
  2. Eclipse+PyDev插件
  3. Spyder

二、源码地图:

1、每个子项目的源码根目录下都有一个setup.py和setuo.cfg文件

2、Setup.py中的setup函数有大量的参数需要设置(包括项目的名称、作者、版本等),setup.cfg文件的出现将setup函数解脱出来,它使用pdf工具去读取和过滤setup.cfg中的数据,将解析后的结果作为自己的参数。

3、Setup.cfg文件的内容由很多section组成,能帮助我们更好的去理解代码的section唯有entry_points(对于一个Python包来说,entry points可以简单地理解为通过setuptools注册的外部可以直接调用的接口)。

4、Entry points都是在运行时动态导入的,有点类似于可扩展的插件。

5、Entry points组中有一个特殊的存在-console_scripts,里面的每一个entry points都表示一个可执行脚本会被生成并且安装。

console_scripts =

    nova-api = nova.cmd.api:main  

    nova-api-metadata = nova.cmd.api_metadata:main  

    nova-api-os-compute = nova.cmd.api_os_compute:main

    nova-compute = nova.cmd.compute:main

    nova-conductor = nova.cmd.conductor:main

    nova-manage = nova.cmd.manage:main

    nova-novncproxy = nova.cmd.novncproxy:main

    nova-policy = nova.cmd.policy:main

    nova-rootwrap = oslo_rootwrap.cmd:main

    nova-rootwrap-daemon = oslo_rootwrap.cmd:daemon

    nova-scheduler = nova.cmd.scheduler:main

    nova-serialproxy = nova.cmd.serialproxy:main

    nova-spicehtml5proxy = nova.cmd.spicehtml5proxy:main

    nova-status = nova.cmd.status:main

由此可知nova项目安装后会包含21个可执行程序

三、结构分析:

1、console_scripts中主要服务:

nova-api:Nova对外提供的RESTful API服务,目前提供两种API服务,即nova-api-metadata和nova-apipos-compute

Nova-api根据配置文件/etc/nova/nova.conf的enable_apis选项设置启动这两种服务。

nova-api-metadata :接受虚拟机实例metadata(元数据)相关请求,目前这部分工作由Neutron项目完成,而nova-api-metadata API服务只在多计算节点部署,并且使用nova-network情况下使用。

nova-api-os-compute :Openstack API服务

nova-compute:Computer服务

nova-conductor:Conductor服务

nova-manage :提供很多与Nova维护和管理相关的功能,比如用户创建,VPN管理等。

nova-novncproxy:Nova提供了 novncproxy代理支持用户通过vnc访问虚拟机。提供完整的vnc访问功能,涉及几个Nova服务(nova-consoleauth提供认证授权, nova-novncproxy用于支持基于浏览器的vnc客户端,nova-xvpvncproxy用于支持基于Java的vnc客户端。)

nova-rootwrap :用于在Openstack在运行过程中以root身份运行某些shell命令

nova-scheduler :Scheduler服务。

Nova-api会读取api-paste.ini,从中加载整个WSGI stack。最终API的入口点都位于nova.api.openstack.compute路径中。

  1. 通常一个服务的目录都会包含api.py、rpcapi.py、manager.py,这三个是最最重要的模块。

api.py: 通常是供其它组件调用的封装库

rpcapi.py:这个是RPC请求的封装,或者说是RPC封装的client端,该模块封装了所有RPC请求调用。

manager.py: 这个才是真正服务的功能实现,也是RPC的服务端,即处理RPC请求的入口,实现的方法和rpcapi实现的方法一一对应。

Nova中各个服务之间的通信使用了基于AMQP实现的RPC机制,其中nova-compute、 nova-conductor 和 nova-scheduler 在启动时都会注册一个 RPC Server,而 nova-api 因为Nova内部并没有服务会调用它提供的接口,所以无需注册。

比如对一个虚拟机执行关机操作的流程为:

API节点:

Nova-api接收用户请求-》nova-api调用compute/api.py-》compute/api调用rpcapi.py-》rpcapi.py向目标计算机发起stop_instance()RPC请求

计算节点:

收到MQ RPC消息-》解析stop_instance()请求-》调用compute/manager.py的callback方法stop_instance()-》调用libvirt关机虚拟机

  1. Openstack项目的目录结构是按照功能划分的,而不是服务组件,因此并不是所有的目录都能由对应的组件。

Nova主要目录结构解析:

cmd:这是服务的启动脚本,即所有服务的main函数。看服务怎么初始化,就从这里开始。

db: 封装数据库访问API,目前支持的driver为sqlalchemy,还包括migrate repository。

conf:Nova的配置项声明都在这里,想看Nova配置的作用和默认值可以从这个目录入手。

locale: 本地化处理。

image: 封装image API,其实就是调用python-glanceclient。

network: 封装网络服务接口,根据配置不同,可能调用nova-network或者neutron。

volume: 封装数据卷访问接口,通常是Cinder的client封装,调用python-cinderclient。

virt: 这是所有支持的hypervisor驱动,主流的如libvirt、xen等。

objects: 对象模型,封装了所有实体对象的CURD操作,相对直接调用db的model更安全,并且支持版本控制。

policies: policy校验实现。

tests: 单元测试和功能测试代码。

注:

1、 pbr库是一个使用统一方式管理setuptools包的库。pbr库通过一个setup钩子函数读取并过滤setup.cfg中的数据,以填充默认值并提供更多合理的操作;然后将结果作为参数返回给setup.py。

2、OpenStack社区将所有组件中的具有共性的组件剥离出来,并统一放在oslo组件下。oslo中的组件不仅可以在OpenStack项目中使用,也可以单独作为第三方工具包供其他项目使用。

3、oslo.config项目是oslo组件中用于OpenStack配置文件的一个项目

4、oslo项目创建了oslo.policy子项目为所有OpenStack服务提供RBAC策略实施支持。

  • 创建虚拟机过程简要分析

需要注意的是Nova支持同时创建多台虚拟机,因此在调度时需要选择多个宿主机。

Nova-api

1、入口为nova/api/openstack/compute/servers.py的create方法,该方法检查了一堆参数以及policy后,调用compute_api的create方法。

def create(self, req, body):
    """Creates a new server for a given user."""
    context = req.environ['nova.context']
    server_dict = body['server']
    password = self._get_server_admin_password(server_dict)
    name = common.normalize_name(server_dict['name'])
    description = name

............

flavor_id = self._flavor_id_from_req_data(body)
try:
    flavor = flavors.get_flavor_by_flavor_id(
    flavor_id, ctxt=context, read_deleted="no")
    supports_multiattach = common.supports_multiattach_volume(req)
    supports_port_resource_request = \
    common.supports_port_resource_request(req)
    instances, resv_id = self.compute_api.create(
        context,
        flavor,
        image_uuid,
        display_name=name,
        display_description=description,
        hostname=hostname,
        availability_zone=availability_zone,
        forced_host=host, forced_node=node,
        metadata=server_dict.get('metadata', {}),
        admin_password=password,
        check_server_group_quota=True,
        supports_multiattach=supports_multiattach,
        supports_port_resource_request=supports_port_resource_request,
        **create_kwargs)

......................
  1. 这里的compute_api即nova/compute/api.py模块,找到该模块的create方法,该方法会创建数据库记录、检查参数等,然后调用compute_task_api中_create_instance的build_instances方法:
self.compute_task_api.schedule_and_build_instances(
    context,
    build_requests=build_requests,
    request_spec=request_specs,
    image=boot_meta,
    admin_password=admin_password,
    injected_files=injected_files,
    requested_networks=requested_networks,
    block_device_mapping=block_device_mapping,
    tags=tags)
  1. compute_task_api即conductor的api.pyself.compute_task_api=conductor.ComputeTaskAPI。conductor的api并没有执行什么操作,直接调用了conductor_compute_rpcapi的build_instances方法nova/conductor/api:
def schedule_and_build_instances(self, context, build_requests,
                                 request_spec, image,
                                 admin_password, injected_files,
                                 requested_networks, block_device_mapping,
                                 tags=None):
    self.conductor_compute_rpcapi.schedule_and_build_instances(
        context, build_requests, request_spec, image,
        admin_password, injected_files, requested_networks,
        block_device_mapping, tags)
  1. 该方法就是conductor RPC API,即nova/conductor/rpcapi.py模块,该方法除了一堆的版本检查,剩下的就是对RPC调用的封装,代码只有两行在build_instance方法中:
cctxt = self.client.prepare(version=version)
cctxt.cast(context, 'build_instances', **kwargs)

其中cast表示异步调用,build_instances是远程调用的方法,kwargs是传递的参数。参数是字典类型,没有复杂对象结构,因此不需要特别的序列化操作。

Ps:截至到现在,虽然目录由api->compute->conductor,但仍在nova-api进程中运行,直到cast方法执行,该方法由于是异步调用,因此nova-api任务完成,此时会响应用户请求,虚拟机状态为building。

nova-conductor

前景摘要:由于是向nova-conductor发起的RPC调用,而前面说了接收端肯定是manager.py因此进程跳到nova-conductor服务

  1. 入口为nova/conductor/manager.py的build_instances方法,该方法首先调用了_schedule_instances方法,该方法调用了scheduler_client的select_destinations方法:
def _schedule_instances(self, context, request_spec,
                        instance_uuids=None, return_alternates=False):
    scheduler_utils.setup_instance_group(context, request_spec)
    with timeutils.StopWatch() as timer:
        host_lists = self.query_client.select_destinations(
            context, request_spec, instance_uuids, return_objects=True,
            return_alternates=return_alternates)
    LOG.debug('Took %0.2f seconds to select destinations for %s '
              'instance(s).', timer.elapsed(), len(instance_uuids))
    return host_lists

scheduler_client和compute_api以及compute_task_api都是一样对服务的client SDK调用,不过scheduler没有api.py,而是有个单独的client目录,实现在client目录的__init__.py,

2、这里仅仅是调用query.py下的SchedulerQueryClient的select_destinations实现,然后又很直接地调用了scheduler_rpcapi的select_destinations方法,终于又到了RPC调用环节(nova/scheduler/client/query)

def select_destinations(self, context, spec_obj, instance_uuids,
        return_objects=False, return_alternates=False):
    return self.scheduler_rpcapi.select_destinations(context, spec_obj,
            instance_uuids, return_objects, return_alternates)

3、毫无疑问,RPC封装同样是在scheduler的rpcapi中实现在select_destinations方法中。该方法RPC调用代码如下:

return cctxt.call(ctxt, 'select_destinations', **msg_args)

注意这里调用的call方法,即同步RPC调用,此时nova-conductor并不会退出,而是堵塞等待直到nova-scheduler返回。因此当前状态为nova-conductor为blocked状态,等待nova-scheduler返回,nova-scheduler接管任务。

 nova-scheduler

同理找到scheduler的manager.py模块的select_destinations方法,该方法会调用driver方法,这里的driver其实就是调度算法实现,通常用的比较多的就是Filter Scheduler算法,对应filter_scheduler.py模块,该模块首先通过host_manager拿到所有的计算节点信息,然后通过filters过滤掉不满足条件的计算节点,剩下的节点通过weigh方法计算权值,最后选择权值高的作为候选计算节点返回。最后nova-scheduler返回调度结果的hosts集合,任务结束,返回到nova-conductor服务。

nova-condutor

回到scheduler/manager.py的build_instances方法,nova-conductor等待nova-scheduler返回后,拿到调度的计算节点列表。因为可能同时启动多个虚拟机,因此循环调用了compute_rpcapi的build_and_run_instance方法。

看到xxxrpc立即想到对应的代码位置,位于compute/rpcapi模块,该方法向nova-compute发起RPC请求位于build_and_run_instance方法中:

cctxt.cast(ctxt, 'build_and_run_instance', **kwargs)

nova-compute

  1. 到了nova-compute服务,入口为compute/manager.py,找到build_and_run_instance方法,该方法调用了driver的spawn方法
def _build_and_run_instance(self, context, instance, image, injected_files,
        admin_password, requested_networks, security_groups,
        block_device_mapping, node, limits, filter_properties,
        request_spec=None, accel_uuids=None):

......................

self.driver.spawn(context, instance, image_meta,
                  injected_files, admin_password,
                  allocs, network_info=network_info,
                  block_device_info=block_device_info,
                  accel_info=accel_info)

......................

  1. 这里的driver就是各种hypervisor的实现,所有实现的driver都在virt目录下,入口为driver.py,比如libvirt driver实现对应为virt/libvirt/driver.py,找到spawn方法,该方法拉取镜像创建根磁盘、生成xml文件、define domain,启动domain等。最后虚拟机完成创建。nova-compute服务结束。
def spawn(self, context, instance, image_meta, injected_files,
          admin_password, allocations, network_info=None,
          block_device_info=None, power_on=True, accel_info=None):

.....................

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值