目录
前言
Nova 控制着一个个虚拟机的状态变迁和生命周期,这种对虚拟机生命周期的管理是由 nova-compute service 来完成的。
在了解 Nova 创建虚拟机的流程之前,需要先补充一些 Openstack 基本概念。
Instance
Instance 表示一个虚拟机,是虚拟化世界的个体,类似与现实世界中的人类。所以,相同的,Instance 也具有一些特征性的标识,也可以称之为属性。如下:
1. 一个唯一的 ID 去标识 Instance
2. 一些描述 Instance 规格特征的信息。EG. Size/内存/InstanceName
3. 有字段去表示 Instance 运行在哪一台 Host
4. 有字段去表示 Instance Status
5. 有字段去表示 Create | Delete Instance 的时间
…
Nova 在 /opt/stack/nova/nova/objects/instance.py 中对 Instance 进行了描述。
NOTE:/opt/stack/nova/nova/objects/ 该目录下存放数据库表对象文件,一个 Class 映射到一个张数据库表。
86 class _BaseInstance(base.NovaPersistentObject, base.NovaObject,
1 base.NovaObjectDictCompat):
2 fields = {
3 'id': fields.IntegerField(),
4
5 'user_id': fields.StringField(nullable=True),
6 'project_id': fields.StringField(nullable=True),
7
...
Flavor
在 Create Instance 之前,需要为 Instance 指定一组资源(Disk/Memory/VCPU/RootDisk/EphemeralDisk/Swap)。
nova-compute 在执行创建之前,需要通过这些资源配置来判断是否由足够的 Host 资源来实现创建。这组资源的设置就是 flavor,即创建虚拟机的规格。每个 Instance 对象的 instance_type_id
字段就表示该 Instance 所拥有的 flavor 。
执行 Commands :nova flavor-list
可以查看 Nova 默认 flavor 的信息 ; nova flavor-create
创建新的 flavor 。
Instance Status
Instance 拥有三个字段与描述其状态有关,这些状态刚被用于程序流的条件判断。
power_status: 使用 libvirt 或 Virt Driver 提供的接口从 Hypervisor 中获取的 Instance Power Status。
- Running
- Shutdown
- NoState
vm_state: 虚拟机的稳定状态。
- Active:正常运行
- Suspended:停止运行
task_state:过渡状态,与 Compute API 的执行密切相关,表示 Instance 正在执行什么任务,只有在执行任务的时候才会有 task_state 。
Virt Driver
Openstack Nova 仅仅是作为云计算虚拟机的管理工具,其本身并不提供任何的虚拟化技术,而是交由具体的 Hypervisor 来实现虚拟机的创建和管理。因此,nova-compute 需要和不同的 Hpyervisor 进行交互,并使用 Virt Driver 来作为这种交互的支撑,其代码实现存放在 /opt/stack/nova/nova/virt/ 目录下。
目前,Nova 实现了 Hyper-V/Libvirt/VMware/Xen 这四种 Virt Dirver 。可以通过配置选项 compute_dirver
来指定使用哪一种 Virt Driver 。
compute_driver = libvirt.LibvirtDriver
compute_driver=vmwareapi.VMwareVCDriver
EXAMPLE:nova-compute 通过 Virt Dirver 来调用 Libvirt 库中提供的 API 来实现虚拟机的管理。
从上图可以看出,nova-compute 必须部署在 Linux+KVM 的 Host 上,当然,现在的 Linux Kernel Version 大多都嵌入了 KVM 虚拟化技术。
Resource Tracker
nova-compute 需要在数据库中存储 Host 的资源使用情况,以便于 nova-scheduler 获取作为选择 Host 依据的数据。Node Project 使用 ConputeNode 数据库表对象来保存 Compute Node的配置信息和资源使用情况(/opt/stack/nova/nova/objects/compute_node.py)。所以 nova-compute 会为每一个 Host 创建一个 ResourceTracker 对象,用于更新 ComputeNode 对象在数据库中对应的 compute_nodes 表。
有两种更新*compute_nodes表的方式:*
Claim 机制:在创建 Instance 之前,预先测试 Host 的可用资源能否满足 Create Instance。如果满足,则首先更新数据库,将虚拟机申请的资源从可用资源中减去。 如果后来创建失败或者将虚拟机删除时,会再通过 Claim 将原来减去的部分再添加到可用资源中去。实现:nova.compute.resource_tracker:instance_claims
Periodic Task:在 nova.compute.manager.ComputeManager 中有个周期性任务
update_available_resource()
用于更新 Host 的资源数据。
NOTE:上面两种数据库更新方式并不冲突。Claim 实在数据库当前数据的基础上去计算并更新,可以保证数据库里可用资源的及时更新。 Periodic Task 是为了数据库内信息的准确性,它每次执行都会通过 Hypervisor 去获取 Host 的信息,并将这些信息更新到数据库中。前者是在 Create Instance 的时候执行,后者是周期性执行。
nova-conductor
Conductor Service 的加入,使得 nova-compute 和数据库解耦,为数据库提供了一重安全保障,提高了对数据库的访问效率,而且还可以实现升级数据库 schema 的同时无需上级 nova-compute 。所以 nova-compute 所有访问数据库的操作都是交由 nova-conductor 去完成。
随着 nova-conductor 的不断完善,它还需要承担部分原来属于 nova-compute 的 TaskAPI 的任务。TaskAPI 主要包含了耗时较长的任务,例如:Create Instance/Migrate Instance 等等。
Create Instance(nova-conductor阶段)
Create Instance 属于 TaskAPI 任务,耗时较长,由 nova-conductor 来承担。但是需要注意的是,对 Instance 管理的流程可以分为俩个阶段,就是调度阶段和执行阶段。调度阶段由 Openstack 负责,主要是 Nova Project 中的几个 Services 来协同完成。而执行阶段则由 Hypervisor 来具体实现。Openstack 仅仅扮演了 管理者的角色。
- 在 nova.conductor.rpcapi 中定义一个
build_instances()
RPC 接口函数:
301 def build_instances(self, context, instances, image, filter_properties,
1 admin_password, injected_files, requested_networks,
2 security_groups, block_device_mapping, legacy_bdm=True):
3 image_p = jsonutils.to_primitive(image)
4 version = '1.10'
5 if not self.client.can_send_version(version):
6 version = '1.9'
7 if 'instance_type' in filter_properties:
8 flavor = filter_properties['instance_type']
9 flavor_p = objects_base.obj_to_primitive(flavor)
10 filter_properties = dict(filter_properties,
11 instance_type=flavor_p)
12 kw = {'instances': instances, 'image': image_p,
13 'filter_properties': filter_properties,
14 'admin_password': admin_password,
15 'injected_files': injected_files,
16 'requested_networks': requested_networks,
17 'security_groups': security_groups}
18 if not self.client.can_send_version(version):
19 version = '1.8'
20 kw['requested_networks'] = kw['requested_networks'].as_tuples()
21 if not self.client.can_send_version('1.7'):
22 version = '1.5'
23 bdm_p = objects_base.obj_to_primitive(block_device_mapping)
24 kw.update({'block_device_mapping': bdm_p,
25 'legacy_bdm': legacy_bdm})
26
27 cctxt = self.client.prepare(version=version)
28 cctxt.cast(context, 'build_instances', **kw)
# /opt/stack/nova/nova/conductor/manager.py
# 创建虚拟机属于 TaskAPI 任务,所有的 TaskAPI 都交由 nova-conductor 来处理,所以 manager.py 的实现在 Conductor 中
515 class ComputeTaskManager(base.Base):
1 """Namespace for compute methods.
2
3 This class presents an rpc API for nova-conductor under the 'compute_task'
4 namespace. The methods here are compute operations that are invoked
5 by the API service. These methods see the operation to completion, which
6 may involve coordinating activities on multiple compute nodes.
7 """
...
526 def __init__(self):
1 super(ComputeTaskManager, self).__init__()
2 self.compute_rpcapi = compute_rpcapi.ComputeAPI()
3 self.image_api = image.API()
4 self.servicegroup_api = servicegroup.API()
5 self.scheduler_client = scheduler_client.SchedulerClient()
6 self.notifier = rpc.get_notifier('compute', CONF.host)
# 其中 Scheduler client 是对 Scheduler_rpcapi 的封装,本质上是一个 Scheduler 提供的 rpcapi:
# nova.manager.ComputeTaskManager:scheduler_client
# ==> nova.scheduler.client.__init__:__init__
# ==> nova.scheduler.client.query.SchedulerQueryClient:__init__
# ==> self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
...
# 1.(被调用) HTTP Request ==> nova.api ==> RPC cast ==> nova-conductor (nova.conductor.manager:ComputeTaskManager.build_instances() 是 nova.conductor.rpcapi.build_instances RPC 接口的实际功能实现函数)
# 参数传递过程: nova-api 调用 conductor.rpcapi:build_instances() 并传入实参
# ==> 将实参和其他信息打包发送到消息队列(数据流形式)
# ==> 将实参传入 conductor.manager:build_instances() ;
708 def build_instances(self, context, instances, image, filter_properties,
1 admin_password, injected_files, requested_networks,
2 security_groups, block_device_mapping=None, legacy_bdm=True):
3 # TODO(ndipanov): Remove block_device_mapping and legacy_bdm in version
4 # 2.0 of the RPC API.
713 request_spec = scheduler_utils.build_request_spec(context, image,
1 instances)
# request_spec 是一个字典类型,包含了详细的虚拟机信息,nova-scheduler 依据这些信息来选择一个最佳的主机并返回给 nova-conductor
...
# 2.(调用) nova-conductor 通过 RPC call 方式调用 nova-scheduler 的接口函数: nova.scheduler.rpcapi:select_destinations()
# 通过 nova-scheduler 来获取 HOSTS
738 hosts = self.scheduler_client.select_destinations(context,
1 request_spec, filter_properties)
...
# 3.(调用) nova-conductor ==> RPC cast ==> nova-compute (nova.compute.build_and_run_instance())
763 self.compute_rpcapi.build_and_run_instance(context,
1 instance=instance, host=host['host'], image=image,
2 request_spec=request_spec,
3 filter_properties=local_filter_props,
4 admin_password=admin_password,
5 injected_files=injected_files,
6 requested_networks=requested_networks,
7 security_groups=security_groups,
8 block_device_mapping=bdms, node=host['nodename'],
9 limits=host['limits'])
在 经过一番远程调用之后,终于进入到了 nova-compute 调用 Virt Dirver 的阶段。