Gnocchi – Ceilometer API v3

https://blueprints.launchpad.net/ceilometer/+spec/gnocchi

问题描述

从 Ceilometer 项目一开始,其一个主要目标就是存储收集到的时序数据。在项目早期阶段,对于这些时序数据如何处理、操作和查询还不是很明确,因此 Ceilometer 使用的数据模型非常灵活。这最终变得非常强大和方便,但由此带来的性能却非常糟糕,以至于在没有导致数据存储后端崩溃的情况下,存储数周的大量指标非常困难。

拥有如此灵活的数据模型和查询系统很好,但最终用户会一遍又一遍地执行相同的请求,并且需要解决的使用场景是该数据模型的一个子集。另一方面,当前数据模型无法解决某些查询和使用场景,要么是因为它们难以表达,要么是因为运行速度太慢。

最近,在香港 Icehouse 设计峰会上,开发人员和用户表现出对 Ceilometer 执行指标数据聚合的兴趣,以便以更持久的方式保留数据。由于缺乏人力,Icehouse 周期内没有进行相关工作,即使当时核心团队已经验证了该想法和动机。

考虑到 Ceilometer 生成和需要存储的数据量和指标数量,需要一种新的策略和对问题的重新思考,因此 Gnocchi 就是对此的一次尝试。

提议的变更

如今,Ceilometer 试图实现两件事

  • 存储指标,即给定实体的一系列(时间戳,值),该实体可以是数据中心中的温度,也可以是 VM 的 CPU 使用率。

  • 存储事件,即 OpenStack 安装中发生的事情列表:已收到 API 请求,已启动 VM,已上传镜像,服务器从屋顶上掉下来,任何事情。

这两件事对于 Ceilometer 试图实现的所有使用场景都非常有用。指标对于监控、性能分析和告警很有用,而事件对于审计、计费、调试等很有用。

然而,虽然 Ceilometer 的事件收集功能相当稳定且良好(但仍需要改进),但指标部分却存在严重的设计和性能问题。

与 Ceilometer 生成的每个指标关联的所谓自由格式元数据是我们遇到的最具问题性的设计。它存储了大量难以高效查询的冗余信息。另一方面,像 RRD 这样的系统已经存在一段时间了,可以存储大量的(聚合)指标而不会出现太多问题。与这些指标相关的元数据是另一个问题。

因此,我们剩下两个不同的问题需要解决:存储指标和存储有关资源的信息(所谓的元数据)。

替代方案

数据模型影响

这将带来全新的数据模型,其中指标和资源被拆分,资源元数据的快照不再与单个样本数据点绑定。

可以通过编写和运行负责执行此操作的脚本,将数据从当前的 Ceilometer 数据库迁移到这个新系统。未来的蓝图应该涵盖这个主题。

REST API 影响

数据模型的变化很大,因此也需要一个新的 API。它应该是 Ceilometer API 的版本 3,或者如果项目保持独立,则应该是版本 1。

提议的 API 是

  • POST /v1/entity:

    -> {"archives": [{"lifespan": 3600, "points": 1000},
                     {"lifespan": "1 year", "interval": 60},
                     {"points": 1000, "interval": 60}]}
    <- 201 Created
       Location: /v1/entity/<uuid>
    
    Create an entity storing:
      - 1000 points over an hour
      - a point every minute over a year
      - 1000 points with a point every minute.
    The uuid of the entity is returned.
    
  • POST /v1/entity/<uuid>/measures:

    -> [{"timestamp": "2013-01-01 12:12:23", "value": 42.0},
        {"timestamp": "2013-01-01 12:12:24", "value": 43.1}]
    <- 204 No Content
    
    Store measures for an entity
    
  • GET /v1/entity/<uuid>/measures:

    <- [{"timestamp": "2013-01-01 12:12:23", "value": 42.0},
        {"timestamp": "2013-01-01 12:12:24", "value": 43.1}]
    
    Returns a list of measures from this entity.
    Time span can be specified with a query string.
    
  • DELETE /v1/entity/<uuid>:

    <- 204 No Content
    
    Delete an entity.
    
  • POST /v1/resource/<resource type>:

    -> { "id": <uuid>,
         "started_at": "2013-01-01 12:23:12",
         "project_id": "foobar",
         "entities": { "cpu.util": <entity uuid> },
         "user_id": "foobaz"}
    <- { "id": <uuid>,
         "started_at": "2013-01-01 12:23:12",
         "project_id": "foobar",
         "entities": { "cpu.util": <entity uuid> },
         "type": <resource type>,
         "user_id": "foobaz"}
    
    Create a resource. The UUID has to be provided by the caller (and is
    expected to match the native UUID of the underlying resource) and
    various attributes can also be provided.
    
    Entities can be specified with their UUID, or with creation parameters:
    
    -> { "id": <uuid>,
         "started_at": "2013-01-01 12:23:12",
         "project_id": "foobar",
         "entities": { "cpu.util": {"archives": [{"lifespan": 3600, "points": 1000}]} },
         "user_id": "foobaz"}
    <- { "id": <uuid>,
         "started_at": "2013-01-01 12:23:12",
         "project_id": "foobar",
         "entities": { "cpu.util": <entity uuid> },
         "user_id": "foobaz"}
    
  • GET /v1/resource/<resource type>:

    <- [{ "id": <uuid>,
          "started_at": "2013-01-01 12:23:12",
          "project_id": "foobar",
          "type": "generic",
          "entities": { "cpu.util": <entity uuid> },
          "user_id": "foobaz"}]
    
    Return list of resources.
    
  • GET /v1/resource/<resource type>/<uuid>:

    <- { "id": <uuid>,
         "started_at": "2013-01-01 12:23:12",
         "project_id": "foobar",
         "type": "generic",
         "entities": { "cpu.util": <entity uuid> },
         "user_id": "foobaz"}
    
    Return details about a resource.
    
  • DELETE /v1/resource/<resource type>/<uuid>:

    <- 204 No Content
    
    Delete a resource.
    
  • PATCH /v1/resource/<resource type>/<uuid>:

    -> {"started_at": "2013-01-01 12:23:13"}
    <- { "id": <uuid>,
         "started_at": "2013-01-01 12:23:13",
         "type": "generic",
         "entities": { "cpu.util": <entity uuid> },
         "project_id": "foobar",
         "user_id": "foobaz"}
    
    Change value for a mutable attribute. The list of attributes that is
    mutable depends on the resource type, but all resource type can change:
    * ended_at
    * entities
    

所有资源都继承自 generic 资源类型,因此可以通过使用此资源类型对其进行部分操作。否则,提供具有更多属性的资源类型,例如 instance,以创建更完整的资源。

所有资源类型都内置在 Gnocchi 中,以便提高性能。如果需要索引的资源类型 Ceilometer 不知道,可以使用 generic 资源类型,并根据用户自行决定在另一个系统中管理资源的属性。

Gnocchi 知道的资源类型将是 OpenStack 提供的资源类型,例如 instanceportnetworkvolume 等。

安全影响

通常的 Keystone token-based authN 和 RBAC-based authZ。

没有提出访问实体的安全机制。由于实体的 UUID 是动态且随机分配的,因此必须知道该实体的 UUID 才能访问它。因此,它可以被视为一个秘密。

可以根据存储的 user_idproject_id 字段过滤对资源的访问,这些字段是强制属性。这与 Ceilometer API v2 中当前使用的机制相同。

Pipeline 影响

发布机制需要适应这个新模型,因为在对其进行计量之前需要创建资源。另一个蓝图应该涵盖这个主题。

其他最终用户影响

ceilometerclient 需要扩展以同时支持旧的和新的 API。

性能/可扩展性影响

这个新系统的可扩展性和性能应该比旧系统好得多。

对这个系统进行实际基准测试也会很有趣。

其他部署影响

开发者影响

Ceilometer 的 API v2 可能会被冻结,并且在此阶段不应进行任何进一步的改进。

实现

负责人

主要负责人
  • jdanjou

其他贡献者
  • sileht

  • dbelova

持续维护者
  • jdanjou

工作项

  • 构建 Gnocchi 服务和 API

  • 根据需要调整 Ceilometer 数据检索和发布,以适应新的数据存储和 API

  • 让它们协同工作

未来生命周期

这将是 Ceilometer 的核心组件,因此每个人都会关心它,包括我。

依赖项

  • 规范的存储驱动程序实现需要 Pandas 和 Swift。

  • 也可能会随着时间的推移提供具有不同依赖项的其他统计/存储驱动程序。

测试

提供了单元测试。

应该添加 Tempest 测试以涵盖新的 API。一种可能性是运行启用 v3 机制的 v2 测试的变体。

文档影响

我们必须记录新的 API。目前没有自动生成 API 文档的机制,但这样做应该可行且有趣。

参考资料