OpenStack组件Nova(计算组件)学习笔记

一、nova组件 

nova是OpenStack中的计算组织控制器,Nova主要负责Computer模块,就是调度管理虚拟机的创建、启动、删除等。

  • nova-api:接收和响应来自最终用户的计算API请求。检查客户端传入的参数是否合法有效、调用 nova 其他子服务的处理客户端 HTTP 请求、格式化 nova 其他子服务返回的结果并返回给客户端。
  • nova-computer:nova组件中最核心的服务,通过调用 Hypervisor API 实现虚机生命周期管理。
  • nova-scheduler:请求的调度器,决定在哪台计算服务主机运行实例。
  • nova-conductor:提供数据库查询功能,隔离了nova直接访问数据库。
  • nova-network: 网络控制器处理主机的网络配置,例如IP地址分配,配置项目VLAN,设定安全群组以及为计算节点配置网络。
  • Rabbit MQ:OpenStack 默认的消息队列,nova对请求应答进行异步调用,接收请求后立即响应,不会让用户处于等待状态。

二、OpenStack创建虚拟机的流程

1.客户端调用nova-api发起创建虚拟机的请求,携带用户登录后返回的token信息。

2.nova-api接受请求后向keystone发送认证请求,查看token是否为有效用户和token。

3.验证成功后才可以发送创建虚拟机的请求。

4.然后nova-api和数据库连接。

5.nova-api把创建虚拟机的请求放到 消息队列 里边。

6.消息队列 通知nova-scheduler选择可供创建虚拟机的host(主机)

7.nova-scheduler 提供可创建虚拟机的host,把消息放置到 消息队列 里边。

8. 消息队列 把创建虚拟机的消息给nova-compute。

9.nova-compute从 消息队列 中获取创建虚拟机请求的信息。

10.nova-compute把查询虚拟机信息的请求放到 消息队列 里边去。

11.nova-conductor从 消息队列 里边拿到nova-compute的请求信息。

12. nova-conductor根据消息查询虚拟机对应的信息。

13. nova-conductor从数据库中获取虚拟机对应的信息。

14. nova-conductor把虚拟机信息 放到 消息队列 里边。

15. nova-compute 从 消息队列 里边获取虚拟机信息的信息。

16. nova-compute请求glance-api获取创建虚拟机所需要的镜像。glance-api 会验证token信息。

17.nova-compute请求neutron-server获取创建虚拟机所需要的网络信息。neutron-server会验证token信息。

18.nova-compute请求cinder-api获取创建虚拟机所需要的持久化存储信息。cinder-api会验证token信息。

19. nova-compute 根据instance信息,对虚拟机进行创建。

Instance信息是nova-scheduler调度nova-compute的run_instance方法,对虚拟机的信息配置。

虚拟机的创建源码解读

# nova/api/openstack/computer/servers.py
class ServersController(wsgi.Controller):
    /****...../
    # 创建的方法
    def create(self, req, body):
    """Creates a new server for a given user."""
        # 首先是参数的提取,然后进行检查
        context = req.environ['nova.context']
        # server_dict保存要创建的虚拟机的信息,包括镜像,网络等
        server_dict = body['server']
        password = self._get_server_admin_password(server_dict)
        name = common.normalize_name(server_dict['name'])
        description = name
        if api_version_request.is_supported(req, min_version='2.19'):
            description = server_dict.get('description')
        ..../
   
    try:
        # 通过flavor_id获取到flavor信息
        inst_type = 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)
         #调用compute_api 传入参数 创建虚机      
        (instances, resv_id) = self.compute_api.create(context,
            inst_type,
            image_uuid,
            display_name=name,
            display_description=description,
            availability_zone=availability_zone,
            forced_host=host, forced_node=node,
            metadata=server_dict.get('metadata', {}),
            admin_password=password,
            requested_networks=requested_networks,
            check_server_group_quota=True,
            supports_multiattach=supports_multiattach,
            supports_port_resource_request=supports_port_resource_request,
            **create_kwargs)
     except (exception.QuotaError,exception.PortLimitExceeded) as error:
        raise exc.HTTPForbidden(explanation=error.format_message())
    # 对异常的处理
    .../

compute_api.create(...) 创建虚拟机

# nova/computer/api.py
@profiler.trace_cls("compute_api")
class API(base.Base):
    """API for interacting with the compute manager."""
    # 把创建虚拟机所用的各个api 进行实例化
    def __init__(self, image_api=None, network_api=None, volume_api=None,
                 security_group_api=None, **kwargs):
        self.image_api = image_api or image.API()
        self.network_api = network_api or network.API()
        self.volume_api = volume_api or cinder.API()
        self._placementclient = None  # Lazy-load on first access.
        self.security_group_api = (security_group_api or
                                   openstack_driver.get_openstack_security_group_driver())
        self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
        self.compute_task_api = conductor.ComputeTaskAPI()
        self.servicegroup_api = servicegroup.API()
        self.notifier = rpc.get_notifier('compute', CONF.host)
        if CONF.ephemeral_storage_encryption.enabled:
            self.key_manager = key_manager.API()
        # Help us to record host in EventReporter
        self.host = CONF.host
        super(API, self).__init__(**kwargs)
        
   # 创建虚拟机前对参数的验证     
   @hooks.add_hook("create_instance")
   def create(self, context, instance_type,
           image_href, kernel_id=None, ramdisk_id=None,
           min_count=None, max_count=None,
           display_name=None, display_description=None,
           key_name=None, key_data=None, security_groups=None,
           availability_zone=None, forced_host=None, forced_node=None,
           user_data=None, metadata=None, injected_files=None,
           admin_password=None, block_device_mapping=None,
           access_ip_v4=None, access_ip_v6=None, requested_networks=None,
           config_drive=None, auto_disk_config=None, scheduler_hints=None,
           legacy_bdm=True, shutdown_terminate=False,
           check_server_group_quota=False, tags=None,
           supports_multiattach=False, trusted_certs=None,
           supports_port_resource_request=False):     
       # 参数的验证
       /****...../
       # 调用方法_create_instance(...)传入创建虚拟机的参数
       return self._create_instance(
            context, instance_type,
            image_href, kernel_id, ramdisk_id,
            min_count, max_count,
            display_name, display_description,
            key_name, key_data, security_groups,
            availability_zone, user_data, metadata,
            injected_files, admin_password,
            access_ip_v4, access_ip_v6,
            requested_networks, config_drive,
            block_device_mapping, auto_disk_config,
            filter_properties=filter_properties,
            legacy_bdm=legacy_bdm,
            shutdown_terminate=shutdown_terminate,
            check_server_group_quota=check_server_group_quota,
            tags=tags, supports_multiattach=supports_multiattach,
            trusted_certs=trusted_certs,
            supports_port_resource_request=supports_port_resource_request)
    
      # 创建虚机的方法       
     def _create_instance(self, context, instance_type,
           image_href, kernel_id, ramdisk_id,
           min_count, max_count,
           display_name, display_description,
           key_name, key_data, security_groups,
           availability_zone, user_data, metadata, injected_files,
           admin_password, access_ip_v4, access_ip_v6,
           requested_networks, config_drive,
           block_device_mapping, auto_disk_config, filter_properties,
           reservation_id=None, legacy_bdm=True, shutdown_terminate=False,
           check_server_group_quota=False, tags=None,
           supports_multiattach=False, trusted_certs=None,
           supports_port_resource_request=False): 
           # 参数的验证检查
           /**...../
           
           # 获取到镜像image信息
           if image_href:
                image_id, boot_meta = self._get_image(context, image_href)
            else:
                # This is similar to the logic in _retrieve_trusted_certs_object.
                if (trusted_certs or
                    (CONF.glance.verify_glance_signatures and
                     CONF.glance.enable_certificate_validation and
                     CONF.glance.default_trusted_certificate_ids)):
                    msg = _("Image certificate validation is not supported "
                            "when booting from volume")
                    raise exception.CertificateValidationFailed(message=msg)
                image_id = None
                boot_meta = self._get_bdm_image_metadata(
                    context, block_device_mapping, legacy_bdm)
             # 参数的验证检查
             /****...... /
             
            
           if CONF.cells.enable:
            for instance in instances:
                instance.create()
            
            # 检查实例的配额
            if CONF.quota.recheck_quota:
                try: 
                    # 当前实例数量是否已到最大值
                    compute_utils.check_num_instances_quota(
                        context, instance_type, 0, 0,
                        orig_num_req=len(instances))
                except exception.TooManyInstances:
                    with excutils.save_and_reraise_exception():
                        # Need to clean up all the instances we created
                        # along with the build requests, request specs,
                        # and instance mappings.
                        self._cleanup_build_artifacts(instances,
                                                      instances_to_build)
                 # 把消息放到队列中
                self.compute_task_api.build_instances(context,
                    instances=instances, image=boot_meta,
                    filter_properties=filter_properties,
                    admin_password=admin_password,
                    injected_files=injected_files,
                    requested_networks=requested_networks,
                    security_groups=security_groups,
                    block_device_mapping=block_device_mapping,
                    legacy_bdm=False)
            else:
                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)      
       return instances, reservation_id     

compute_task_api.build_instances(...)/compute_task_api.schedule_and_build_instances(...)消息放入队列中

# nova/conductor/api.py
def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
            security_groups, block_device_mapping, legacy_bdm=True,
            request_spec=None, host_lists=None):
        self.conductor_compute_rpcapi.build_instances(context,
                instances=instances, image=image,
                filter_properties=filter_properties,
                admin_password=admin_password, injected_files=injected_files,
                requested_networks=requested_networks,
                security_groups=security_groups,
                block_device_mapping=block_device_mapping,
                legacy_bdm=legacy_bdm, request_spec=request_spec,
                host_lists=host_lists)

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)

使用rpc api以消息队列通信

# nova/conductor/rpc.api
def build_instances(self, context, instances, image, filter_properties,
        admin_password, injected_files, requested_networks,
        security_groups, block_device_mapping, legacy_bdm=True,
        request_spec=None, host_lists=None):
    image_p = jsonutils.to_primitive(image)
    kwargs = {"instances": instances, "image": image_p,
              "filter_properties": filter_properties,
              "admin_password": admin_password,
              "injected_files": injected_files,
              "requested_networks": requested_networks,
              "security_groups": security_groups,
              "request_spec": request_spec,
              "host_lists": host_lists}
    # api版本的检查          
    version = '1.19'
    if not self.client.can_send_version(version):
        version = '1.18'
        kwargs.pop("host_lists")
    ..../
    
    cctxt = self.client.prepare(version=version)
    cctxt.cast(context, 'build_instances', **kwargs)

def schedule_and_build_instances(self, context, build_requests,
                                 request_specs,
                                 image, admin_password, injected_files,
                                 requested_networks,
                                 block_device_mapping,
                                 tags=None):
    version = '1.17'
    kw = {'build_requests': build_requests,
          'request_specs': request_specs,
          'image': jsonutils.to_primitive(image),
          'admin_password': admin_password,
          'injected_files': injected_files,
          'requested_networks': requested_networks,
          'block_device_mapping': block_device_mapping,
          'tags': tags}
    # api版本的检查
    if not self.client.can_send_version(version):
        version = '1.16'
        del kw['tags']
    # 发送消息到消息队列
    cctxt = self.client.prepare(version=version)
    cctxt.cast(context, 'schedule_and_build_instances', **kw)

猜你喜欢

转载自blog.csdn.net/weixin_40686306/article/details/88052765