万用表算术变换器

https://blueprints.launchpad.net/ceilometer/+spec/arithmetic-transformer

在执行转换时,增加考虑多个仪表的​​能力。内存利用率就是其中一个例子,其中

memory_util = 100 * memory.usage / memory

问题描述

管道转换器目前只对同一仪表的​​一个或多个值执行计算。某些计算需要考虑多个仪表,例如内存利用率的情况,其中

memory_util = 100 * memory.usage / memory

提议的变更

我们建议实现一种灵活的转换器,能够对多个仪表和/或其元数据执行算术运算。为了防止生成的仪表出现不规则的节拍,计算仅限于具有相同间隔的仪表。

新转换器的配置将是

- name: "arithmetic"
  parameters:
    target:
      name: "memory_util"
      unit: "%"
      type: "gauge"
      expr: "100 * $(memory.usage) / $(memory)"

为了演示元数据的使用,这里是一个愚蠢的度量的实现,它显示了每个核心的平均CPU时间

- name: "arithmetic"
  parameters:
    target:
      name: "avg_cpu_per_core"
      unit: "ns"
      type: "cumulative"
      expr: "$(cpu) / ($(cpu).resource_metadata.cpu_number or 1)"

表达式求值将优雅地处理NaN和异常。在这种情况下,它不会创建新的样本,而只会记录警告。

解析仪表名称

由于仪表名称不一定是有效的Python标识符,我们必须对其进行转义。表达式字符串只在管道初始化时解析,因此不会造成任何性能开销。

转义方式如下

$(cpu) -> cpu
$(cpu_util) -> cpu_util
$(cpu.util) -> _cpu_util_ESCAPED (in order to avoid clashes with legal
  meter names)
$(class) -> _class_ESCAPED (avoid clash with Python keywords)

此外,如果仪表名称后面没有点号('。'),我们默认为volume参数。

因此,表达式

$(memory.usage) / $(memory)

将被翻译为

_memory_usage_ESCAPED.volume / memory.volume

并使用以下命名空间对象进行求值

Namespace({
    "_memory_usage_ESCAPED": { ... memory.usage sample dict ... }
    "memory": { ... memory sample dict ... }
})

收集值

并非所有需要的样本都会同时到达。这里有多种方法。

1. [选定的方法] 我们可以缓存必要仪表的​​样本(以资源ID为键)当它们到达时。当管道被刷新时,我们可以检查我们是否有所需的样本并进行转换。

  • 优点:无需为缓存的样本指定TTL

  • 缺点:算术运算仅限于原始样本

    来自同一轮询任务(=>相同间隔)

2. 我们像上面一样缓存样本,但一旦我们有所需的样本(在计算表达式中使用的样本)就进行转换。转换后我们清除缓存。用户提供样本的最大年龄(TTL),以便将其考虑用于计算。例如,我们得到一个“memory.usage”样本,并且我们缓存中已经有一个“memory”样本,其时间小于TTL=10秒。我们可以执行计算。

  • 优点:使用的仪表不限于同一轮询任务

  • 优点:所有选项中灵活性最大

  • 缺点:需要指定用于计算的样本的最大年龄

  • 缺点:可能会产生不规则节拍的样本,如在

    涉及60秒节拍的仪表A和45秒节拍的仪表B的计算

  1. 类似于#2,但仅限于来自相同间隔源的仪表。

  • 优点:生成的样本不能有不规则节拍

  • 优点:由于所有涉及的仪表都具有相同的间隔,我们可以有一个

    静态TTL,无需用户配置。如果我们知道所有样本大约同时到达(因为它们在相同的间隔内,因此在相同的轮询任务中),我们只有很短的一段时间内样本是有效的(例如,5秒)。

  • 缺点:下一个问题是如何设置TTL?对于600秒的节拍,

    5秒是可以的,但对于10秒的节拍可能不行。

  • 缺点:如何从技术上强制执行此限制?

4. 与#2相同,但提供一个配置选项,在计算后不清除缓存。

  • 优点:支持诸如“我们每10秒获取‘memory.usage’和‘memory’”之类的用例

    每600秒一次。如果我们不清除缓存,我们可以每10秒使用缓存的‘memory’值计算‘memory_util’。

  • 缺点:额外的配置选项可能会让用户感到困惑。这个

    好处可能超过此缺点。

替代方案

  • 要求用户将仪表分配给“变量”,例如

    - name: "arithmetic"
      parameters:
        target:
          name: "memory_util"
          unit: "%"
          type: "gauge"
          a:    "memory.usage"
          b:    "memory"
          expr: "100 * a / b"
    

    不必要,因为解析只在管道初始化时完成,因此不会在处理中造成任何开销。

  • 以某种方式更改管道,使其仅在拥有所有必需样本时才将样本发送到转换器

    我们的方法更简单,因为其他转换器已经使用缓存,所以将此功能推送到管道是没有意义的。

数据模型影响

REST API 影响

安全影响

Pipeline 影响

它影响pipeline.yaml的格式,如“实现”部分所述。

其他最终用户影响

性能/可扩展性影响

无。表达式解析仅在管道初始化时执行。关于缓存,其他转换器已在间隔之间缓存值。唯一的区别是我们会存储多个仪表的值。

其他部署影响

开发者影响

实现

负责人

主要负责人

nejc-saje

持续维护者

nejc-saje

工作项

  • 创建新的转换器

  • 为新的转换器编写单元测试

未来生命周期

依赖项

测试

根据Tempest测试的当前功能,我认为单元测试应该足够了。

文档影响

应为新型转换器添加文档。

文档必须明确指出,表达式中包含的仪表必须具有相同的节拍。如果表达式包含具有不同节拍或不存在的仪表,用户应预期每次不成功的计算都会记录警告。

参考资料