跳至主内容
版本:4.3

集群形成与节点发现

概述

本指南涵盖了各种面向自动化的集群形成和节点发现功能。有关 RabbitMQ 集群的概述,请参阅集群指南

本指南假设您已大致熟悉 RabbitMQ 集群,并将重点介绍节点发现子系统。例如,它不会涵盖必须开放哪些端口以进行节点间通信、节点如何相互认证等内容。除了发现机制及其配置外,本指南还涵盖了与集群形成过程中功能可用性节点重新加入、节点并行启动时的初始集群形成竞争条件问题,以及某些发现实现所提供的额外健康检查等紧密相关的主题。

本指南还涵盖了节点发现故障排除的基础知识。

什么是节点发现?

为了形成集群,新的(“空白”)节点需要能够发现其对等节点。这可以通过多种机制(后端)来实现。一些机制假设所有集群成员都是预先已知的(例如在配置文件中列出),另一些则是动态的(节点可以随时加入或离开)。

所有节点发现机制都假设新加入的节点能够联系集群中的对等节点并成功进行身份验证。依赖外部服务(如 DNS 或 Consul)或 API(如 AWS 或 Kubernetes)的机制,要求这些服务或 API 在其标准端口上可用且可访问。如果无法访问这些服务,将导致节点无法加入集群。

可用的发现机制

以下机制内置于核心程序中,始终可用:

其他节点发现机制可通过插件获得。以下节点发现插件随受支持的 RabbitMQ 版本一同发布:

上述插件无需安装,但像所有插件一样,它们在使用前必须被启用预配置

对于必须在节点启动时可用的节点发现插件,这意味着它们必须在首次启动节点之前启用。下例使用了 rabbitmq-plugins--offline 模式:

rabbitmq-plugins --offline enable <plugin name>

一个更具体的例子:

rabbitmq-plugins --offline enable rabbitmq_peer_discovery_k8s

如果节点的配置文件中包含未启用的节点发现插件的设置,该节点将无法启动并报告这些设置为未知。

指定节点发现机制

所使用的发现机制在配置文件中指定,各种特定于机制的设置(例如发现服务主机名、凭据等)也是如此。cluster_formation.peer_discovery_backend 是控制使用哪个发现模块(实现)的键。

cluster_formation.peer_discovery_backend = classic_config

# The backend can also be specified using its module name. Note that
# module names do not necessarily match plugin names exactly.
# cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config

该模块必须实现 rabbit_peer_discovery_backend 行为。因此,插件可以引入其自己的发现机制。

节点发现的工作原理

当节点启动并检测到没有先前初始化的数据库时,它会检查是否配置了节点发现机制。如果是这种情况,它将执行发现并尝试按顺序联系每个发现的对等节点。最后,它会尝试加入第一个可到达的对等节点的集群。

根据所使用的后端(机制),节点发现的过程可能涉及联系外部服务,例如 AWS API 端点、Consul 节点或执行 DNS 查询。某些后端要求节点进行注册(告知后端该节点已启动并应作为集群成员计入):例如,Consul 和 etcd 都支持注册。对于其他后端,节点列表是预先配置的(例如配置文件)。这些后端被称为不支持节点注册。

在某些情况下,节点注册是隐式的或由外部服务管理。AWS 自动伸缩组就是一个很好的例子:AWS 会跟踪组成员,因此节点无需(或无法)显式注册。但是,集群成员列表不是预定义的。此类后端通常包含一个空操作注册步骤,并应用下述竞争条件缓解机制之一。

如果配置的后端支持注册,节点在被指示停止时会注销。

可以通过配置选项 cluster_formation.registration.enabled 完全选择退出注册。

cluster_formation.registration.enabled = false

当以这种方式配置时,节点必须手动注册或使用其他机制注册,例如通过像 Nomad 这样的容器编排器。

如果未配置节点发现,或者它反复失败,或者没有对等节点可达,那么一个过去不是集群成员的节点将从头开始初始化,并作为独立节点运行。节点发现的进度和结果将由节点记录

如果一个节点以前是集群成员,它将在一段时间内尝试联系并重新加入其“最后看到”的对等节点。在这种情况下,不会执行任何节点发现。这适用于所有后端。

集群形成与功能可用性

作为一般规则,一个仅部分形成的集群(即只有一部分节点加入了它)必须被客户端视为完全可用

在集群形成之前,各个节点会接受客户端连接。在这种情况下,客户端应准备好某些功能可能不可用的情况。例如,除非集群节点数量达到或超过配置的副本数法定人数,否则仲裁队列将不可用。

功能标志控制的功能也可能在集群形成完成前不可用。

节点重新加入其现有集群

新节点加入集群只是可能的情况之一。另一个常见场景是现有集群成员暂时离开然后重新加入集群。虽然节点发现子系统不会影响本节描述的行为,但了解节点在重启或故障后如何重新加入其集群非常重要。

现有集群成员不会执行节点发现。相反,它们会尝试联系先前已知的对等节点。

如果一个节点以前是集群成员,当它启动时,它将在一段时间内尝试联系其“最后看到”的对等节点。如果对等节点未启动(例如执行完全集群重启或升级时)或无法到达,该节点将重试该操作多次。

默认值分别为 10 次重试和每次尝试 30 秒,总计 5 分钟。在节点启动时间可能很长和/或不均匀的环境中,建议增加重试次数。

如果节点自与集群失去联系后被重置,它将表现得像一个空白节点。请注意,其他集群成员可能仍将其视为集群成员,在这种情况下,双方将产生分歧,节点将无法加入。此类重置的节点必须通过在现有集群成员上执行 rabbitmqctl forget_cluster_node 从集群中移除。

如果节点被操作员明确从集群中移除并重置,它将能够作为新成员加入集群。在这种情况下,它的行为将完全像一个空白节点

在节点名称或主机名更改后重新加入的节点,如果其数据目录路径因此发生更改,可能会以空白节点身份启动。此类节点将无法重新加入集群。当节点处于离线状态时,其对等节点可以被重置或以空白数据目录启动。在这种情况下,正在恢复的节点也将无法重新加入其对等节点,因为内部数据存储集群标识将不再匹配。

考虑以下场景

  1. 形成了一个包含 A、B 和 C 3 个节点的集群
  2. 节点 A 被关闭
  3. 节点 B 被重置
  4. 节点 A 启动
  5. 节点 A 尝试重新加入 B,但 B 的集群标识已更改
  6. 节点 B 因为已被重置而不识别 A 为已知的集群成员

在这种情况下,节点 B 将拒绝 A 的集群尝试,并在日志中显示适当的错误消息

Node 'rabbit@node1.local' thinks it's clustered with node 'rabbit@node2.local', but 'rabbit@node2.local' disagrees

在这种情况下,B 可以再次重置,然后将能够加入 A,或者 A 可以被重置并成功加入 B。

如何配置节点发现

节点发现插件的配置方式与核心服务器和其他插件一样:使用配置文件

cluster_formation.peer_discovery_backend控制将使用哪个节点发现后端的键。每个后端还会有许多特定于它的配置设置。本指南的其余部分将介绍特定于特定机制的可配置设置,并为每个机制提供示例。

配置文件节点发现后端

配置文件节点发现概述

节点发现其集群对等节点的最基本方式是从配置文件中读取节点列表。集群成员集被假定在部署时是已知的。

配置

对等节点使用 cluster_formation.classic_config.nodes 配置设置列出。

cluster_formation.peer_discovery_backend = classic_config

# the backend can also be specified using its module name
# 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

DNS 节点发现后端

重要

此节点发现机制对影响主机名解析的操作系统和 RabbitMQ 配置敏感。

例如,修改本地 hosts 文件的部署工具可能会影响(破坏)此节点发现机制。

DNS 节点发现概述

自 RabbitMQ 3.7.0 起,另一种内置的节点发现机制是基于 DNS 的。它依赖于带有 DNS A(或 AAAA)记录和反向 DNS 查询的预配置主机名(“种子主机名”)来执行节点发现。更具体地说,此机制将执行以下步骤:

  1. 查询种子主机名的 DNS A 记录。
  2. 对于返回的每个 DNS 记录的 IP 地址,执行反向 DNS 查询。
  3. 将当前节点的前缀(例如 rabbit@hostname1.example.local 中的 rabbit)附加到每个主机名并返回结果。

例如,假设种子主机名为 discovery.eng.example.local。它有 2 条返回两个 IP 地址的 DNS A 记录:192.168.100.1192.168.100.2。这些 IP 地址的反向 DNS 查询分别返回 node1.eng.example.localnode2.eng.example.local。当前节点的名称未设置,默认为 rabbit@$(hostname)。最终发现的节点列表将包含两个节点:rabbit@node1.eng.example.localrabbit@node2.eng.example.local

配置

种子主机名使用 cluster_formation.dns.hostname 配置设置进行设置。

cluster_formation.peer_discovery_backend = dns

# the backend can also be specified using its module name
# cluster_formation.peer_discovery_backend = rabbit_peer_discovery_dns

cluster_formation.dns.hostname = discovery.eng.example.local

DNS 节点发现与并行集群形成竞争条件

DNS 节点发现后端不使用锁定来防止并行初始集群形成期间的固有竞争条件,并且需要由部署工具注入随机启动延迟。

对于大多数环境,建议使用 1 到 20 秒之间的随机值。在节点动态注册到 DNS 系统且可能需要时间的环境中,请使用更宽的范围,例如 1 到 60 秒。

容器化环境中的 Host 文件修改

警告

在某些容器化环境中,本地 hosts 文件会在容器启动时被修改。这可能会影响主机上的主机名解析,并使此节点发现机制无法完成其工作。

在某些容器化环境中,本地 hosts 文件会在容器启动时被修改,例如,可以将基于配置或约定的本地主机名添加到其中。

这可能会影响主机上的主机名解析,并使此节点发现机制无法完成其工作。

Podman 是一个已知的可以执行此类 host 文件修改的工具示例。为了避免这种情况,必须将其 host_containers_internal_ip 设置设置为空字符串。

在无法调整容器级设置的环境中,可以将运行时配置为忽略标准本地 hosts 文件,而仅使用 DNS 或预配置的主机名到 IP 地址映射集。

AWS (EC2) 上的节点发现

AWS 节点发现概述

通过插件提供特定于 AWS (EC2) 的发现机制。

与任何插件一样,它必须先启用才能使用。对于节点发现插件,这意味着它们必须在第一次节点启动之前启用预配置

rabbitmq-plugins --offline enable rabbitmq_peer_discovery_aws

该插件为节点提供了两种发现其对等节点的方法:

  • 使用 EC2 实例标签
  • 使用 AWS 自动伸缩组成员资格

这两种方法都依赖于特定于 AWS 的 API(端点)和功能,因此无法在其他 IaaS 环境中工作。一旦检索到集群成员实例列表,最终节点名称将使用实例主机名或 IP 地址进行计算。

配置和凭据

在节点可以在 AWS 上执行任何操作之前,它需要配置一组 AWS 账户凭据。这可以通过以下几种方式完成:

  1. 通过配置文件
  2. 使用环境变量 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY

还将咨询该区域的 EC2 实例元数据服务

以下示例片段配置 RabbitMQ 使用 AWS 节点发现后端,并提供有关 AWS 区域以及一组凭据的信息:

cluster_formation.peer_discovery_backend = aws

# the backend can also be specified using its module name
# 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

如果区域未配置,默认将使用 us-east-1。配置文件中的敏感值可以选择加密

如果为运行 RabbitMQ 节点的 EC2 实例分配了 IAM 角色,则必须使用策略来允许所述实例使用 EC2 实例元数据服务。当插件被配置为使用自动伸缩组成员时,策略必须授予描述自动伸缩组成员(实例)的访问权限。以下是涵盖这两种用例的策略示例:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingInstances",
"ec2:DescribeInstances"
],
"Resource": [
"*"
]
}
]
}

使用自动伸缩组成员资格

当使用基于自动伸缩的节点发现时,将列出当前节点的 EC2 实例自动伸缩组成员,并用于生成发现的对等节点列表。

要使用自动伸缩组成员资格,请将 cluster_formation.aws.use_autoscaling_group 键设置为 true

cluster_formation.peer_discovery_backend = 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.use_autoscaling_group = true

使用 EC2 实例标签

当使用基于标签的节点发现时,插件将使用 EC2 API 列出 EC2 实例,并按配置的实例标签进行过滤。生成的实例集将用于生成发现的对等节点列表。

标签使用 cluster_formation.aws.instance_tags 键进行配置。下面的示例使用了三个标签:regionserviceenvironment

cluster_formation.peer_discovery_backend = 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

使用私有 EC2 实例 IP

默认情况下,节点发现将使用私有 DNS 主机名来计算节点名称。此选项最方便,且强烈推荐

但是,可以通过将 cluster_formation.aws.use_private_ip 键设置为 true 来选择使用私有 IP。为了使此设置生效,必须在节点部署时将 RABBITMQ_NODENAME 设置为私有 IP 地址

RABBITMQ_USE_LONGNAME 也必须设置为 true,否则 IP 地址将被视为节点名称的无效部分。

cluster_formation.peer_discovery_backend = 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.use_autoscaling_group = true
cluster_formation.aws.use_private_ip = true

Kubernetes 上的节点发现

提示

在大多数情况下,当部署到 Kubernetes 时,您无需担心节点发现。

集群操作员(部署到 Kubernetes 的推荐方式)以及流行的 Helm 图表已为您预先配置了节点发现。

Kubernetes 节点发现概述

通过插件提供基于 Kubernetes 的发现机制。

由于节点发现发生在节点启动的早期,您应该将 rabbitmq_peer_discovery_k8s 添加到 enabled_plugins 文件中。在 Kubernetes 部署的情况下,它通常是一个 ConfigMap

自 RabbitMQ 4.1 起,此插件仅允许具有最低序数索引的节点(几乎总是带有 -0 后缀的 Pod)形成新集群。该节点被称为种子节点。

所有其他节点将加入种子节点,或者如果无法加入,将永远尝试加入它。

在最常见的场景中,这意味着:

  • 带有 -0 后缀的 Pod 将立即启动,有效地形成一个新的单节点集群
  • 任何其他 Pod 将加入带有 -0 后缀的 Pod,并与其同步集群元数据

配置

在大多数情况下,除了启用此插件外,无需进行任何配置。

如果您在 StatefulSet 中使用不同的序数起始值,则必须配置此插件以使用它:

cluster_formation.k8s.ordinal_start = N

其中 N 与 StatefulSet 的 .spec.ordinals.start 值匹配。

如果插件因任何原因无法工作(非常不寻常的 Kubernetes 配置或主机名解析问题),并且您必须强制 RabbitMQ 使用与其自动选择的不同的种子节点,则可以这样做:

cluster_formation.k8s.seed_node = rabbit@seed-node-hostname

如果配置了 cluster_formation.k8s.seed_node,此插件将直接使用该值作为种子节点。如果您这样做,请在 GitHub 上提交问题并解释该插件为何无法为您工作,以便我们进行改进。

使用 Consul 的节点发现

Consul 节点发现概述

通过插件提供基于 Consul 的发现机制。

与任何插件一样,它必须先启用才能使用。对于节点发现插件,这意味着它们必须在第一次节点启动之前启用预配置

rabbitmq-plugins --offline enable rabbitmq_peer_discovery_consul

该插件支持 Consul 0.8.0 及更高版本。

节点在启动时向 Consul 注册,并在离开时注销。在注册之前,节点将尝试在 Consul 中获取锁,以降低初始集群形成期间竞争条件的可能性。当节点向 Consul 注册时,它将为自己设置一个周期性的健康检查(详见下文)。

配置

要使用 Consul 进行节点发现,请将 cluster_formation.peer_discovery_backend 设置为 consul 或其模块名称 rabbit_peer_discovery_consul(注意:模块名称与插件名称略有不同)。

cluster_formation.peer_discovery_backend = consul

# the backend can also be specified using its module name
# cluster_formation.peer_discovery_backend = rabbit_peer_discovery_consul

# Consul host (hostname or IP address). Default value is localhost
cluster_formation.consul.host = consul.eng.example.local

Consul 端点

可以配置 Consul 端口和 URI 方案:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# 8500 is used by default
cluster_formation.consul.port = 8500
# http is used by default
cluster_formation.consul.scheme = http

Consul ACL 令牌

要配置 Consul ACL 令牌,请使用 cluster_formation.consul.acl_token

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
cluster_formation.consul.acl_token = acl-token-value

服务名称(在 Consul 中注册)默认为 "rabbitmq",但可以覆盖。

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# rabbitmq is used by default
cluster_formation.consul.svc = rabbitmq

服务地址

在 Consul 中注册的服务主机名(地址)将由对等节点获取,因此必须在所有节点上都能解析。主机名可以由插件计算或由用户指定。自动计算时,可以使用许多节点和 OS 属性:

  • 主机名(由 gethostname(2) 返回)
  • 节点名称(不带 rabbit@ 前缀)
  • NIC(网络控制器接口)的 IP 地址

cluster_formation.consul.svc_addr_auto 设置为 false 时,服务名称将按原样从 cluster_formation.consul.svc_addr 获取。当它设置为 true 时,下面解释的其他选项将发挥作用。

在下面的示例中,报告给 Consul 的服务地址被硬编码为 hostname1.rmq.eng.example.local,而不是根据环境自动计算:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local

cluster_formation.consul.svc = rabbitmq
# do not compute service address, it will be specified below
cluster_formation.consul.svc_addr_auto = false
# service address, will be communicated to other nodes
cluster_formation.consul.svc_addr = hostname1.rmq.eng.example.local
# use long RabbitMQ node names?
cluster_formation.consul.use_longname = true

在此示例中,报告给 Consul 的服务地址是从节点名称解析的(rabbit@ 前缀将被删除):

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local

cluster_formation.consul.svc = rabbitmq
# do compute service address
cluster_formation.consul.svc_addr_auto = true
# compute service address using node name
cluster_formation.consul.svc_addr_use_nodename = true
# use long RabbitMQ node names?
cluster_formation.consul.use_longname = true

cluster_formation.consul.svc_addr_use_nodename 是一个布尔字段,它指示 Consul 节点发现后端使用 RabbitMQ 节点名称计算服务地址。

在下一个示例中,服务地址是使用操作系统报告的主机名计算的,而不是节点名称:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local

cluster_formation.consul.svc = rabbitmq
# do compute service address
cluster_formation.consul.svc_addr_auto = true
# compute service address using host name and not node name
cluster_formation.consul.svc_addr_use_nodename = false
# use long RabbitMQ node names?
cluster_formation.consul.use_longname = true

在下面的示例中,服务地址是通过获取提供的 NIC en0 的 IP 地址来计算的:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local

cluster_formation.consul.svc = rabbitmq
# do compute service address
cluster_formation.consul.svc_addr_auto = true
# compute service address using the IP address of a NIC, en0
cluster_formation.consul.svc_addr_nic = en0
cluster_formation.consul.svc_addr_use_nodename = false
# use long RabbitMQ node names?
cluster_formation.consul.use_longname = true

服务端口

在 Consul 中注册的服务端口可以覆盖。只有当 RabbitMQ 为客户端(技术上为 AMQP 0-9-1 和 AMQP 1.0)连接使用非标准端口时,这才是必要的,因为默认值是 5672。

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# 5672 is used by default
cluster_formation.consul.svc_port = 6674

服务标签和元数据

可以提供 Consul 服务标签

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# Define tags for the RabbitMQ service: "qa" and "3.8"
cluster_formation.consul.svc_tags.1 = qa
cluster_formation.consul.svc_tags.2 = 3.8

可以配置 Consul 服务元数据,这是一个字符串键到字符串值的映射,具有一定的限制(请参阅 Consul 文档以了解更多信息)。

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local

# Define metadata for the RabbitMQ service. Both keys and values have a
# maximum length limit enforced by Consul. This can be used to provide additional
# context about the service (RabbitMQ cluster) for operators or other tools.
cluster_formation.consul.svc_meta.owner = team-xyz
cluster_formation.consul.svc_meta.service = service-one
cluster_formation.consul.svc_meta.stats_url = https://service-one.eng.megacorp.local/stats/

服务健康检查

当节点向 Consul 注册时,它会为自己设置一个周期性的健康检查。在线节点将定期向 Consul 发送健康检查更新以指示服务可用。此间隔可以配置:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# health check interval (node TTL) in seconds
# default: 30
cluster_formation.consul.svc_ttl = 40

健康检查失败的节点被 Consul 视为处于警告状态。此类节点可以在一段时间后被 Consul 自动注销(注意:这是一个与上面的 TTL 不同的间隔值)。该周期不能少于 60 秒。

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# health check interval (node TTL) in seconds
cluster_formation.consul.svc_ttl = 30
# how soon should nodes that fail their health checks be unregistered by Consul?
# this value is in seconds and must not be lower than 60 (a Consul requirement)
cluster_formation.consul.deregister_after = 90

请参阅下面关于节点自动清理的部分。

警告状态下的节点默认排除在节点发现结果之外。可以通过将 cluster_formation.consul.include_nodes_with_warnings 设置为 true 来选择将它们包括在内:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# health check interval (node TTL) in seconds
cluster_formation.consul.svc_ttl = 30
# include node in the warning state into discovery result set
cluster_formation.consul.include_nodes_with_warnings = true

选择退出注册

如果配置的后端支持注册,节点在被指示停止时会注销。

可以通过配置选项 cluster_formation.registration.enabled 完全选择退出注册。

cluster_formation.registration.enabled = false

当以这种方式配置时,节点必须手动注册或使用其他机制注册,例如通过像 Nomad 这样的容器编排器。

节点名称后缀

如果计算节点名称并使用长节点名称,则可以向从 Consul 检索到的节点名称追加后缀。格式为 .node.{domain_suffix}。这在具有 DNS 约定的环境中非常有用,例如当所有服务节点都组织在单独的子域中时。这是一个示例:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local

cluster_formation.consul.svc = rabbitmq
# do compute service address
cluster_formation.consul.svc_addr_auto = true
# compute service address using node name
cluster_formation.consul.svc_addr_use_nodename = true
# use long RabbitMQ node names?
cluster_formation.consul.use_longname = true
# append a suffix (node.rabbitmq.example.local) to node names retrieved from Consul
cluster_formation.consul.domain_suffix = example.local

使用此设置,节点名称将被计算为 rabbit@192.168.100.1.node.example.local,而不是 rabbit@192.168.100.1

分布式锁获取

当节点尝试在启动时获取锁而锁已被占用时,它将等待锁可用一段时间。默认值为 300 秒,但可以配置:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
# lock acquisition timeout in seconds
# default: 300
# cluster_formation.consul.lock_wait_time is an alias
cluster_formation.consul.lock_timeout = 60

锁键前缀默认为 rabbitmq。它也可以覆盖:

cluster_formation.peer_discovery_backend = consul

cluster_formation.consul.host = consul.eng.example.local
cluster_formation.consul.lock_timeout = 60
# should the Consul key used for locking be prefixed with something
# other than "rabbitmq"?
cluster_formation.consul.lock_prefix = environments-qa

使用 Etcd 的节点发现

Etcd 节点发现概述

通过插件提供基于 etcd 的发现机制。

自 RabbitMQ 3.8.4 起,该插件使用 v3 API、基于 gRPC 的 etcd 客户端,并需要 etcd 3.4 或更高版本

与任何插件一样,它必须先启用才能使用。对于节点发现插件,这意味着它们必须在第一次节点启动之前启用预配置

rabbitmq-plugins --offline enable rabbitmq_peer_discovery_etcd

节点在启动时通过在常规命名的目录中创建键来向 etcd 注册。这些键有较短(例如一分钟)的过期时间。当节点正常停止时,这些键会被删除。

在注册之前,节点将尝试在 etcd 中获取锁,以降低初始集群形成期间竞争条件的可能性。

每个节点的键都有一个关联的租约,具有可配置的 TTL。节点保持其键的租约处于活动状态。如果节点失去连接并且无法更新其租约,其键将在 TTL 过期后由 etcd 清理。此类节点将不会被新加入的节点发现。如果配置了,此类节点可以从集群中强制移除。

配置

etcd 端点和身份验证

要使用 etcd 进行节点发现,请将 cluster_formation.peer_discovery_backend 设置为 etcd 或其模块名称 rabbit_peer_discovery_etcd(注意:模块名称与插件名称略有不同)。

该插件需要配置 etcd 端点才能连接:

cluster_formation.peer_discovery_backend = etcd

# the backend can also be specified using its module name
# cluster_formation.peer_discovery_backend = rabbit_peer_discovery_etcd

# etcd endpoints. This property is required or peer discovery won't be performed.
cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379

可以配置多个 etcd 端点。将使用插件可以成功连接到的第一个随机选择的端点。

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479
cluster_formation.etcd.endpoints.3 = three.etcd.eng.example.local:2579

如果启用了 etcd 身份验证,则可以将插件配置为使用一对凭据:

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479
cluster_formation.etcd.endpoints.3 = three.etcd.eng.example.local:2579

cluster_formation.etcd.username = rabbitmq
cluster_formation.etcd.password = s3kR37

可以使用 advanced.config 文件来加密配置文件中列出的密码值。在这种情况下,所有插件设置都必须移动到高级配置文件中。

%% advanced.config file
[
{rabbit,
[{cluster_formation,
[{peer_discovery_etcd, [
{endpoints, [
"one.etcd.eng.example.local:2379",
"two.etcd.eng.example.local:2479",
"three.etcd.eng.example.local:2579"
]},

{etcd_prefix, "rabbitmq"},
{cluster_name, "default"},

{etcd_username, "etcd user"},
{etcd_password, {encrypted, <<"cPAymwqmMnbPXXRVqVzpxJdrS8mHEKuo2V+3vt1u/fymexD9oztQ2G/oJ4PAaSb2c5N/hRJ2aqP/X0VAfx8xOQ==">>}
}]
}]
}]
},

{config_entry_decoder, [
{passphrase, <<"decryption key passphrase">>}
]}
].

键命名

节点发现机制使用的目录和键遵循命名方案。由于在 etcd v3 API 中键空间是扁平的,因此使用了硬编码的前缀。它允许插件使用众所周知的前缀可预测地执行键范围查询:

# for node presence keys
/rabbitmq/discovery/{prefix}/clusters/{cluster name}/nodes/{node name}
# for registration lock keys
/rabbitmq/locks/{prefix}/clusters/{cluster name}/registration

这是一个节点 rabbit@hostname1 将使用的键示例,其中包含用户提供的默认键前缀和集群名称:

/rabbitmq/discovery/rabbitmq/clusters/default/nodes/rabbit@hostname1

默认键前缀仅为 "rabbitmq"。它很少需要覆盖,但受支持:

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479
cluster_formation.etcd.endpoints.3 = three.etcd.eng.example.local:2579

# rabbitmq is used by default
cluster_formation.etcd.key_prefix = rabbitmq_discovery

如果多个 RabbitMQ 集群共享同一个 etcd 安装,则每个集群必须使用唯一的名称。

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479
cluster_formation.etcd.endpoints.3 = three.etcd.eng.example.local:2579

# default name: "default"
cluster_formation.etcd.cluster_name = staging

键租约和 TTL

用于节点注册的键将具有与其关联的带 TTL 的租约。在线节点将定期保持租约处于活动状态(刷新)。TTL 值可以配置:

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479
cluster_formation.etcd.endpoints.3 = three.etcd.eng.example.local:2579

# node TTL in seconds
# default: 30
cluster_formation.etcd.node_ttl = 40

在节点运行且插件保持启用状态时,键租约会定期更新。

可以强制从集群中移除未能刷新其键的节点。这将在本指南后面介绍。

当节点尝试在启动时获取锁而锁已被占用时,它将等待锁可用一段时间。默认值为 300 秒,但可以配置:

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479

# lock acquisition timeout in seconds
# default: 300
# cluster_formation.consul.lock_wait_time is an alias
cluster_formation.etcd.lock_timeout = 60

检查键

要列出基于 etcd 的节点发现机制使用的所有键,请使用 etcdctl get,如下所示:

etcdctl get --prefix=true "/rabbitmq"

TLS

可以将插件配置为在连接到 etcd 时使用 TLS。如果配置了下面列出的任何 TLS 选项,则将启用 TLS,否则连接将使用没有 TLS 的“普通 TCP”。

该插件充当 TLS 客户端。必须提供受信任的 CA 证书文件以及客户端证书和私钥对:

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479

# trusted CA certificate file path
cluster_formation.etcd.ssl_options.cacertfile = /path/to/ca_certificate.pem
# client certificate (public key) file path
cluster_formation.etcd.ssl_options.certfile = /path/to/client_certificate.pem
# client private key file path
cluster_formation.etcd.ssl_options.keyfile = /path/to/client_key.pem

# use TLSv1.2 for connections
cluster_formation.etcd.ssl_options.versions.1 = tlsv1.2

# enables peer verification (the plugin will verify the certificate chain of the server)
cluster_formation.etcd.ssl_options.verify = verify_peer
cluster_formation.etcd.ssl_options.fail_if_no_peer_cert = true

支持更多 TLS 选项,例如密码套件和客户端会话重新协商选项。

cluster_formation.peer_discovery_backend = etcd

cluster_formation.etcd.endpoints.1 = one.etcd.eng.example.local:2379
cluster_formation.etcd.endpoints.2 = two.etcd.eng.example.local:2479

# trusted CA certificate file path
cluster_formation.etcd.ssl_options.cacertfile = /path/to/ca_certificate.pem
# client certificate (public key) file path
cluster_formation.etcd.ssl_options.certfile = /path/to/client_certificate.pem
# client private key file path
cluster_formation.etcd.ssl_options.keyfile = /path/to/client_key.pem

# use TLSv1.2 for connections
cluster_formation.etcd.ssl_options.versions.1 = tlsv1.2

# enables peer verification (the plugin will verify the certificate chain of the server)
cluster_formation.etcd.ssl_options.verify = verify_peer
cluster_formation.etcd.ssl_options.fail_if_no_peer_cert = true

# use secure session renegotiation
cluster_formation.etcd.ssl_options.secure_renegotiate = true

# Explicitly list enabled cipher suites. This can break connectivity
# and is not necessary most of the time.
cluster_formation.etcd.ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
cluster_formation.etcd.ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
cluster_formation.etcd.ssl_options.ciphers.3 = ECDH-ECDSA-AES256-GCM-SHA384
cluster_formation.etcd.ssl_options.ciphers.4 = ECDH-RSA-AES256-GCM-SHA384
cluster_formation.etcd.ssl_options.ciphers.5 = DHE-RSA-AES256-GCM-SHA384
cluster_formation.etcd.ssl_options.ciphers.6 = DHE-DSS-AES256-GCM-SHA384
cluster_formation.etcd.ssl_options.ciphers.7 = ECDHE-ECDSA-AES128-GCM-SHA256
cluster_formation.etcd.ssl_options.ciphers.8 = ECDHE-RSA-AES128-GCM-SHA256
cluster_formation.etcd.ssl_options.ciphers.9 = ECDH-ECDSA-AES128-GCM-SHA256
cluster_formation.etcd.ssl_options.ciphers.10 = ECDH-RSA-AES128-GCM-SHA256
cluster_formation.etcd.ssl_options.ciphers.11 = DHE-RSA-AES128-GCM-SHA256
cluster_formation.etcd.ssl_options.ciphers.12 = DHE-DSS-AES128-GCM-SHA256

初始集群形成期间的竞争条件

为了成功形成集群,最初只有一个节点应该形成集群,即作为独立节点启动并初始化其数据库。如果不是这种情况,将形成多个集群而不是一个,这违反了操作员的预期。

考虑一个整个集群一次性配置并且所有节点并行启动的部署。在这种情况下,启动节点之间会发生自然的竞争条件。为了防止多个节点形成单独的集群,节点发现后端在形成集群(播种)或加入对等节点时尝试获取锁。使用的锁因后端而异:

  • 经典配置文件、K8s 和 AWS 后端使用由运行时提供的内置锁定库
  • Consul 节点发现后端在 Consul 中设置了一个锁。
  • etcd 节点发现后端在 etcd 中设置了一个锁。

DNS 节点发现机制不使用锁定,需要由部署工具注入随机启动延迟。对于大多数环境,建议使用 1 到 20 秒之间的随机值。在节点动态注册到 DNS 系统且可能需要时间的环境中,请使用更宽的范围,例如 1 到 60 秒。

节点健康检查和强制移除

使用节点发现形成的集群中的节点可能会失败、变得不可用或被永久移除(退役)。一些操作员可能希望此类节点在一段时间后自动从集群中移除。此类自动强制移除也可能产生不可预见的副作用,因此 RabbitMQ 不会强制执行此行为。它应非常小心地使用,并且仅在完全理解并考虑了副作用的情况下使用。

例如,考虑一个使用 AWS 后端并配置为使用自动伸缩组成员资格的集群。如果该组中的一个 EC2 实例失败并随后作为新节点重新创建,它的原始“化身”将被视为同一个集群中另一个永久不可用的节点。

对于提供动态节点管理(而不是配置文件中固定的节点列表)的节点发现后端,此类未知节点可以被记录或强制从集群中移除。

它们是:

强制节点移除可能很危险,应该仔细考虑。例如,暂时不可用但将重新加入(或由于其持久存储已从其之前的化身重新连接而重新创建)的节点可能会被自动清理永久踢出集群,从而导致无法重新加入。

在启用下面涵盖的配置键之前,请确保启用了兼容的节点发现插件。如果不是这种情况,节点将报告设置未知并无法启动。

要为未知节点记录警告,应将 cluster_formation.node_cleanup.only_log_warning 设置为 true

# Don't remove cluster members unknown to the peer discovery backend but log
# warnings.
#
# This setting can only be used if a compatible peer discovery plugin is enabled.
cluster_formation.node_cleanup.only_log_warning = true

这是默认行为。

要从集群中强制删除未知节点,应将 cluster_formation.node_cleanup.only_log_warning 设置为 false

# Forcefully remove cluster members unknown to the peer discovery backend. Once removed,
# the nodes won't be able to rejoin. Use this mode with great care!
#
# This setting can only be used if a compatible peer discovery plugin is enabled.
cluster_formation.node_cleanup.only_log_warning = false

请注意,此选项应谨慎使用,特别是对于除 AWS 以外的发现后端。

清理检查会定期执行。该间隔默认为 60 秒,可以覆盖:

# perform the check every 90 seconds
cluster_formation.node_cleanup.interval = 90

某些后端(Consul、etcd)支持节点健康检查或 TTL。这些检查不应与监控健康检查混淆。它们允许节点发现服务(如 etcd 或 Consul)跟踪哪些节点仍然存在(最近已检入)。

通过服务发现健康检查,节点在其键上设置 TTL 和/或定期通知其各自的发现服务它们仍然存在。如果一段时间后没有来自节点的通知,节点的键最终将过期(对于 Consul,此类节点将被视为处于警告状态)。

对于 etcd,此类节点将不再出现在发现结果中。对于 Consul,它们可以被移除(取消注册)或报告其警告状态。请参阅这些后端的文档以了解更多信息。

对不存在的节点进行自动清理在故障/终止的节点将被全新节点替换(包括持久存储不会被重新连接的情况)的环境中最为合理。

当自动节点清理被停用(切换到警告模式)时,操作员必须使用 rabbitmqctl forget_cluster_node 明确移除不存在的集群节点。

自动移除的负面副作用

自动节点移除有许多操作员应该注意的负面副作用。一个暂时不可达的节点,例如因为它失去了与网络其余部分的连接或其 VM 被暂时挂起,将被移除,然后重新出现。此类节点将无法重新加入其集群,并记录类似的消息:

Node 'rabbit@node1.local' thinks it's clustered with node 'rabbit@node2.local', but 'rabbit@node2.local' disagrees

此外,此类节点可能会开始无法通过其监控健康检查,因为它们将处于永久“分区”状态。即使此类节点可能已被新节点替换且集群按预期运行,此类自动移除和替换的节点也可能产生监控误报。

副作用列表不仅限于这两个场景,但它们都有相同的根本原因:自动移除的节点可能会在没有意识到它已被踢出集群的情况下返回。监控系统和操作员也不会立即意识到该事件。

节点发现失败和重试

在最新版本中,如果节点发现尝试失败,它将重试一定次数,每次尝试之间有延迟。这类似于节点在重启后上线时执行的对等同步重试。

例如,使用 Kubernetes 节点发现机制,这意味着如果列出 Pod 的 Kubernetes API 请求失败,将会重试。使用 AWS 机制,EC2 API 请求会被重试,等等。

此类重试绝不能处理所有可能的故障场景,但它们在实践中提高了节点发现的韧性,从而提高了集群和节点部署的韧性。但是,如果集群节点无法相互身份验证,重试只会推迟集群形成的不可避免的失败。

未能执行节点发现的节点将记录其剩余的恢复尝试:

2020-06-27 06:35:36.426 [error] <0.277.0> Trying to join discovered peers failed. Will retry after a delay of 500 ms, 4 retries left...
2020-06-27 06:35:36.928 [warning] <0.277.0> Could not auto-cluster with node rabbit@hostname2: {badrpc,nodedown}
2020-06-27 06:35:36.930 [warning] <0.277.0> Could not auto-cluster with node rabbit@hostname3: {badrpc,nodedown}
2020-06-27 06:35:36.930 [error] <0.277.0> Trying to join discovered peers failed. Will retry after a delay of 500 ms, 3 retries left...
2020-06-27 06:35:37.432 [warning] <0.277.0> Could not auto-cluster with node rabbit@hostname2: {badrpc,nodedown}
2020-06-27 06:35:37.434 [warning] <0.277.0> Could not auto-cluster with node rabbit@hostname3: {badrpc,nodedown}

如果节点未能执行节点发现并耗尽所有重试次数,强烈建议启用调试日志记录以进行故障排除

重试次数和延迟可以配置:

# These are the default values

# Retry peer discovery operations up to ten times
cluster_formation.discovery_retry_limit = 10

# 500 milliseconds
cluster_formation.discovery_retry_interval = 500

默认值涵盖了五秒的服务、API 端点或参与节点发现的节点不可用的情况。这些值足以覆盖零星故障。在依赖服务(DNS、etcd、Consul 等)可能与 RabbitMQ 集群部署并发配置,因此可能在一段时间后才可用的环境中,需要增加这些值。

HTTP 代理设置

使用 HTTP 与其依赖项(例如 AWS、Consul 和 etcd)交互的节点发现机制可以使用 HTTP 代理代理其请求。

HTTP 和 HTTPS 有单独的代理设置:

# example HTTP and HTTPS proxy servers, values in your environment
# will vary
cluster_formation.proxy.http_proxy = 192.168.0.98
cluster_formation.proxy.https_proxy = 192.168.0.98

某些主机可以被排除在代理之外,例如链路本地 AWS 实例元数据 IP 地址:

# example HTTP and HTTPS proxy servers, values in your environment
# will vary
cluster_formation.proxy.http_proxy = 192.168.0.98
cluster_formation.proxy.https_proxy = 192.168.0.98

# requests to these hosts won't go via proxy
cluster_formation.proxy.proxy_exclusions.1 = 169.254.169.254
cluster_formation.proxy.proxy_exclusions.2 = excluded.example.local

故障排除

节点发现子系统和各个机制实现以 info 日志级别记录重要的发现过程步骤。更广泛的日志记录可在 debug 级别获得。依赖于通过 HTTP 可访问的外部服务的机制将在 debug 级别记录所有传出的 HTTP 请求和响应代码。有关日志记录配置的更多信息,请参阅日志记录指南

如果日志中不包含任何演示节点发现进度的条目(例如机制检索到的节点列表或集群尝试),则可能意味着节点已经有了初始化的数据目录或已经是集群的成员。在这些情况下,不会执行节点发现。

节点发现依赖于节点间的网络连接和通过共享密钥进行的成功身份验证。验证节点可以相互通信并使用预期的 Erlang cookie 值(在所有集群节点上也是相同的)。有关更多信息,请参阅主集群指南

网络连接故障排除的方法以及常用工具在网络连接故障排除指南中进行了介绍。

© . This site is unofficial and not affiliated with VMware.