微信号:Gcloudyun

介绍:专注游戏运营十五年,提供的不止是技术服务 G云是以盛大游戏15年支撑经验为基础,经过几百款游戏锤炼打造而成的定制云平台; 拥有20个骨干机房、近万台各种配置的备用服务器,满足你不同的需求; 提供稳定、可靠、高效、灵活的服务。

Openstack liberty源码分析之云主机的启动过程1

2016-08-17 16:48 李忠文

       接触Openstack也有一段时间了,因为工作需要着重阅读了Glance、Nova、Cinder模块源码并通过搭建的devstack测试环境调试学习相关操作的执行流程。现准备陆续将相关的学习成果和心得分享出来,希望对读者有所帮助。这是第一篇 -- 介绍云主机启动时,nova-api的处理过程。


Nova架构图


在进入今天的主题前,我们先来了解下nova的架构图,如下:



       从上图我们可以看到nova服务由多个组件组成,包括:nova-api,nova-compute,nova-scheduler等(nova服务详细的组成,请看nova服务概况),各组件以消息队列MQ及DB为中心相互协作,共同完成云主机的启动。本文中涉及到的组件包括:nova-api,Glance-api,MQ,DB及Nova-conductor。(上图红圈部分),下面来看具体内容:

启动云主机

      用户既可以通过Dashboard启动云主机,也可以通过Nova CLI命令行实现,下面的命令启动了一台类型为2(--flavor 2)名为vm2的云主机,使用的镜像是:226bc6e5-60d7-4a2c-bf0d-a568a1e26e00,--debug参数用打开调试信息:

 

nova --debug boot --flavor 2 --image226bc6e5-60d7-4a2c-bf0d-a568a1e26e00 vm2


nova-api的处理过程


      启动云主机过程中,nova-api 主要完成输入参数的验证和装配,之后根据输入参数创建云主机实例对象、记录数据库条目,最后将请求转发给nova-conductor 进行后续处理。简单分析如下:


根据nova-api启动过程中建立的路由映射以及调试信息的佐证,如下:



       我们知道启动云主机的请求经过novaclient的处理后,发送给了nova-api,并由nova/api/openstack/servers.py.ServersController.create 处理,从代码上看该方法的处理过程比较简单,主要是完成:用户参数的转换、policy验证,方法声明如下:

 

@wsgi.response(202)

@extensions.expected_errors((400, 403, 409, 413))

@validation.schema(schema_server_create_v20, '2.0','2.0')

@validation.schema(schema_server_create, '2.1')

def createselfreqbody):


来看看body参数的内容:



       body参数是该次请求的请求体,显示的是待启动云主机的配置参数,包含有:云主机名、image uuid,云主机类型等,很明显这就是之前在nova boot 命令行中指定的参数,之后根据extension 扩展方法转换用户参数,完成参数解析,policy认证等,最后将请求转发给nova/compute/api.py.API.create 。


        API.create方法虽然参数很多,但启动云主机必须的参数只有instance_type及image_href ,分别指定主机类型及image uuid,处理逻辑也非常的简单:执行网络及块设备policy检查(如果有),接着将参数原封不动的传递给_create_instance 方法,该方法主要完成如下工作:

 

·        验证输入参数

·        获取镜像元信息

·        生成主机配置并创建主机实例对象

·        在数据库生成主机信息记录

下面具体来看看该方法的实现(说明见注释部分):

#nova/compute/api.py.API

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,

          reservation_id=None, scheduler_hints=None,

          legacy_bdm=True, shutdown_terminate=False,

           check_server_group_quota=False):

   """Verify all the input parameters regardless of theprovisioning

    strategy beingperformed and schedule the instance(s) for

    creation.

   """

 

    # Normalize andsetup some parameters

    if reservation_idis None:

       reservation_id = utils.generate_uid('r')

    security_groups= security_groups or ['default']

    min_count =min_count or 1

    max_count =max_count or min_count

   block_device_mapping = block_device_mapping or []

    if notinstance_type:

       instance_type = flavors.get_default_flavor()

 

    #根据uuid获取imagemetadata信息

    if image_href:

        image_id,boot_meta = self._get_image(context, image_href)

    else:

        image_id =None

        boot_meta =self._get_bdm_image_metadata(

           context, block_device_mapping, legacy_bdm)

 

   self._check_auto_disk_config(image=boot_meta,

                                auto_disk_config=auto_disk_config)

 

    handle_az =self._handle_availability_zone

    availability_zone,forced_host, forced_node = handle_az(context,

                                                       availability_zone)

 

    if notself.skip_policy_check and (forced_host or forced_node):

       check_policy(context, 'create:forced_host', {})

 

    #根据输入参数,生成主机配置

    base_options,max_net_count = self._validate_and_build_base_options(

           context,

           instance_type, boot_meta, image_href, image_id, kernel_id,

            ramdisk_id,display_name, display_description,

           key_name, key_data, security_groups, availability_zone,

           forced_host, user_data, metadata, access_ip_v4,

           access_ip_v6, requested_networks, config_drive,

           auto_disk_config, reservation_id, max_count)

 

    # max_net_countis the maximum number of instances requested by the

    # user adjustedfor any network quota constraints, including

    # considerationof connections to each requested network

    ifmax_net_count == 0:

        raiseexception.PortLimitExceeded()

    elifmax_net_count < max_count:

       LOG.debug("max count reduced from %(max_count)d to "

                 "%(max_net_count)d due to network port quota",

                 {'max_count': max_count,

                  'max_net_count': max_net_count})

        max_count =max_net_count

 

#归总用户输入(--block-device-mapping参数)、镜像属性中指定、flavor配置中指定的块设备映

#

   block_device_mapping = self._check_and_transform_bdm(context,

       base_options, instance_type, boot_meta, min_count, max_count,

       block_device_mapping, legacy_bdm)

 

    # We can't dothis check earlier because we need bdms from all sources

    # to have beenmerged in order to get the root bdm.

    self._checks_for_create_and_rebuild(context,image_id, boot_meta,

           instance_type, metadata, injected_files,

           block_device_mapping.root_bdm())

 

    instance_group= self._get_requested_instance_group(context,

                               scheduler_hints,check_server_group_quota)

 

    #创建云主机实例对象,并生成数据库条目

    instances =self._provision_instances(context, instance_type,

           min_count, max_count, base_options, boot_meta, security_groups,

           block_device_mapping, shutdown_terminate,

           instance_group, check_server_group_quota)

 

    #scheduler需要用的过滤选项

   filter_properties = self._build_filter_properties(context,

           scheduler_hints, forced_host,

           forced_node, instance_type)

 

    #更新数据库中实例的启动状态

    for instance ininstances:

       self._record_action_start(context, instance,

                                 instance_actions.CREATE)

 

    #调用conductorapi,通过conductor rpc将请求转发给conductor manager

   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)

 

    return(instances, reservation_id)

 

      接着上文的分析,nova/conductor/api.py.ComputeTaskAPI.build_instance 直接将请求转发给nova/conductor/rpcapi.py.ComputeTaskAPI.build_instance,来看看该函数的实现(请根据注解阅读):

#nova/conductor/rpcapi.py.ComputeTaskAPI

def build_instances(self, context, instances, image,

   filter_properties,

    admin_password,injected_files, requested_networks,

    security_groups,block_device_mapping, legacy_bdm=True):

   """

    该函数的逻辑清晰简单:根据rpc client的版本,调整云主机参数:主机过滤参数,网络参数,

    块设备映射参数,最后通过一个异步rpc调用,将请求转发给nova-conductor

   """

    image_p =jsonutils.to_primitive(image)

    version ='1.10'

    if notself.client.can_send_version(version):

        version ='1.9'

    if'instance_type' in filter_properties:

         flavor =filter_properties['instance_type']

         flavor_p =objects_base.obj_to_primitive(flavor)

        filter_properties = dict(filter_properties,

                                   instance_type=flavor_p)

    kw ={'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}

    if notself.client.can_send_version(version):

        version ='1.8'

       kw['requested_networks'] =

       kw['requested_networks'].as_tuples()

    if notself.client.can_send_version('1.7'):

        version ='1.5'

        bdm_p =

               objects_base.obj_to_primitive(block_device_mapping)

       kw.update({'block_device_mapping': bdm_p,

                  'legacy_bdm': legacy_bdm})

 

    #返回_CallContext对象

    cctxt =self.client.prepare(version=version)

    #发送异步rpc消息到消息队列,nova-conductor会收到该消息

   cctxt.cast(context, 'build_instances', **kw)

 

      至此,nova-api的任务就完成了,总结如下:在启动云主机过程中nova-api主要完成:输入参数验证,生成云主机配置,将云主机信息添加到数据库(nova.instances)等操作,最后将请求转发给nova-conductor处理;下一篇文章将分析nova-conductor的处理过程。敬请关注G云公众号!!!






 
G云定制云 更多文章 Docker系列文章--Docker内置Swarm 模式介绍 MySQL字符集介绍及乱码解决方法 Docker系列文章--基于Docker的高可用架构实践 Docker系列文章--Docker容器内多进程管理(二) 数据挖掘之用户画像实时更新流程
猜您喜欢 Python基础教程3:基础语法 业务运维实战:腾讯是怎么优化APP用户体验的? 每日安全动态推送(11-15) 六月书单 用户到底长啥样?三步教您玩转它