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 记录
- 配置文件
并且将来很容易引入对更多选项的支持。
由于在配置文件中列出集群节点的能力并不新鲜,所以让我们关注新功能。
节点注册和注销
一些机制使用数据存储来跟踪节点列表。新加入的集群成员会更新数据存储以表明其存在。etcd 和 Consul 是两个选项,它们通过随 RabbitMQ 一起提供的插件支持。
对于其他机制,集群成员资格是在带外管理的(由 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 角色和权限
如果将 IAM 角色 分配给运行 RabbitMQ 节点的 EC2 实例,则必须使用策略来允许这些实例使用 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 邮件列表 上提供反馈。