将模型资源表示为对象

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’ 内容;一个 ‘numa_topology’ blob,它存储 nova.virt.hardware 中对象的一个 JSON 序列化表示;以及一个包含完全非结构化和未记录的键/值对的 ‘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, 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, amounts):
    for resource, amount in amounts.items():
        self.inventories[resource].consume(amount)

用例

Nova 贡献者希望扩展调度器的功能,并打算将调度器分解为 Gantt 项目。为了有效地做到这一点,资源跟踪器和调度器周围的内部接口必须清理,以使用结构化对象。

项目优先级

此蓝图是 Liberty 版本中定义的 scheduler 重构工作的一部分。

提议的变更

建模请求的和使用的资源量是必须首先完成的基础构建块,然后才能对调度器或资源跟踪器接口进行任何进一步的重构或清理。

此蓝图包含添加一组类来表示

  • 不同数据类型的数量,例如 IntegerAmountNUMATopologyAmount

  • 不同数据类型的库存,描述实际容量、已使用的数量以及任何超额承诺比率。例如 IntegerInventoryNUMAInventory

  • 不同类型的资源,例如使用 IntegerAmountIntegerInventory 的 RAM,或使用 NUMAAmountNUMAInventory 的 NUMA 拓扑。

这些数量、库存和资源类将是 nova.objects 对象类,并将使 Nova 能够以版本化的方式发展其跟踪资源和暴露资源消耗的方式。

可扩展资源跟踪器 (ERT) 的目标是建立一个框架,允许添加新的资源类型并允许以不同的方式计算这些资源。虽然此蓝图确实删除了 ERT,但由于这些资源、数量和库存类被添加为 nova.object 对象,我们将获得 ERT 想要实现的灵活性,但同时具有 nova 对象系统的稳定性。

资源跟踪器代码随后将被转换为在表示计算节点上所有资源的库存时使用上述类。目前,这些将通过简单地调用 compute_node.save() 来持久化。

不建议对 compute_nodes 表的数据库模式或 nova.objects.ComputeNode 中的字段进行任何更改,但是我们添加了翻译方法到 nova.objects.ComputeNode,这些方法能够生成一个由 Inventory 对象(以 Resource 为键)组成的字典,并从类似的结构更新计算节点。

备选方案

无。

数据模型影响

无。此蓝图添加的对象未存储在数据库中。这些对象是用于替换当前用于表示资源数量的非结构化嵌套字典的。

REST API 影响

无。

安全影响

无。

通知影响

无。

其他最终用户影响

无。

性能影响

无。

其他部署者影响

在完成此蓝图后,将删除 ERT。

开发人员影响

完成此蓝图后,处理请求规范的代码将更加结构化,资源跟踪器中围绕 ERT、PCI 跟踪器和 NUMA 拓扑怪癖的大量意大利面条代码将消失。

实现

将提供以下抽象类

class Amount(object):
   """Represents a quantity of a resource."""

   def __eq__(self, other):
       raise NotImplementedError

   def __ne__(self, other):
       return not self == other

   def __hash__(self, other):
       raise NotImplementedError

   def __neg__(self, other):
       raise NotImplementedError


class Inventory(object):
   """Describes the capacity, available and used amounts for a resource."""

   def consume(self, amount):
       """Update (i.e. add) the given amount to the used amount in this
       inventory. If the amount is negative, more resources will be available
       afterwards than were before.

       :param amount 'Amount' to add to the usage.
       :raises ValueError if amount is the wrong type for this inventory.
       :raises CapacityException if accommodating this request would cause
               either available or used resources to go negative.
       """
       raise NotImplementedError

   def can_provide(self, amount):
       """Determine if this inventory can provide the given amount of
       resources. An overcommit ratio may be applied.

       :param amount 'Amount' to determine if there is room for.
       :raises ValueError if amount is the wrong type for this inventory or is
               negative.
       :returns True if the requested amount of resources may be consumed,
                False otherwise.
       """
       raise NotImplementedError


class Resource(object):
   """Describes a particular kind of resource."""

   @classmethod
   def make_amount(cls, *args, **kwargs):
       """Makes an Amount of the type appropriate to this resource."""
       raise NotImplementedError

   @classmethod
   def make_inventory(cls, *args, **kwargs):
       """Makes an Inventory of the type appropriate to this resource."""
       raise NotImplementedError

库存类的每个具体特化都必须能够处理其处理的资源类型的超额承诺比率。

考虑到所有实例请求的资源都应该能够以相同的方式与计算节点上的所有资源库存进行比较,使用如下代码

for resource, amount in request_spec.resources.items():
   if compute_node.inventories[resource].can_provide(amount):
       # do something... perhaps claim resources on the compute
       # node, which might eventually call:
       compute_node.inventories[resource].consume(amount)

负责人

主要负责人

jaypipes

其他贡献者

lxsli

工作项

  • 添加用于数量和库存表示的类。

  • 添加用于资源表示的类。

  • 添加翻译方法(get_inventoriesupdate_inventories)到 nova.objects.ComputeNode,以返回或更新由 Resource, Inventory 对象组成的字典,并进行单元测试。

  • 将资源跟踪器转换为使用库存,而不是在字典中以键/值对的形式使用自由/总计/已用数量的三元组,用于非 PCI、非 ERT、非 NUMA 资源。

  • 删除可扩展资源跟踪器代码。

  • 将资源跟踪器转换为使用库存,而不是 ‘numa_topology’ 键和 nova.virt.hardware.VirtNUMATopology 对象在 old_resources 字典中。

  • 将资源跟踪器转换为使用库存,而不是 ‘pci_devices’ 和 ‘pci_passthrough_devices’ 键以及 nova.pci.pci_stats.PciDeviceStats 对象在资源跟踪器的 pci_tracker 属性中。

  • 将 virt 驱动程序的 get_available_resources 方法转换为返回资源对象字典。

  • 弃用旧的 update_resource_stats() 调度器 RPC API 方法。

  • 将调度器的 HostStateManager 转换为利用新的 ComputeNode.get_inventories()ComputeNode.update_inventories 方法。

  • 添加开发人员参考文档,说明如何建模资源。

依赖项

无。

测试

将添加用于对象的新的单元测试。资源跟踪器的现有单元测试将在将资源跟踪器转换为使用新的资源对象模型而不是其当前自由形式字典的补丁集中进行修改。

文档影响

目前没有开发人员参考文档解释 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