开发者社区> 文艺小青年> 正文

nova分析(8)—— nova-compute

简介:
+关注继续查看

nova-compute是管理和配置虚拟机的入口,在所有compute机器上都需要该服务来创建和管理虚拟机。

nova-compute服务的入口在 nova.cmd.compute:main ,其启动过程与其他nova服务类似。

简单看下它是如何启动的, binary='nova-compute', topic='nova.compute.rpcapi' ,  manager=nova.compute.manager.ComputeManager ,manager类在初始化的时候会创建与其他服务交互的api实例:

复制代码
class ComputeManager(manager.Manager):
    """Manages the running instances from creation to destruction."""

    target = messaging.Target(version='3.32')

    def __init__(self, compute_driver=None, *args, **kwargs):
        """Load configuration options and connect to the hypervisor."""
        self.virtapi = ComputeVirtAPI(self)
        self.network_api = network.API()
        self.volume_api = volume.API()
        self.image_api = image.API()
        self._last_host_check = 0
        self._last_bw_usage_poll = 0
        self._bw_usage_supported = True
        self._last_bw_usage_cell_update = 0
        self.compute_api = compute.API()
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
        self.conductor_api = conductor.API()
        self.compute_task_api = conductor.ComputeTaskAPI()
        self.is_neutron_security_groups = (
            openstack_driver.is_neutron_security_groups())
        self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
        self.cells_rpcapi = cells_rpcapi.CellsAPI()
        self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
        self._resource_tracker_dict = {}
        self.instance_events = InstanceEvents()

        super(ComputeManager, self).__init__(service_name="compute",
                                             *args, **kwargs)

        # NOTE(russellb) Load the driver last.  It may call back into the
        # compute manager via the virtapi, so we want it to be fully
        # initialized before that happens.
        self.driver = driver.load_compute_driver(self.virtapi, compute_driver)   #libvirt.LibvirtDriver
        self.use_legacy_block_device_info = \
                            self.driver.need_legacy_block_device_info
复制代码

在service.start()中会初始化主机上的虚拟机和安全组,并开启RPC服务

复制代码
    def start(self):
        verstr = version.version_string_with_package()
        LOG.audit(_('Starting %(topic)s node (version %(version)s)'),
                  {'topic': self.topic, 'version': verstr})
        self.basic_config_check()
        # Initialization for a standalone compute service
        # init instances and iptables
        self.manager.init_host()
        self.model_disconnected = False
        ctxt = context.get_admin_context()
        try: # init conductor api
            self.service_ref = self.conductor_api.service_get_by_args(ctxt,
                    self.host, self.binary)
            self.service_id = self.service_ref['id']
        except exception.NotFound:
            try:
                self.service_ref = self._create_service_ref(ctxt)
            except (exception.ServiceTopicExists,
                    exception.ServiceBinaryExists):
                # NOTE(danms): If we race to create a record with a sibling
                # worker, don't fail here.
                self.service_ref = self.conductor_api.service_get_by_args(ctxt,
                    self.host, self.binary)

        """After the service is initialized, but before we fully bring
        the service up by listening on RPC queues, make sure to update
        our available resources (and indirectly our available nodes).
        """
        self.manager.pre_start_hook()

        if self.backdoor_port is not None:
            self.manager.backdoor_port = self.backdoor_port

        LOG.debug("Creating RPC server for service %s", self.topic)

        # start rpc api
        target = messaging.Target(topic=self.topic, server=self.host)

        endpoints = [
            self.manager,
            baserpc.BaseRPCAPI(self.manager.service_name, self.backdoor_port)
        ]
        endpoints.extend(self.manager.additional_endpoints)

        serializer = objects_base.NovaObjectSerializer()

        self.rpcserver = rpc.get_server(target, endpoints, serializer)
        self.rpcserver.start()

        self.manager.post_start_hook()

        LOG.debug("Join ServiceGroup membership for this service %s",
                  self.topic)
        # Add service to the ServiceGroup membership group.
        self.servicegroup_api.join(self.host, self.topic, self)

        if self.periodic_enable:
            if self.periodic_fuzzy_delay:
                initial_delay = random.randint(0, self.periodic_fuzzy_delay)
            else:
                initial_delay = None

            self.tg.add_dynamic_timer(self.periodic_tasks,
                                     initial_delay=initial_delay,
                                     periodic_interval_max=
                                        self.periodic_interval_max)
复制代码

这样,nova-compute服务就起来了,而之后接收到的请求都会到 nova.compute.manager.ComputeManager 类来处理。

最常使用的KVM虚拟化需要配置driver为libvirt.LibvirtDriver(所有支持的driver有 libvirt.LibvirtDriver, xenapi.XenAPIDriver, fake.FakeDriver, baremetal.BareMetalDriver, vmwareapi.VMwareVCDriver, hyperv.HyperVDriver).

下面来看看libvirt是如何创建虚拟机的:

复制代码
   def spawn(self, context, instance, image_meta, injected_files,
              admin_password, network_info=None, block_device_info=None):
        disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
                                            instance,
                                            block_device_info,
                                            image_meta)
        # 获取镜像信息
        self._create_image(context, instance,
                           disk_info['mapping'],
                           network_info=network_info,
                           block_device_info=block_device_info,
                           files=injected_files,
                           admin_pass=admin_password)
        # 生成libvirt配置文件格式的XML
        xml = self._get_guest_xml(context, instance, network_info,
                                  disk_info, image_meta,
                                  block_device_info=block_device_info,
                                  write_to_disk=True)
        # 创建卷、网络,并创建虚拟机
        self._create_domain_and_network(context, xml, instance, network_info,
                                        block_device_info)
        LOG.debug("Instance is running", instance=instance)

        def _wait_for_boot():
            """Called at an interval until the VM is running."""
            state = self.get_info(instance)['state']

            if state == power_state.RUNNING:
                LOG.info(_LI("Instance spawned successfully."),
                         instance=instance)
                raise loopingcall.LoopingCallDone()
        # 等待虚拟机状态变为RUNNING
        timer = loopingcall.FixedIntervalLoopingCall(_wait_for_boot)
        timer.start(interval=0.5).wait()
复制代码
_create_domain_and_network为实际的创建设备和创建虚拟机函数
复制代码
   def _create_domain_and_network(self, context, xml, instance, network_info,
                                   block_device_info=None, power_on=True,
                                   reboot=False, vifs_already_plugged=False):

        """Do required network setup and create domain."""
        # 查询卷的映射关系
        block_device_mapping = driver.block_device_info_get_mapping(
            block_device_info)
        # 挂载卷
        for vol in block_device_mapping:
            connection_info = vol['connection_info']
            disk_info = blockinfo.get_info_from_bdm(
                CONF.libvirt.virt_type, vol)
            # 根据volume类型,调用其connect_volume方法挂载卷
            # 支持的类型包括:
            #     [
            #       'iscsi=nova.virt.libvirt.volume.LibvirtISCSIVolumeDriver',
            #       'iser=nova.virt.libvirt.volume.LibvirtISERVolumeDriver',
            #       'local=nova.virt.libvirt.volume.LibvirtVolumeDriver',
            #       'fake=nova.virt.libvirt.volume.LibvirtFakeVolumeDriver',
            #       'rbd=nova.virt.libvirt.volume.LibvirtNetVolumeDriver',
            #       'sheepdog=nova.virt.libvirt.volume.LibvirtNetVolumeDriver',
            #       'nfs=nova.virt.libvirt.volume.LibvirtNFSVolumeDriver',
            #       'aoe=nova.virt.libvirt.volume.LibvirtAOEVolumeDriver',
            #       'glusterfs='nova.virt.libvirt.volume.LibvirtGlusterfsVolumeDriver',
            #       'fibre_channel=nova.virt.libvirt.volume.LibvirtFibreChannelVolumeDriver',
            #       'scality=nova.virt.libvirt.volume.LibvirtScalityVolumeDriver',
            #     ]
            conf = self._connect_volume(connection_info, disk_info)

            # cache device_path in connection_info -- required by encryptors
            if 'data' in connection_info:
                connection_info['data']['device_path'] = conf.source_path
                vol['connection_info'] = connection_info
                vol.save(context)

            # 如果是加密卷,需要attach到encryptor
            if (not reboot and 'data' in connection_info and
                    'volume_id' in connection_info['data']):
                volume_id = connection_info['data']['volume_id']
                encryption = encryptors.get_encryption_metadata(
                    context, self._volume_api, volume_id, connection_info)

                if encryption:
                    encryptor = self._get_volume_encryptor(connection_info,
                                                           encryption)
                    encryptor.attach_volume(context, **encryption)

        timeout = CONF.vif_plugging_timeout
        if (self._conn_supports_start_paused and
            utils.is_neutron() and not
            vifs_already_plugged and power_on and timeout):
            events = self._get_neutron_events(network_info)
        else:
            events = []

        launch_flags = events and libvirt.VIR_DOMAIN_START_PAUSED or 0
        domain = None
        try:
            with self.virtapi.wait_for_instance_event(
                    instance, events, deadline=timeout,
                    error_callback=self._neutron_failed_callback):
                # Plug VIFs into networks using vif_driver
                self.plug_vifs(instance, network_info)
                # Init iptables using firewall_driver
                self.firewall_driver.setup_basic_filtering(instance,
                                                           network_info)
                self.firewall_driver.prepare_instance_filter(instance,
                                                             network_info)
                # 调用libvirt库提供的方法创建虚拟机
                # if xml:
                #     err = _LE('Error defining a domain with XML: %s') % xml
                #     domain = self._conn.defineXML(xml)

                # if power_on:
                #     err = _LE('Error launching a defined domain with XML: %s') \
                #               % domain.XMLDesc(0)
                #     domain.createWithFlags(launch_flags)
                domain = self._create_domain(
                    xml, instance=instance,
                    launch_flags=launch_flags,
                    power_on=power_on)

                self.firewall_driver.apply_instance_filter(instance,
                                                           network_info)
        except exception.VirtualInterfaceCreateException:
            # Neutron reported failure and we didn't swallow it, so
            # bail here
            with excutils.save_and_reraise_exception():
                if domain:
                    domain.destroy()
                self.cleanup(context, instance, network_info=network_info,
                             block_device_info=block_device_info)
        except eventlet.timeout.Timeout:
            # We never heard from Neutron
            LOG.warn(_LW('Timeout waiting for vif plugging callback for '
                         'instance %(uuid)s'), {'uuid': instance['uuid']})
            if CONF.vif_plugging_is_fatal:
                if domain:
                    domain.destroy()
                self.cleanup(context, instance, network_info=network_info,
                             block_device_info=block_device_info)
                raise exception.VirtualInterfaceCreateException()

        # Resume only if domain has been paused
        if launch_flags & libvirt.VIR_DOMAIN_START_PAUSED:
            domain.resume()
        return domain
复制代码

 

本文转自feisky博客园博客,原文链接:http://www.cnblogs.com/feisky/p/3876087.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
+关注
文艺小青年
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
Spark Compute as a Service @ P
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载