万用表算术变换器¶
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的计算
类似于#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测试的当前功能,我认为单元测试应该足够了。
文档影响¶
应为新型转换器添加文档。
文档必须明确指出,表达式中包含的仪表必须具有相同的节拍。如果表达式包含具有不同节拍或不存在的仪表,用户应预期每次不成功的计算都会记录警告。
参考资料¶
无