代码改变世界

Openstack Nova 源码分析 — RPC 远程调用过程

2016-08-04 10:03  云物互联  阅读(602)  评论(0编辑  收藏  举报

目录

Nova Project Services

这里写图片描述

  • nova-api:捕获novaclient发送过来的HTTP请求,并且将它转换为AMQP消息,通过Queue来与别的services通信。

  • nova-conductor:为数据库访问提供了一层安全保障。
    NOTE:除了nova-conductor可以访问数据库之外,因为nova-scheduler是只读数据库,而nova-api对数据库的操作有Policy保护,所以它们也都是可以访问数据库的。但是最好还是仅通过nova-conductor来访问数据库。

  • nova-scheduler:从可用资源池中选择最合适的Compute Node来创建虚拟机实例。

  • novaclient:是为了简化用户使用而将Restful API封装起来,负责将用户的请求转换为标准的HTTP请求。

  • nova-compute:负责虚拟机的生命周期管理

Project 的程序入口( setup.py )

每个 Openstack Project 的 setup.cfg(nova/setup.cfg) 文件,也被称之为 Openstack Project 的源码地图。其中 [entry_points] 是该文件最重要的 Section ,它包含了几个相对特殊的组或称之为命名空间,其中下列两个是非常重要的:

  • console_scripts = :其中的每一项都表示一个可执行的脚本,这些脚本在 Openstack Project 部署时候就会被安装到 lib 目录中,是 Openstack Projec 所提供的各个服务的入口点,在系统希望运行这些进程时,需要找到的位置。 EG. nova-api = nova.cmd.api:main (是 nove-api services或守护进程的程序入口)
    console_scripts命名空间我们可以看见Nova所提供的许多服务,而这些选项的值就是对应服务的程序入口。
console_scripts =
    nova-all = nova.cmd.all:main                          #启动所有Nova服务的辅助脚本
    nova-api = nova.cmd.api:main                          #作为WSGI服务器对外提供Restful API服务,有三种类型nova-api-ec2/,通过nova.conf中的enable_apis选项值来决定。
    nova-api-ec2 = nova.cmd.api_ec2:main                  #EC2 API支持
    nova-api-metadata = nova.cmd.api_metadata:main
    nova-api-os-compute = nova.cmd.api_os_compute:main    #Openstack API服务
    nova-cells = nova.cmd.cells:main
    nova-cert = nova.cmd.cert:main                        #管理X509证书
    nova-compute = nova.cmd.compute:main                  #compute服务,负责虚拟机的生命周期管理
    nova-conductor = nova.cmd.conductor:main              #conductor服务,负责与数据库的交互
    nova-console = nova.cmd.console:main                  
    nova-consoleauth = nova.cmd.consoleauth:main
    nova-dhcpbridge = nova.cmd.dhcpbridge:main            #管理nova-network的DHCP bridge
    nova-idmapshift = nova.cmd.idmapshift:main
    nova-manage = nova.cmd.manage:main
    nova-network = nova.cmd.network:main                  #已经被Neutron取代,只有在使用Devstack部署的时候才会被使用
    nova-novncproxy = nova.cmd.novncproxy:main            #支持用户通过该代理服务以vnc连接到虚拟机实例
    nova-objectstore = nova.cmd.objectstore:main          #兼容EC2的存储接口
    nova-rootwrap = oslo_rootwrap.cmd:main                #在Openstack运行的过程中,支持以root的身份来运行某些shell指令
    nova-rootwrap-daemon = oslo_rootwrap.cmd:daemon
    nova-scheduler = nova.cmd.scheduler:main              #scheduler服务,服务计算节点物理资源的调度
    nova-serialproxy = nova.cmd.serialproxy:main
    nova-spicehtml5proxy = nova.cmd.spicehtml5proxy:main
    nova-xvpvncproxy = nova.cmd.xvpvncproxy:main          #允许用户通过代理的方式来访问虚拟机实例的控制台。
  • nova.api.v21.extensions:其中的每一项都对应了一个 API ,starting nova-api service 时会根据nova.api.v21.extensions命名空间来进行加载。Extensions 是不同版本之间的过渡,以后更趋向以这种方式来实现路由的Setup ,将资源映射到Controller。如果希望搞清楚一个API的实现过程,那么这个命名空间的选项值代码路径将会作为突破口,能更有效的理清API的脉络。

Nova中RPC(远程过程调用)

RPC:同一个项目内的不同服务进程之间的交互方式。

nova-compute/nova-conductor/nova-scheduler 在启动时都会注册一个RPC Server,这几个服务之间会通过消息队列 Queue 和 RPC 来实现互相调用,因为 nova-api 负责传递请求,并没有会调用它的服务,所以无需要启动RPC Server。

nova-compute RPC API的实现

nova-compute 主要用于管理虚拟机的生命周期,所以其RPC API实现的方法多是用于对虚拟机的操作。

# nova/nova/compute/rpcapi.py
class ComputeAPI(object):

    # 这是一个RPC远程调用的方法
    def live_migration(self, ctxt, instance, dest, block_migration, host,
                       migration, migrate_data=None):
        args = {'migration': migration}
        version = '4.2'
        if not self.client.can_send_version(version):
            version = '4.0'

        # 获取目标 compute 主机(DEST HOST)的RPC client,即被调用的服务进程的HostIP
        cctxt = self.client.prepare(server=host, version=version)

        # 通过目标主机对象的 RPC cliient 来调用远程过程方法 cast() ,以此来实现远程调用
        cctxt.cast(ctxt, 'live_migration', instance=instance,
                   dest=dest, block_migration=block_migration,
                   migrate_data=migrate_data, **args)
        # cast()异步远程调用,不会阻塞别的进程,适合于需要长时间进行的执行过程
        # cast()的第二个参数是RPC client调用的函数名,case()后面的参数会继续作为参数传入该调用函数
        # cast()函数内的live_migration()函数是 manager.live_migration() 视具体实现迁移功能的函数,在manager.py内实现。

注意:在 Nova Project 中大多数的服务都提供了 API 或 RPC API 的实现文件,这些 API 是服务进程自身为了能被其他的服务进程访问所提供出来的一种接口,当别的服务进程希望影响另一个服务进程时,就可以通过 import 另一个服务进程的 rpcapi 就可以实现了。

EXAMPLE:当nova-conductor通知nova-compute创建虚拟机实例时,过程如下:
1. 在 nova-conductor 的代码中使用了 import nova-compute rpcapi module 或者类实例化传参这两种实现方式来加载 compute rpcapi 对象。这样 nova-conductor 就拥有了通过 RPC 访问 nova-compue 的能力。
2. 在 nova-conductor 的代码实现中调用了 rpcapi 模块的方法,即 nova-conductor发送了一个请求到 Queue,并等待 nova-compute 接受和响应。
3. nova-compute 接收到 nova-conductor 的请求,并作出响应。

这里写图片描述

#nova/nova/conductor/tasks/live_migrate.py

class LiveMigrationTask(base.TaskBase):
    def __init__(self, context, instance, destination,
                 block_migration, disk_over_commit, migration, compute_rpcapi,
                 servicegroup_api, scheduler_client):
        super(LiveMigrationTask, self).__init__(context, instance)
    ... 

    def _execute(self):
        self._check_instance_is_active()
        self._check_host_is_up(self.source)

        if not self.destination:
            self.destination = self._find_destination()
            self.migration.dest_compute = self.destination
            self.migration.save()
        else:
            self._check_requested_destination()

        # TODO(johngarbutt) need to move complexity out of compute manager
        # TODO(johngarbutt) disk_over_commit?

        #调用 ComputeAPI 类中的 live_migration() RPC接口,以RPC的方式发出一个请求到Queue再被nova-compute接收
        return self.compute_rpcapi.live_migration(self.context,
                host=self.source,
                instance=self.instance,
                dest=self.destination,
                block_migration=self.block_migration,
                migration=self.migration,
                migrate_data=self.migrate_data)

注意:**nova-compute RPC Server 接收到别的服务的RPC请求之后(调用了ComputeAPI提供的RPC接口),真正完成请求操作的是**nova.compute.manager 模块。

nova.compute.manager 模块

nova.compute.manager 会一直在监听 Queue ,当Queue中存在相关的 RPC 请求时,实际上是由 manager 来实现的。

# nova/nova/compute/manager.py

5189     def live_migration(self, context, dest, instance, block_migration, 
   1                        migration, migrate_data):
   2         """Executing live migration.
   3          
   4         :param context: security context
   5         :param dest: destination host
   6         :param instance: a nova.objects.instance.Instance object
   7         :param block_migration: if true, prepare for block migration
   8         :param migration: an nova.objects.Migration object
   9         :param migrate_data: implementation specific params
  10          
  11         """
  12          
  13         # NOTE(danms): Remove these guards in v5.0 of the RPC API
  14         if migration:
  15             migration.status = 'queued'
  16             migration.save()
  17          
  18         def dispatch_live_migration(*args, **kwargs):
  19             with self._live_migration_semaphore:
  20                 self._do_live_migration(*args, **kwargs)
  21          
  22         # NOTE(danms): We spawn here to return the RPC worker thread back to
  23         # the pool. Since what follows could take a really long time, we don't
  24         # want to tie up RPC workers.
  25         utils.spawn_n(dispatch_live_migration,
  26                       context, dest, instance,
  27                       block_migration, migration,
  28                       migrate_data)

最后

说到底,从 ComputeAPI 到 ComputeManager 的过程即是 RPC 调用过程。