中央代理的水平扩展和工作负载分区¶
https://blueprints.launchpad.net/ceilometer/+spec/central-agent-partitioning
中央代理的任务是轮询资源以获取信息,将这些信息转换为样本,并将样本传递给收集代理。
本规范提出了一种多个中央代理之间的协调实现,它们可以动态地在它们之间分配工作负载,从而提供可扩展性和高可用性。
问题描述¶
目前,每个中央代理检索一组资源并轮询所有这些资源。 如果我们运行多个中央代理,它们都会轮询相同的资源集,这阻止了我们进行水平扩展。
在每个轮询间隔的开始,每个轮询器都会从其发现插件(在管道中配置或默认插件)中检索要轮询的资源列表。 这使得发现过程成为实现协调和分区逻辑的好地方,而轮询器本身可以对正在发生的事情一无所知。
提议的变更¶
基本思想是使用tooz [1] 库进行组管理和哈希,以将资源分配给活动中央代理。
在不同中央代理上发现相同资源集的发现插件将加入tooz 中的同一个组。
对于每个轮询间隔,在独立发现需要由一个或另一个中央代理轮询的全局资源集之后,它们将获得所有组成员的列表(其他中央代理中的发现插件具有相同的资源集)。
它们将使用哈希来确定分配给发现的中央代理的资源集。
确定我们负责的资源
我们有一组资源,并从tooz 获取一个活动代理列表。 然后,我们按如下方式获取分配给我们的资源
our_key = sorted(agents).index(our_agent_uuid)
our_resources = []
for resource in resources:
key = hash(resource) mod len(agents)
if key == our_key:
our_resources.append(resource)
# or more pythonic
our_resources = [r for r in resources if hash(r) mod len(agents) == our_key]
本质上,我们将资源哈希到 <中央代理数量> 个桶中,并且只轮询落入我们桶中的资源。 良好的哈希函数 [3] 确保资源均匀地分配给活动中央代理。
分组
轮询器的发现插件(无论是计算发现、硬件发现等)都提供了其资源所属的范围。
例如,如果发现插件不限制为资源的子集,就像大多数发现插件一样,那么它应该简单地加入未约束发现插件的全局组。
另一方面,如果发现插件可以发现的资源受到约束,就像在计算发现的情况下一样,那么组名应该反映它们的范围。 例如,这将是 ‘compute-<hostname>-discovery’。 这样,只有轮询相同主机的轮询器才会共享它们的工作负载。
当我们启动另一个代理(或停止现有代理)时会发生什么?
tooz 允许我们注册一个回调,当组成员加入或离开组时会调用该回调。 它使用心跳机制跟踪成员的活跃度。
当成员加入或离开组时,会发生以下情况
已经运行的代理 如果它们当前正在轮询过程中,它们将完成完整的轮询周期,然后才重新平衡它们的哈希桶。
我们刚刚启动的代理 首先加入一个组,然后等待一个轮询间隔,以确保所有其他代理都已更新它们的哈希桶,然后开始轮询。
停止/崩溃期间的代理 停止的代理尚未轮询的资源将不会在本周期内轮询,但将在下一个周期内轮询。
通用化实现以供重用
协调将“事物”(资源、警报等)分配给代理的需求并非中央代理独有。 目前,警报评估器也可以利用它来运行多个警报评估器,每个评估器评估其份额的警报。
此功能可以捕获在PartitionCoordinator 类中,代理可以使用它,例如
partition_coordinator = PartitionCoordinator(group='alarm')
partition_coordinator.start()
every evaluation_interval:
all_alarms = get_all_alarms()
my_alarms = partition_coordinator.get_my_subset(all_alarms)
for a in my_alarms:
evaluate(a)
注意
警报分区协调的实际变更将通过单独的蓝图进行跟踪。
或者在中央代理的情况下
partition_coordinator = PartitionCoordinator(group='central_agent')
partition_coordinator.start()
every polling_interval:
all_resources = discover_resources()
my_resource = partition_coordinator.get_my_subset(all_resources)
for r in my_resources:
poll(r)
替代方案¶
Fabio Gianetti 的方法 [2]。
Fabio 的方法使用数据库中的源<->代理分配来确定要轮询的内容,并使用心跳以及其他代理侦听该心跳来检测故障。
相比之下,本提案使用tooz 进行故障检测(也通过心跳)。 此外,资源分配更加动态,因为资源在任何时候都均匀地分配给代理。 它也更轻量级,因为我们不需要在数据库中保留显式的资源<->代理映射,而是使用哈希。
锁定
另一种方法是使用tooz 提供的分布式锁定。 在轮询器轮询资源之前,它需要获取其锁。 轮询器争夺锁,谁获得锁,谁轮询资源。
这种方法的缺点是分布式锁定的开销。 获取分布式锁会产生成本(时间、网络流量)。 当使用分布式锁进行资源争用时,此成本会产生每个资源的成本。 而在使用组管理的方法中,协调成本仅在成员加入/离开组时产生,其频率与资源数量相比可以忽略不计。
数据模型影响¶
无
REST API 影响¶
无
安全影响¶
无
Pipeline 影响¶
无
其他最终用户影响¶
无
性能/可扩展性影响¶
正面
其他部署影响¶
如果部署者想要使用多个中央代理,他们需要部署一个 tooz 后端(ZooKeeper、memcached,可能很快就是一个 AMQP 代理)
开发者影响¶
无
实现¶
负责人¶
- 主要负责人
nejc-saje
- 其他贡献者
chdent
- 持续维护者
ceilometer 团队
工作项¶
未来生命周期¶
依赖项¶
tooz
tooz 的后端之一(ZooKeeper、memcached,可能只是 oslo.messaging)
测试¶
实现应该使用单元测试进行测试。
文档影响¶
操作手册应该解释运行多个中央代理的过程和属性。