将模型资源表示为对象¶
https://blueprints.launchpad.net/nova/+spec/resource-objects
添加模型对象来表示实例可能请求的资源。
问题描述¶
在 Nova 中,我们对虚拟机实例消耗的资源以及计算节点提供的资源建模的方式非常宽松。Flavor 对象具有许多静态字段,这些字段对应于简单的资源数量,例如 CPU、RAM 和本地磁盘。我们使用键/值对的字典和 JSON 序列化的 BLOB 数据来建模其他类型的资源,例如 PCIe 设备或 NUMA 单元布局。
计算节点上的资源跟踪器跟踪节点上消耗的资源集合。 ResourceTracker.old_resources 属性是一个字典,包含许多随机嵌套的字典。其中一些嵌套字典包括可扩展资源跟踪器的 ‘stats’ 字典、各种 ‘pci_devices’、‘pci_stats’ 和 ‘pci_passthrough_devices’ 内容、存储 nova.virt.hardware 中对象的 JSON 序列化表示形式的 ‘numa_topology’ blob,以及一个包含完全非结构化和未记录的键/值对的 ‘metrics’ 字典。除了这些非结构化的嵌套字典之外,ResourceTracker.old_resources 字典还包含顶级键,其中一些键与 Flavor 对象公开的简单资源类型匹配
local_gb_used: 计算节点上使用的磁盘空间(GB)
local_gb: 计算节点提供的本地磁盘总容量(GB)
free_disk_gb: 计算节点可用的磁盘空间(GB)
vcpus_used: 计算节点上消耗的虚拟 CPU 数量
vcpus: 计算节点提供的虚拟 CPU 总数
free_vcpus: 计算节点可用的虚拟 CPU 数量
memory_mb_used: 计算节点上使用的 RAM 容量(MB)
memory_mb: 计算节点提供的 RAM 总容量(MB)
free_ram_mb: 计算节点可用的 RAM 容量(MB)
running_vms: 在节点上运行的虚拟机实例数量
current_workload: 节点上工作负载的某个计算值
不幸的是,以上内容均未在代码中记录,为了向调度器添加新功能,人们只是继续向字典中添加这些自由形式的键和嵌套字典。这使得将实际使用量传达给调度器容易出错:资源跟踪器调用 scheduler_client.update_resource_stats() 方法,将此非结构化、未版本化的信息字典原样传递。这意味着调度器接口非常脆弱,因为任何决定向自由形式资源字典添加新键的开发人员都可以随意更改该接口。资源字典键中的拼写错误很容易在代码审查中被忽略,而且坦率地说,对于资源跟踪器中可扩展资源跟踪器周围的许多边缘情况代码,几乎没有功能测试。
除了接口脆弱的问题之外,资源字典的自由形式也意味着不同的资源以不同的方式被跟踪。PCI 资源以一种方式被跟踪,NUMA 拓扑使用情况以另一种方式被跟踪,CPU/RAM/磁盘以不同的方式被跟踪,并且在可扩展资源跟踪器的完全自由混乱中建模的任何资源都以完全不同的方式被跟踪,使用修改提供的 ‘stats’ 嵌套字典的插件。
资源跟踪器中由此造成的混乱的一个例子可以在这里看到
def _update(self, context, values):
"""Update partial stats locally and populate them to Scheduler."""
self._write_ext_resources(values)
# NOTE(pmurray): the stats field is stored as a json string. The
# json conversion will be done automatically by the ComputeNode object
# so this can be removed when using ComputeNode.
values['stats'] = jsonutils.dumps(values['stats'])
if not self._resource_change(values):
return
if "service" in self.compute_node:
del self.compute_node['service']
# NOTE(sbauza): Now the DB update is asynchronous, we need to locally
# update the values
self.compute_node.update(values)
# Persist the stats to the Scheduler
self._update_resource_stats(context, values)
if self.pci_tracker:
self.pci_tracker.save(context)
如果资源实际上以一致的方式建模,上述代码将如下所示
def _update(self, context, resources):
if not self._resource_change(resources):
return
# Notify the scheduler about changed resources
scheduler_client.update_usage_for_compute_node(
context, self.compute_node.id, resources)
与此类似,现有资源跟踪器中的以下代码
def _update_usage(self, context, resources, usage, sign=1):
mem_usage = usage['memory_mb']
overhead = self.driver.estimate_instance_overhead(usage)
mem_usage += overhead['memory_mb']
resources['memory_mb_used'] += sign * mem_usage
resources['local_gb_used'] += sign * usage.get('root_gb', 0)
resources['local_gb_used'] += sign * usage.get('ephemeral_gb', 0)
# free ram and disk may be negative, depending on policy:
resources['free_ram_mb'] = (resources['memory_mb'] -
resources['memory_mb_used'])
resources['free_disk_gb'] = (resources['local_gb'] -
resources['local_gb_used'])
resources['running_vms'] = self.stats.num_instances
self.ext_resources_handler.update_from_instance(usage, sign)
# Calculate the numa usage
free = sign == -1
updated_numa_topology = hardware.get_host_numa_usage_from_instance(
resources, usage, free)
resources['numa_topology'] = updated_numa_topology
将改为如下所示
def _update_usage(self, context, resources, usage, sign=1):
for resource_type, amount in usage.items():
resources[resource_type].consume(amount*sign)
用例¶
Nova 贡献者希望扩展调度器的功能,并打算将调度器分解为 Gantt 项目。为了有效地做到这一点,资源跟踪器和调度器周围的内部接口必须清理,以使用结构化对象。
项目优先级¶
此蓝图是 Kilo 版本中定义的 scheduler 重构工作的一部分。
提议的变更¶
建模请求的和使用的资源量是必须首先完成的基础构建块,然后才能对调度器或资源跟踪器接口进行任何进一步的重构或清理。
此蓝图包含一组模型对象的添加
一组表示不同类型资源数量的类
一组表示不同类型资源使用情况的类
一组表示不同类型资源的类
这些数量、使用情况和资源类型类将是 nova.objects 对象类,并将使 Nova 以版本化的方式演进它跟踪资源和暴露消耗资源使用数据的方式。
可扩展资源跟踪器的目标是建立一个框架,允许添加新的资源类型并允许以不同的方式计算这些资源。虽然此蓝图确实删除了可扩展资源跟踪器,但由于这些数量、使用情况和资源类型类被添加为 nova.object 对象,我们将获得演进和增强资源跟踪的能力,并使用对象版本控制和添加新的资源类型类。这种方法将提供 ERT 期望的灵活性,但具有 nova 对象系统的稳定性。
资源跟踪器代码然后将被转换为在使用上述类来表示计算节点上所有资源的使用情况时。
将添加一个新的 conductor RPC API 方法 update_compute_node_resource_usages 方法,该方法获取一个 nova.objects.ComputeNode 对象和一个 nova.objects.ResourceUsageCollection 对象,并使用来自资源跟踪器的资源使用量/数据更新计算节点数据库记录。
请注意,我们目前不建议对 compute_nodes 表的数据库模式或 nova.objects.ComputeNode 中的字段进行任何更改,但是我们添加了翻译方法到 nova.objects.ComputeNode,这些方法能够为计算节点上的每种资源类型构建一组 UsageSpec 对象,以及从提供的 UsageSpec 对象集合设置 nova.objects.ComputeNode 对象上的现有字段。
备选方案¶
无。
数据模型影响¶
无。此蓝图添加的对象未存储在数据库中。这些对象是用于替换当前用于表示资源数量的非结构化嵌套字典的。
REST API 影响¶
无。
安全影响¶
无。
通知影响¶
无。
其他最终用户影响¶
无。
性能影响¶
无。
其他部署者影响¶
当此蓝图完成时,可扩展资源跟踪器将被删除。
开发人员影响¶
一旦此蓝图完成,处理请求规范构造的代码将更加结构化,并且资源跟踪器中围绕 ERT、PCI 跟踪器和 NUMA 拓扑怪癖的大量意大利面条代码将消失。
实现¶
将存在一个通用的使用规范类接口,如下所示
class UsageSpec(object):
"""Represents the used amount of a particular type of resource."""
def update(self, amount_spec):
"""Update the used amount of resources by the supplied amount.
:param amount_spec: `AmountSpec` to modify the usage with.
"""
raise NotImplementedError
def has_room_for(self, amount_spec):
"""Determine if there is room to fit the supplied amount of resources.
:param amount_spec: `AmountSpec` to determine if there is room for.
:returns True if the supplied requested amount of resources is able
to be consumed on the node, False otherwise.
If the supplied `amount_spec` is negative, returns False .
"""
raise NotImplementedError
请注意,UsageSpec 类的每个具体特化都必须能够处理其处理的资源类型的超额承诺比率。
考虑到所有实例请求的资源都应该能够以相同的方式与所有计算节点的资源使用记录进行比较,使用如下代码
for resource_type, amount in request_spec.resources.items():
if compute_node.usages[resource_type].has_room_for(amount):
# do something... perhaps claim resources on the compute
# node, which might eventually call:
compute_node.usages[resource_type].update(amount)
conductor RPC API 应该添加一个新的 update_compute_node_resource_usages() 方法,该方法将新的 nova.objects.ResourceUsageCollection 对象传递给 conductor,conductor 将分解并更新数据库中的计算节点记录。
负责人¶
- 主要负责人
jaypipes
工作项¶
添加用于请求和使用量表示的类
添加用于资源类型表示的类
添加一个表示请求资源数量的集合的类
添加一个表示资源使用情况的集合的类
添加翻译方法到 nova.objects.ComputeNode 以构建和分解一组 UsageSpec 对象,并附带单元测试
将资源跟踪器转换为使用使用量对象,而不是在字典中用于非 PCI、非 ERT、非 NUMA 资源的键/值对中的自由/总计/已用数量的三元组。
删除可扩展资源跟踪器代码。
将资源跟踪器转换为使用使用量对象,而不是 ‘numa_topology’ 键和 nova.virt.hardware.VirtNUMATopology 对象在 old_resources 字典中。
将资源跟踪器转换为使用使用量对象,而不是 ‘pci_devices’ 和 ‘pci_passthrough_devices’ 键以及资源跟踪器的 pci_tracker 属性中的 nova.pci.pci_stats.PciDeviceStats 对象。
将 virt 驱动程序的 get_available_resources 方法转换为返回资源对象字典。
添加一个新的 conductor RPC API(和调度器客户端)方法,称为 update_compute_node_resource_usages(),该方法提供新的 nova.objects.ResourceUsageCollection 对象表示形式的资源使用情况。
弃用旧的 update_resource_stats() conductor RPC API 方法。
将调度器的 HostStateManager 转换为利用新的 ComputeNode.get_resource_usages() 方法和 ComputeNode.update_from_resource_usages 方法。
添加开发人员参考文档,说明如何建模资源。
依赖项¶
无。
测试¶
将添加用于对象的新的单元测试。资源跟踪器的现有单元测试将在将资源跟踪器转换为使用新的资源对象模型而不是其当前自由形式字典的补丁集中进行修改。
文档影响¶
目前没有开发人员参考文档解释 Nova 中如何跟踪不同的资源。解释新的资源类型和数量类的开发人员参考材料将作为此蓝图的一部分提供。
参考资料¶
此蓝图是整体工作的一部分,旨在清理、版本化和稳定 nova-api、nova-scheduler、nova-conductor 和 nova-compute 守护程序之间涉及调度和资源决策的接口。
detach-service-from-computenode
resource-objects <– 此蓝图
request-spec-object
sched-select-destinations-use-request-spec-object
placement-spec-object
condition-objects
sched-placement-spec-use-resource-objects
sched-placement-spec-use-condition-objects
sched-get-placement-claims