openstack虚拟机创建流程

openstack虚拟机创建流程

  • novaclient<->keystone
    • novaclient通过APIkeystone获取认证信息
    • keystone通过用户请求认证信息,并生成auth-token返回给对应的认证请求
  • novaclient<->nova-api
    • novaclient通过APInova-api发送http post create servers请求
  • nova-api<->keystone
    • nova-api接受请求后向keystone发送认证请求,查看token是否有效
    • keystone验证token是否有效,将结果返回给nova-api
  • nova-api<->db
    • nova-api和db通信,初始化新建虚拟机的数据库记录
  • nova-api<->nova-scheduler
    • nova-api调用rpc,向nova-scheduler请求是否创建虚拟机资源
    • nova-scheduler通过rpc,获取nova-api请求
  • nova-scheduler<->db
    • nova-scheduler查询db中计算资源的情况,通过filter和weight调度算法决定host
    • nova-scheduler更新数据库中虚拟机对应的物理主机信息
  • nova-scheduler<->nova-compute
    • nova-scheduler通过rpc调用向nova-compute发送对应的创建虚拟机请求的消息
    • nova-compute通过rpc获取nova-scheduler创建虚拟机请求的消息
  • nova-compute<->nova-conductor
    • nova-compute通过rpc调用向nova-conductor请求获取虚拟机消息
    • nova-conductor通过rpc获取nova-compute消息
  • nova-conductor<->db
    • nova-conductor查询db中虚拟机信息
  • nova-conductor<->nova-compute
    • nova-conductor调用rpc发送虚拟机信息
    • nova-compute通过rpc获取nova-conductor信息
  • nova-compute<->glance
    • nova-compute请求glance-api获取镜像image
    • glance-apikeystone获取token认证
    • keystone返回验证
    • nova-compute获取镜像信息(URL)
  • nova-compute<->neutron
    • nova-compute请求neutron-server获取网络信息
    • neutron-serverkeystone获取token认证
    • keystone返回验证
    • nova-compute获得虚拟机网络信息
  • nova-compute<->cinder
    • nova-compute请求cinder-api获取卷信息
    • cinder-apikeystone获取token认证
    • keystone返回验证
    • nova-compute获得虚拟机持久化卷存储信息
  • nova-compute
    • nova-compute调用虚拟化驱动创建虚拟机
    • build instance
Create server code listing example
#!/usr/bin/env python
import time
from credentials import get_nova_credentials_v2
from novaclient.client import Client

try:
    credentials = get_nova_credentials_v2()
    nova_client = Client(**credentials)

    image = nova_client.images.find(name="cirros")
    flavor = nova_client.flavors.find(name="m1.tiny")
    net = nova_client.networks.find(label="private")
    nics = [{'net-id': net.id}]
    instance = nova_client.servers.create(name="vm2", image=image,
                                      flavor=flavor, key_name="keypair-1", nics=nics)
    print("Sleeping for 5s after create command")
    time.sleep(5)
    print("List of VMs")
    print(nova_client.servers.list())
finally:
    print("Execution Completed")

novaclient

  • novaclient
    • v2
      • client.py
      • service.py
    • base.py
    • client.py
    • shell.py

创建虚拟机一般是从界面或命令行发出请求开始的
两种方式都会借助novaclient向nova-api发出HTTP请求

novaclient会向Keystone发送用户的用户名、密码、域名信息来申请一个有效的token
然后向nova-api发送带有token信息的请求

/shell.py

# 实际调用client
    self.cs = client.Client()
    args.func(self.cs, args)
client

/client.py

# HTTP服务和keystone验证
# keystone的keystoneauth1:通过keystone验证token
class SessionClient()
def _construct_http_client()

# 基于version,初始化client
# version:1.1、2、2.x
def Client(version, username=None, password=None, project_id=None,
           auth_url=None, **kwargs):

    #数据验证
    
    api_version, client_class = _get_client_class_and_version(version)  

    return client_class(api_version=api_version,
                        auth_url=auth_url,
                        direct_use=False,
                        username=username,
                        **kwargs)

client调用novaclient.v2.client.Client
/client.py

def _get_client_class_and_version(version):
    return version, importutils.import_class(
        "novaclient.v%s.client.Client" % version.ver_major)

/v2/client.py

class Client(object):
    def __init__():
        self.services = services.ServiceManager(self)

    #调用client._construct_http_client
        #返回SessionClient类
client.server.create

/v2/servers.py

class ServerManager(base.BootingManagerWithFind):

    def _boot():
        return self._create('/servers', body, response_key,
                            return_raw=return_raw, **kwargs)

    def create():
        return self._boot(response_key, *boot_args, **boot_kwargs)

/base.py
通过父类寻找create方法

class Manager(HookableMixin):
    def _create(self, url, body, response_key, return_raw=False,
                obj_class=None, **kwargs):
        resp, body = self.api.client.post(url, body=body)

总结:novaclient实例化Client调用create最终nova-api调用post方法

nova

官方文档:https://docs.openstack.org/api-ref/compute/?expanded=create-server-detail#create-server
POST
/servers
Create Server
  • nova
    • api
      • openstack
        • compute
          • routes.py
          • servers.py
    • compute
      • __init__.py
      • api.py
      • rpcapi.py
    • conductor
      • __init__.py
      • api.py
      • manager.py
      • rpcapi.py
    • virt
      • libvirt
        • __init__.py
        • driver.py
      • driver.py
nova-api

调用nova-api的post方法
nova-api收到请求后会向Keystone验证token的有效性,token验证通过后,nova-api会向数据库写入虚拟机的初始数据

nova.api.openstack.compute.servers.ServersController::create()

/nova/api/openstack/compute/routes.py

server_controller = functools.partial(_create_controller,
    servers.ServersController,
    [,]
)
ROUTE_LIST = (
    ('/servers', {
        'GET': [server_controller, 'index'],
        'POST': [server_controller, 'create']
    }),
)

/nova/api/openstack/compute/servers.py

class ServersController(wsgi.Controller):
    def __init__(self, **kwargs):
        self.compute_api = compute.API()

    def create(self, req, body):
        (instances, resv_id) = self.compute_api.create()
nova.compute.api.API::create()

/nova/compute/__init__.py

#通过配置文件导入nova.compute.api.API
CELL_TYPE_TO_CLS_NAME = {'api': 'nova.compute.cells_api.ComputeCellsAPI',
                         'compute': 'nova.compute.api.API',
                         None: 'nova.compute.api.API',
                        }
def API(*args, **kwargs)

/nova/compute/api.py

@profiler.trace_cls("compute_api")
class API(base.Base):
    #API for interacting with the compute manager
    def __init__():
        self.compute_task_api = conductor.ComputeTaskAPI()

    #检查信息
    #发送实例信息给scheduler
    #scheduler决定host和db
    @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):
         #返回:(instances, reservation_id)
         return self._create_instance()

    def _create_instance():

        #根据配置文件选择不同创建实例方法
        self.compute_task_api.build_instances()
        self.compute_task_api.schedule_and_build_instances()
调用conductor API

/nova/conductor/__init__.py

from nova.conductor import api as conductor_api
API = conductor_api.API
ComputeTaskAPI = conductor_api.ComputeTaskAPI

/nova/conductor/api.py

class ComputeTaskAPI(object):
    """ComputeTask API that queues up compute tasks for nova-conductor."""

    def __init__(self):
        self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()

    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)
通过rpc调用nova-conduct·or

nova-conductor是nova-compute和数据库之间的桥梁,它可以防止nova-compute直接访问数据库,从而提高对数据库访问的安全性

topic publisher在消息队列中发布topic
RPC_TOPIC = ‘conductor’
method = build_instances
等待topic consumer接收并调用nova-conductor manager中的build_instance方法

/nova/conductor/rpcapi.py

@profiler.trace_cls("rpc")
class ComputeTaskAPI(object):

    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):

        #异步消息队列
        #RPC_TOPIC = 'conductor'
        cctxt = self.client.prepare(version=version)
        cctxt.cast(context, 'build_instances', **kwargs)

/nova/conductor/manager.py

@profiler.trace_cls("rpc")
class ComputeTaskManager(base.Base):

    def __init__(self):
        #nova-scheduler
        self.query_client = query.SchedulerQueryClient()
        self.report_client = report.SchedulerReportClient()
        #nova-compute
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
        
    def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
            security_groups, block_device_mapping=None, legacy_bdm=True,
            request_spec=None, host_lists=None):

            host_lists = self._schedule_instances(context, spec_obj,
                        instance_uuids, return_alternates=True)

            self.compute_rpcapi.build_and_run_instance(context,
                    instance=instance, host=host.service_host, image=image,
                    request_spec=local_reqspec,
                    filter_properties=local_filter_props,
                    admin_password=admin_password,
                    injected_files=injected_files,
                    requested_networks=requested_networks,
                    security_groups=security_groups,
                    block_device_mapping=bdms, node=host.nodename,
                    limits=host.limits, host_list=host_list)

    def _schedule_instances(self, context, request_spec,
                            instance_uuids=None, return_alternates=False):
        host_lists = self.query_client.select_destinations(
                context, request_spec, instance_uuids, return_objects=True,
                return_alternates=return_alternates)

通过rpc调用nova-scheduler

nova-conductor收到请求后首先以rpc.call的方式请求nova-scheduler完成虚拟机创建的调度工作
nova-scheduler使用过滤和权重计算的方法来选定创建虚拟机的主机,过滤器和权重计算方法可以在nova.conf中配置
nova-scheduler自己会维护一份计算节点数据,并在数据库中对比
调度完成后将选定的host返回给nova-conductor,由nova-conductor向nova-compute发起rpc.cast的创建请求

/nova/scheduler/client/query.py

class SchedulerQueryClient(object):
    def select_destinations():
        return self.scheduler_rpcapi.select_destinations()

/nova/scheduler/rpcapi.py

@profiler.trace_cls("rpc")
class SchedulerAPI(object):
    def select_destinations():
        # rpc call
        return cctxt.call(ctxt, 'select_destinations', **msg_args)

/nova/scheduler/manager.py

    @messaging.expected_exceptions(exception.NoValidHost)
    def select_destinations(self, ctxt, request_spec=None,
            filter_properties=None, spec_obj=_sentinel, instance_uuids=None,
            return_objects=False, return_alternates=False):
        #filter过滤
        #过滤通用接口返回list,queens版本前后不同
        return selections
通过rpc调用nova-compute

topic publisher在消息队列中发布topic
RPC_TOPIC = ‘compute’
method = build_and_run_instance
等待topic consumer接收并调用nova-compute manager中的build_and_run_instance方法

/nova/compute/rpcapi.py

def build_and_run_instance(self, ctxt, instance, host, image, request_spec,
            filter_properties, admin_password=None, injected_files=None,
            requested_networks=None, security_groups=None,
            block_device_mapping=None, node=None, limits=None,
            host_list=None):
    #异步消息队列
    #RPC_TOPIC = "compute"
    cctxt = client.prepare(server=host, version=version)
    cctxt.cast(ctxt, 'build_and_run_instance', **kwargs)    

/nova/compute/manager.py

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

    def __init__():
        self.driver = driver.load_compute_driver(self.virtapi, compute_driver)
  
    def build_and_run_instance()
    def _do_build_and_run_instance()
        #vm_states.BUILDING

    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):

        self._build_resources():

            #调用neutronclient,通过neutron-server发起创建网络请求
            #network_info:neutronclient

            #标记task_states
            #task_states.BLOCK_DEVICE_MAPPING

            # --Hypervisor--
            self.driver.spawn(context, instance, image_meta,
                                          injected_files, admin_password,
                                          allocs, network_info=network_info,
                                          block_device_info=block_device_info)
            #task_states.SPAWNING
            #block_device_info/network_info/allocs     
    

此时虚拟机的vm_state为Building,虚拟机还只存在于数据库中,最后调用Hypervisor来完成创建

Hypervisor

OpenStack支持的Hypervisor包括Libvirt、hyperv、xen、vmware等,其中对libvirt管理的KVM的支持性最好

OpenStack默认的hypervisor:libvirt

寻找libvirt.LibvirtDriver

/nova/virt/driver.py

#nova.virt.%s' % compute_driver
#compute_driver = CONF.compute_driver

nova/virt/libvirt/__init__.py

from nova.virt.libvirt import driver
LibvirtDriver = driver.LibvirtDriver
driver.LibvirtDriver.spawn

/nova/virt/libvirt/driver.py

class LibvirtDriver(driver.ComputeDriver):

    def spawn(self, context, instance, image_meta, injected_files,
              admin_password, allocations, network_info=None,
              block_device_info=None):

        #disk_info/injection_info/gen_confdrive

        #下载image
        self._create_image()

        #生成xml文件
        self._get_guest_xml()

        #写入_create_domain_and_network
        #传入host

        self._create_domain_and_network(
            context, xml, instance, network_info,
            block_device_info=block_device_info,
            post_xml_callback=gen_confdrive,
            destroy_disks_on_failure=True)

        #间隔检测虚拟机状态:state == power_state.RUNNING

    #spawn调用    
    def _create_domain_and_network(self, context, xml, instance, network_info,
                                   block_device_info=None, power_on=True,
                                   vifs_already_plugged=False,
                                   post_xml_callback=None,
                                   destroy_disks_on_failure=False,
                                   external_events=None):
        self.plug_vifs(instance, network_info)
        guest = self._create_domain(
                        xml, pause=pause, power_on=power_on,
                        post_xml_callback=post_xml_callback)

    #_create_domain_and_network调用
    def plug_vifs(self, instance, network_info):
        #根据network_info中的vif添加网络

    #_create_domain_and_network调用
    def _create_domain(self, xml=None, domain=None,
                       power_on=True, pause=False, post_xml_callback=None):
        #传入domain和xml时,xml覆盖domain

        libvirt_guest.Guest.create(xml, self._host)

        guest = libvirt_guest.Guest(domain)

        #guest控制虚拟机状态power_on/pause
        
libvirt_guest.Guest.create

/nova/virt/libvirt/guest.py

/nova/virt/libvirt/guest.py
#create_domain调用
class Guest(object):
    @classmethod
    def create(cls, xml, host):
        # xml写入
        guest = host.write_instance_config(xml)

/nova/virt/libvirt/host.py

/nova/virt/libvirt/host.py
class Host(object):
    def write_instance_config(self, xml):
        #创建一个虚拟机,但未启动
        domain = self.get_connection().defineXML(xml)
        return libvirt_guest.Guest(domain)

    def get_connection()
    def _get_connection()
    def _get_new_connection()

    def _connect(uri, read_only):
        #返回一个libvirt连接
        return tpool.proxy_call(
            (libvirt.virDomain, libvirt.virConnect),
            libvirt.openAuth, uri, auth, flags)

总结:调用libvirt执行创建请求,下载image,生成xml文件,并根据xml创建网络、domain,直到虚拟机状态为running则创建成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值