RabbitMQ 3.7 中的节点发现子系统
在本篇博文中,我们将深入探讨在 RabbitMQ 3.7.0 中引入的新子系统。
为什么我们需要节点发现?
像 RabbitMQ 这样的开源数据服务的用户对运维自动化有着越来越高的期望。这包括所谓的“Day 1”运维:初始集群的配置。
当 RabbitMQ 集群首次形成时,新启动的节点需要一种相互发现的方式。在 3.6.x 及更早版本中,有两种方式可以做到这一点:
- CLI 工具
- 配置文件中的节点列表
前者选项被一些配置工具使用,但通常不太适合自动化。后者更方便,但有其自身的局限性:节点集是固定的,更改它需要重新部署配置文件和重启节点。
更好的方法
还有第三种选择,它在社区中已经存在了几年: rabbitmq-autocluster,这是 Gavin Roy 最初开发的一个插件。该插件会修改 RabbitMQ 的启动过程,并注入一个节点发现步骤。在这种情况下,节点列表不必来自配置文件:它可以从 AWS 自动伸缩组或外部工具(如 etcd)中检索。
rabbitmq-autocluster 的作者认为,没有一种“真正”的节点发现方法,并且不同的方法适用于不同的部署场景。因此,他们引入了一个可插拔的接口。这个可插拔接口的一个具体实现称为节点发现机制。考虑到近年来平台和部署自动化栈的爆炸式增长,这个决定被证明是明智的。
对于 RabbitMQ 3.7.0,我们采纳了 rabbitmq-autocluster 并将其主要思想整合到核心中,并根据我们支持生产环境 RabbitMQ 部署的经验和社区反馈进行了一些修改。
其结果是引入了一个新的 节点发现子系统。
它是如何工作的?
当一个节点启动并检测到它没有先前初始化的数据库时,它会检查是否配置了节点发现机制。如果是,它将执行发现并尝试按顺序联系每个已发现的节点。最后,它将尝试加入第一个可达节点的集群。
某些机制假定所有集群成员都预先已知(例如,列在配置文件中),而其他机制是动态的(节点可以进出)。
RabbitMQ 3.7 附带了多种机制:
- AWS(EC2 实例标签或自动伸缩组)
- Kubernetes
- etcd
- Consul
- 预配置的 DNS 记录
- 配置文件
并且未来很容易引入对更多选项的支持。
由于在配置文件中列出集群节点的功能并非新功能,因此我们重点关注新功能。
节点注册和注销
一些机制使用数据存储来跟踪节点列表。新加入的集群成员会更新数据存储以表明其存在。通过 RabbitMQ 插件支持的选项包括 etcd 和 Consul。
对于其他机制,集群成员是通过带外方式管理的(由 RabbitMQ 节点无法控制的机制)。例如,AWS 机制 使用 EC2 实例过滤或自动伸缩组成员身份,这两者都由 AWS 管理和更新。
使用预配置的集合
理论就到此为止,让我们来看看使用在 3.7 中与节点发现一起引入的新配置格式 来配置节点列表以进行节点发现需要什么。
首先,我们必须告诉 RabbitMQ 使用经典配置机制进行节点发现。这通过 cluster_formation.peer_discovery_backend 键来实现。然后使用 cluster_formation.classic_config.nodes 列出一个或多个节点,这是一个集合。
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = rabbit@hostname1.eng.example.local
cluster_formation.classic_config.nodes.2 = rabbit@hostname2.eng.example.local
就这样。
这种发现方法可能是最容易上手的,但有一个明显的问题:节点列表是静态的。
接下来,让我们看看一个使用动态节点列表的机制:AWS EC2 实例过滤。
使用 AWS 实例过滤
就像之前的示例一样,我们必须告诉节点使用 AWS 进行节点发现。
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_aws
有两种方法可以使用 AWS 机制,但后端名称(模块)对两者来说都是相同的。
要使用实例过滤,插件需要配置 AWS 区域以及一对凭证。敏感的配置文件值可以加密。
这是一个同时完成这两项工作的配置文件示例:
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_aws
cluster_formation.aws.region = us-east-1
cluster_formation.aws.access_key_id = ANIDEXAMPLE
cluster_formation.aws.secret_key = WjalrxuTnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY
现在节点拥有足够的信息来尝试查询 EC2 实例元数据服务。
最后,操作员需要提供一组要过滤的标签。标签是键值对。这意味着可以过滤多个标签,例如,rabbitmq 和集群名称或环境类型(例如,development、test 或 production)。
这是一个使用 3 个标签(region、service 和 environment)的完整配置示例:
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_aws
cluster_formation.aws.region = us-east-1
cluster_formation.aws.access_key_id = ANIDEXAMPLE
cluster_formation.aws.secret_key = WjalrxuTnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY
cluster_formation.aws.instance_tags.region = us-east-1
cluster_formation.aws.instance_tags.service = rabbitmq
cluster_formation.aws.instance_tags.environment = staging
有了这个示例,我们就准备好了。唯一需要讨论的是如何处理集群首次形成时发生的自然竞争条件,此时节点列表可能只返回一个空集。这将在下面的单独部分中介绍。
IAM 角色和权限
如果为运行 RabbitMQ 节点的 EC2 实例分配了 IAM 角色,则必须使用一个策略来允许这些实例使用 EC2 实例元数据服务。这是一个此类策略的示例:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingInstances",
"ec2:DescribeInstances"
],
"Resource": [
"*"
]
}
]
}
如果没有此策略,AWS 节点发现插件将无法列出实例,发现将失败。当发现失败时,节点将认为这是一个致命错误并终止。
节点名称
默认情况下,使用 AWS 节点发现的节点名称将使用私有主机名计算。也可以切换到使用私有 IP 地址:
cluster_formation.aws.use_private_ip = true
节点发现的鸡生蛋问题
考虑一个同时配置整个集群并且所有节点都并行启动的部署。例如,它们可能刚刚由 BOSH 或 AWS 集群配置模板创建。在这种情况下,节点注册之间存在一个自然的竞争条件,并且可能不止一个节点成为“第一个注册者”(它发现没有现有节点,因此作为独立节点启动)。
不同的节点发现后端使用不同的方法来最小化这种情况的发生概率。一些后端会与其数据服务(etcd、Consul)获取一个锁,并在注册后释放它,如果获取锁失败则重试。
其他后端使用一种称为随机启动延迟的技术。通过随机启动延迟,节点将延迟其启动,延迟时间为一个随机选取的数值(默认为 5 到 60 秒)。虽然这种策略乍一看可能显得简单,但实际上在最大延迟间隔足够高的情况下效果很好。它也被用于某些分布式系统算法中的领导者选举,例如 Raft。
一些后端(配置文件、DNS)依赖于预先配置的节点集,并且不受此问题的影响,因为当节点尝试加入其对等节点时,它会尝试重试一段时间。
节点发现不做什么
引入节点发现是为了解决一系列狭窄的问题。它不会改变 RabbitMQ 集群在形成后的运行方式。即使某些机制引入了附加功能,某些问题(例如,共享密钥分发和监控)仍应由其他工具解决。
节点发现也由空白(未初始化)节点执行。如果一个节点以前是集群成员,它将在启动时尝试联系其“最后见到的”对等节点一段时间。在这种情况下,将不执行节点发现。这与早期 RabbitMQ 版本在这种场景下的工作方式没有区别。
节点发现故障排除
推理一个自动化集群配置系统,该系统还使用具有外部依赖项(例如 AWS API 或 etcd)的节点发现机制可能很棘手。因此,所有节点发现实现都会在 debug 日志级别记录关键决策,并且大多数会记录所有外部请求。如有疑问,请启用调试日志记录并查看节点日志!
并请记住上面关于何时不应启动节点发现的部分内容。
与 rabbitmq-autocluster 的区别
虽然新的节点发现子系统在许多方面与 rabbitmq-autocluster 相似,但有几个重要的区别对操作员很重要。
使用 rabbitmq-autocluster 时,节点会在加入其对等节点之前重置自身。这在某些环境中是合理的,而在其他环境中则不是。RabbitMQ 核心中的节点发现不会这样做。
rabbitmq-autocluster 除了 RabbitMQ 配置文件外,还允许使用环境变量进行特定于机制的配置。虽然此功能得以保留以简化迁移,但在 3.7.0 中,节点发现子系统应将其视为已弃用。
核心中的节点发现大量使用新的配置文件格式。rabbitmq-autocluster 不支持该格式,因为它现在实际上是一个仅限 3.6.x 的插件。
未来工作
本文介绍的节点发现子系统的大多数主要方面都通过 rabbitmq-autocluster 经历了数年的实战检验。然而,随着越来越多的用户在越来越多的环境中采用此功能,来自更广泛用户和用例的新反馈也在不断积累。
目前一个悬而未决的问题是,无法联系节点发现机制使用的外部服务(例如 AWS API 端点、etcd 或 DNS)是否应立即被视为致命故障,导致节点停止,还是应在一段时间内重试节点发现?欢迎您在 RabbitMQ 邮件列表 上提供反馈。
