Clustering Guide
refer to : https://www.rabbitmq.com/docs/3.13/clustering
What is a Cluster?
A RabbitMQ cluster is a logical grouping of one or more (three, five, seven, or more) nodes,
each sharing users, virtual hosts, queues, streams, exchanges, bindings, runtime parameters and other distributed state.
For a cluster to be formed, nodes must be configured in a certain way and satisfy a number of requirements such as open port access.
After cluster formation, all nodes in a cluster are aware of other cluster members.
Client applications can be aware or not be aware of the fact that there are multiple cluster nodes, and connect to any of them, or, depending on the protocol used, a subset of them. For example, RabbitMQ Stream Protocol clients can connect to multiple nodes at once. This is covered in more details later in this guide.
Cluster Formation
Ways of Forming a Cluster
A RabbitMQ cluster can be formed in a number of ways:
- Declaratively by listing cluster nodes in config file
- Declaratively using DNS-based discovery
- Declaratively using AWS (EC2) instance discovery (via a plugin)
- Declaratively using Kubernetes discovery (via a plugin)
- Declaratively using Consul-based discovery (via a plugin)
- Declaratively using etcd-based discovery (via a plugin)
- Manually with
rabbitmqctl
These mechanisms are covered in more details in the Cluster Formation guide.
The composition of a cluster can be altered dynamically. All RabbitMQ brokers start out as running on a single node. These nodes can be joined into clusters, and subsequently turned back into individual brokers again.
Node Names (Identifiers)
RabbitMQ nodes are identified by node names. A node name consists of two parts, a prefix (usually rabbit
) and hostname. For example, rabbit@node1.messaging.svc.local
is a node name with the prefix of rabbit
and hostname of node1.messaging.svc.local
.
Node names in a cluster must be unique. If more than one node is running on a given host (this is usually the case in development and QA environments), they must use different prefixes, e.g. rabbit1@hostname
and rabbit2@hostname
.
In a cluster, nodes identify and contact each other using node names. This means that the hostname part of every node name must resolve. CLI tools also identify and address nodes using node names.
When a node starts up, it checks whether it has been assigned a node name. This is done via the RABBITMQ_NODENAME
environment variable. If no value was explicitly configured, the node resolves its hostname and prepends rabbit
to it to compute its node name.
If a system uses fully qualified domain names (FQDNs) for hostnames, RabbitMQ nodes and CLI tools must be configured to use so called long node names. For server nodes this is done by setting the RABBITMQ_USE_LONGNAME
environment variable to true
.
For CLI tools, either RABBITMQ_USE_LONGNAME
must be set or the --longnames
option must be specified.
Cluster Formation Requirements
Hostname Resolution
RabbitMQ nodes address each other using a node name, a combination of a prefix and domain name, either short or fully-qualified (FQDNs).
Therefore every cluster member must be able to resolve hostnames of every other cluster member, its own hostname, as well as machines on which command line tools such as rabbitmqctl
might be used.
Nodes will perform hostname resolution early on node boot. In container-based environments it is important that hostname resolution is ready before the container is started.
Hostname resolution can use any of the standard OS-provided methods:
- DNS records
- Local host files (e.g.
/etc/hosts
)
Ports That Must Be Opened for Clustering and Replication
RabbitMQ nodes bind to ports (open server TCP sockets) in order to accept client and CLI tool connections. Other processes and tools such as SELinux may prevent RabbitMQ from binding to a port. When that happens, the node will fail to start.
CLI tools, client libraries and RabbitMQ nodes also open connections (client TCP sockets). Firewalls can prevent nodes and CLI tools from communicating with each other. The following ports are most relevant to inter-node communication in a cluster:
- 4369: epmd, a helper discovery daemon used by RabbitMQ nodes and CLI tools
- 6000 through 6500: used by RabbitMQ Stream replication
- 25672: used for inter-node and CLI tools communication (Erlang distribution server port) and is allocated from a dynamic range (limited to a single port by default, computed as AMQP port + 20000). Unless external connections on these ports are really necessary (e.g. the cluster uses federation or CLI tools are used on machines outside the subnet), these ports should not be publicly exposed. See networking guide for details.
- 35672-35682: used by CLI tools (Erlang distribution client ports) for communication with nodes and is allocated from a dynamic range (computed as server distribution port + 10000 through server distribution port + 10010).
It is possible to configure RabbitMQ to use different ports and specific network interfaces. See RabbitMQ Networking guide to learn more.
Port Access
RabbitMQ nodes bind to ports (open server TCP sockets) in order to accept client and CLI tool connections. Other processes and tools such as SELinux may prevent RabbitMQ from binding to a port. When that happens, the node will fail to start.
CLI tools, client libraries and RabbitMQ nodes also open connections (client TCP sockets). Firewalls can prevent nodes and CLI tools from communicating with each other. Make sure the following ports are accessible:
- 4369: epmd, a peer discovery service used by RabbitMQ nodes and CLI tools
- 5672, 5671: used by AMQP 0-9-1 and AMQP 1.0 clients without and with TLS
- 5552, 5551: used by the RabbitMQ Stream protocol clients without and with TLS
- 6000 through 6500: used for stream replication
- 25672: used for inter-node and CLI tools communication (Erlang distribution server port) and is allocated from a dynamic range (limited to a single port by default, computed as AMQP port + 20000). Unless external connections on these ports are really necessary (e.g. the cluster uses federation or CLI tools are used on machines outside the subnet), these ports should not be publicly exposed
- 35672-35682: this client TCP port range is used by CLI tools for communication with nodes. By default, the range computed as
(server distribution port + 10000)
through(server distribution port + 10010)
- 15672, 15671: HTTP API clients, management UI and rabbitmqadmin, without and with TLS (only if the management plugin is enabled)
- 61613, 61614: STOMP clients without and with TLS (only if the STOMP plugin is enabled)
- 1883, 8883: MQTT clients without and with TLS, if the MQTT plugin is enabled
- 15674: STOMP-over-WebSockets clients (only if the Web STOMP plugin is enabled)
- 15675: MQTT-over-WebSockets clients (only if the Web MQTT plugin is enabled)
- 15692, 15691: Prometheus metrics, without and with TLS (only if the Prometheus plugin is enabled)
Nodes in a Cluster
What is Replicated?
All data/state required for the operation of a RabbitMQ broker is replicated across all nodes. An exception to this are message queues, which by default reside on one node, though they are visible and reachable from all nodes. To replicate queues across nodes in a cluster, use a queue type that supports replication. This topic is covered in the Quorum Queues guide.
Nodes are Equal Peers
Some distributed systems have leader and follower nodes. This is generally not true for RabbitMQ. All nodes in a RabbitMQ cluster are equal peers: there are no special nodes in RabbitMQ core. This topic becomes more nuanced when quorum queues and plugins are taken into consideration but for most intents and purposes, all cluster nodes should be considered equal.
Many CLI tool operations can be executed against any node. An HTTP API client can target any cluster node.
Individual plugins can designate (elect) certain nodes to be "special" for a period of time. For example, federation links are colocated on a particular cluster node. Should that node fail, the links will be restarted on a different node.
In older (long maintained) versions, RabbitMQ management plugin used a dedicated node for stats collection and aggregation.
How CLI Tools Authenticate to Nodes (and Nodes to Each Other): the Erlang Cookie
RabbitMQ nodes and CLI tools (e.g. rabbitmqctl
) use a cookie to determine whether they are allowed to communicate with each other. For two nodes to be able to communicate they must have the same shared secret called the Erlang cookie. The cookie is just a string of alphanumeric characters up to 255 characters in size. It is usually stored in a local file. The file must be only accessible to the owner (e.g. have UNIX permissions of 600
or similar). Every cluster node must have the same cookie.
If the file does not exist, Erlang VM will try to create one with a randomly generated value when the RabbitMQ server starts up. Using such generated cookie files are appropriate in development environments only. Since each node will generate its own value independently, this strategy is not really viable in a clustered environment.
Erlang cookie generation should be done at cluster deployment stage, ideally using automation and orchestration tools.
In distributed deployment
Cookie File Locations
Linux, MacOS, *BSD
On UNIX systems, the cookie will be typically located in /var/lib/rabbitmq/.erlang.cookie
(used by the server) and $HOME/.erlang.cookie
(used by CLI tools). Note that since the value of $HOME
varies from user to user, it's necessary to place a copy of the cookie file for each user that will be using the CLI tools. This applies to both non-privileged users and root
.
RabbitMQ nodes will log its effective user's home directory location early on boot.
Troubleshooting
When a node starts, it will log the home directory location of its effective user:
node : rabbit@cdbf4de5f22d
home dir : /var/lib/rabbitmq
Authentication Failures
When the cookie is misconfigured (for example, not identical), RabbitMQ nodes will log errors such as "Connection attempt from disallowed node", "", "Could not auto-cluster".
For example, when a CLI tool connects and tries to authenticate using a mismatching secret value:
2020-06-15 13:03:33 [error] <0.1187.0> ** Connection attempt from node 'rabbitmqcli-99391-rabbit@warp10' rejected. Invalid challenge reply. **
When a CLI tool such as rabbitmqctl
fails to authenticate with RabbitMQ, the message usually says
* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed
* suggestion: hostname mismatch?
* suggestion: is the cookie set correctly?
* suggestion: is the Erlang distribution using TLS?
An incorrectly placed cookie file or cookie value mismatch are most common scenarios for such failures.
When a recent Erlang/OTP version is used, authentication failures contain more information and cookie mismatches can be identified better:
* connected to epmd (port 4369) on warp10
* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed
* Authentication failed (rejected by the remote node), please check the Erlang cookie
See the CLI Tools guide for more information.
CLI Tools
Starting with version 3.8.6
, rabbitmq-diagnostics
includes a command that provides relevant information on the Erlang cookie file used by CLI tools:
rabbitmq-diagnostics erlang_cookie_sources
The command will report on the effective user, user home directory and the expected location of the cookie file:
Cookie File
Effective user: antares
Effective home directory: /home/cli-user
Cookie file path: /home/cli-user/.erlang.cookie
Cookie file exists? true
Cookie file type: regular
Cookie file access: read
Cookie file size: 20
Cookie CLI Switch
--erlang-cookie value set? false
--erlang-cookie value length: 0
Env variable (Deprecated)
RABBITMQ_ERLANG_COOKIE value set? false
RABBITMQ_ERLANG_COOKIE value length: 0
Node Counts and Quorum
Because several features (e.g. quorum queues, client tracking in MQTT) require a consensus between cluster members, odd numbers of cluster nodes are highly recommended: 1, 3, 5, 7 and so on.
Unless any server directories were overridden, that's the directory where the cookie file will be looked for, and created by the node on first boot if it does not already exist.
In the example above, the cookie file location will be /var/lib/rabbitmq/.erlang.cookie
.
Clustering and Clients
Messaging Protocols
Assuming all cluster members are available, a messaging (AMQP 0-9-1, AMQP 1.0, MQTT, STOMP) client can connect to any node and perform any operation. Nodes will route operations to the quorum queue leader or queue leader replica transparently to clients.
With all supported messaging protocols a client is only connected to one node at a time.
In case of a node failure, clients should be able to reconnect to a different node, recover their topology and continue operation. For this reason, most client libraries accept a list of endpoints (hostnames or IP addresses) as a connection option. The list of hosts will be used during initial connection as well as connection recovery, if the client supports it. See documentation guides for individual clients to learn more.
With quorum queues and streams, clients will only be able to perform operations on queues that have a quorum of replicas online.
With classic mirrored queues, there are scenarios where it may not be possible for a client to transparently continue operations after connecting to a different node. They usually involve non-mirrored queues hosted on a failed node.
Stream Clients
RabbitMQ Stream protocol clients behave differently from messaging protocols clients: they are more cluster topology-aware. For publishing, they can connect to any node, and that node will forward all relevant operations to the node that hosts the leader replica of the stream.
However, stream consumers should connect to one of the nodes hosting the replicas of the target stream. The protocol includes a topology discovery operation, so well-behaved client libraries will select one of the suitable nodes. This won't be the case when a load balancer is used, however.
See Connecting to Streams to learn more.
Clustering and Observability
Client connections, channels and queues will be distributed across cluster nodes. Operators need to be able to inspect and monitor such resources across all cluster nodes.
RabbitMQ CLI tools such as rabbitmq-diagnostics
and rabbitmqctl
provide commands that inspect resources and cluster-wide state. Some commands focus on the state of a single node (e.g. rabbitmq-diagnostics environment
and rabbitmq-diagnostics status
), others inspect cluster-wide state. Some examples of the latter include rabbitmqctl list_connections
, rabbitmqctl list_mqtt_connections
, rabbitmqctl list_stomp_connections
, rabbitmqctl list_users
, rabbitmqctl list_vhosts
and so on.
Such "cluster-wide" commands will often contact one node first, discover cluster members and contact them all to retrieve and combine their respective state. For example, rabbitmqctl list_connections
will contact all nodes, retrieve their AMQP 0-9-1 and AMQP 1.0 connections, and display them all to the user. The user doesn't have to manually contact all nodes. Assuming a non-changing state of the cluster (e.g. no connections are closed or opened), two CLI commands executed against two different nodes one after another will produce identical or semantically identical results. "Node-local" commands, however, will not produce identical results since two nodes rarely have identical state: at the very least their node names will be different!
Management UI works similarly: a node that has to respond to an HTTP API request will fan out to other cluster members and aggregate their responses. In a cluster with multiple nodes that have management plugin enabled, the operator can use any node to access management UI. The same goes for monitoring tools that use the HTTP API to collect data about the state of the cluster. There is no need to issue a request to every cluster node in turn.
Node Failure Handling
RabbitMQ brokers tolerate the failure of individual nodes. Nodes can be started and stopped at will, as long as they can contact a cluster member node known at the time of shutdown.
Quorum queue allows queue contents to be replicated across multiple cluster nodes with parallel replication and a predictable leader election and data safety behavior as long as a majority of replicas are online.
Non-replicated classic queues can also be used in clusters. Non-mirrored queue behaviour in case of node failure depends on queue durability.
RabbitMQ clustering has several modes of dealing with network partitions, primarily consistency oriented. Clustering is meant to be used across LAN. It is not recommended to run clusters that span WAN. The Shovel or Federation plugins are better solutions for connecting brokers across a WAN. Note that Shovel and Federation are not equivalent to clustering.
Clustering Transcript with rabbitmqctl
The following several sections provide a transcript of manually setting up and manipulating a RabbitMQ cluster across three machines: rabbit1
, rabbit2
, rabbit3
. It is recommended that the example is studied before more automation-friendly cluster formation options are used.
We assume that the user is logged into all three machines, that RabbitMQ has been installed on the machines, and that the rabbitmq-server and rabbitmqctl scripts are in the user's PATH.
This transcript can be modified to run on a single host, as explained more details below.
Starting Independent Nodes
Clusters are set up by re-configuring existing RabbitMQ nodes into a cluster configuration. Hence the first step is to start RabbitMQ on all nodes in the normal way:
# on rabbit1
rabbitmq-server -detached
# on rabbit2
rabbitmq-server -detached
# on rabbit3
rabbitmq-server -detached
This creates three independent RabbitMQ brokers, one on each node, as confirmed by the cluster_status command:
# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}]
# => ...done.
# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.
The node name of a RabbitMQ broker started from the rabbitmq-server
shell script is rabbit@*shorthostname*
, where the short node name is lower-case (as in rabbit@rabbit1
, above). On Windows, if rabbitmq-server.bat
batch file is used, the short node name is upper-case (as in rabbit@RABBIT1
). When you type node names, case matters, and these strings must match exactly.
Creating a Cluster
In order to link up our three nodes in a cluster, we tell two of the nodes, say rabbit@rabbit2
and rabbit@rabbit3
, to join the cluster of the third, say rabbit@rabbit1
. Prior to that both newly joining members must be reset.
We first join rabbit@rabbit2
in a cluster with rabbit@rabbit1
. To do that, on rabbit@rabbit2
we stop the RabbitMQ application and join the rabbit@rabbit1
cluster, then restart the RabbitMQ application. Note that a node must be reset before it can join an existing cluster. Resetting the node removes all resources and data that were previously present on that node. This means that a node cannot be made a member of a cluster and keep its existing data at the same time. When that's desired, using the Blue/Green deployment strategy or backup and restore are the available options.
# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit2 ...
rabbitmqctl join_cluster rabbit@rabbit1
# => Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.
We can see that the two nodes are joined in a cluster by running the cluster_status command on either of the nodes:
# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# => {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.
Now we join rabbit@rabbit3
to the same cluster. The steps are identical to the ones above, except this time we'll cluster to rabbit2
to demonstrate that the node chosen to cluster to does not matter - it is enough to provide one online node and the node will be clustered to the cluster that the specified node belongs to.
# on rabbit3
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit3 ...done.
# on rabbit3
rabbitmqctl reset
# => Resetting node rabbit@rabbit3 ...
rabbitmqctl join_cluster rabbit@rabbit2
# => Clustering node rabbit@rabbit3 with rabbit@rabbit2 ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit3 ...done.
We can see that the three nodes are joined in a cluster by running the cluster_status
command on any of the nodes:
# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit3,rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.
# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.
By following the above steps we can add new nodes to the cluster at any time, while the cluster is running.
Restarting Cluster Nodes
Nodes that have been joined to a cluster can be stopped at any time. They can also fail or be terminated by the OS.
In general, if the majority of nodes is still online after a node is stopped, this does not affect the rest of the cluster, although client connection distribution, queue replica placement, and load distribution of the cluster will change.
Schema Syncing from Online Peers
A restarted node will sync the schema and other information from its peers on boot. Before this process completes, the node won't be fully started and functional.
It is therefore important to understand the process node go through when they are stopped and restarted.
A stopping node picks an online cluster member (only disc nodes will be considered) to sync with after restart. Upon restart the node will try to contact that peer 10 times by default, with 30 second response timeouts.
In case the peer becomes available in that time interval, the node successfully starts, syncs what it needs from the peer and keeps going.
If the peer does not become available, the restarted node will give up and voluntarily stop. Such condition can be identified by the timeout (timeout_waiting_for_tables
) warning messages in the logs that eventually lead to node startup failure:
2020-07-27 21:10:51.361 [warning] <0.269.0> Error while waiting for Mnesia tables: {timeout_waiting_for_tables,[rabbit@node2,rabbit@node1],[rabbit_durable_queue]}
2020-07-27 21:10:51.361 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 1 retries left
2020-07-27 21:11:21.362 [warning] <0.269.0> Error while waiting for Mnesia tables: {timeout_waiting_for_tables,[rabbit@node2,rabbit@node1],[rabbit_durable_queue]}
2020-07-27 21:11:21.362 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 0 retries left
2020-07-27 21:15:51.380 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 1 retries left
2020-07-27 21:16:21.381 [warning] <0.269.0> Error while waiting for Mnesia tables: {timeout_waiting_for_tables,[rabbit@node2,rabbit@node1],[rabbit_user,rabbit_user_permission, …]}
2020-07-27 21:16:21.381 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 0 retries left
2020-07-27 21:16:51.393 [info] <0.44.0> Application mnesia exited with reason: stopped
2020-07-27 21:16:51.397 [error] <0.269.0> BOOT FAILED
2020-07-27 21:16:51.397 [error] <0.269.0> ===========
2020-07-27 21:16:51.397 [error] <0.269.0> Timeout contacting cluster nodes: [rabbit@node1].
When a node has no online peers during shutdown, it will start without attempts to sync with any known peers. It does not start as a standalone node, however, and peers will be able to rejoin it.
When the entire cluster is brought down therefore, the last node to go down is the only one that didn't have any running peers at the time of shutdown. That node can start without contacting any peers first. Since nodes will try to contact a known peer for up to 5 minutes (by default), nodes can be restarted in any order in that period of time. In this case they will rejoin each other one by one successfully. This window of time can be adjusted using two configuration settings:
# wait for 60 seconds instead of 30
mnesia_table_loading_retry_timeout = 60000
# retry 15 times instead of 10
mnesia_table_loading_retry_limit = 15
By adjusting these settings and tweaking the time window in which known peer has to come back it is possible to account for cluster-wide redeployment scenarios that can be longer than 5 minutes to complete.
During upgrades, sometimes the last node to stop must be the first node to be started after the upgrade. That node will be designated to perform a cluster-wide schema migration that other nodes can sync from and apply when they rejoin.
Cluster Node Restart Example
The below example uses CLI tools to shut down the nodes rabbit@rabbit1
and rabbit@rabbit3
and check on the cluster status at each step:
# on rabbit1
rabbitmqctl stop
# => Stopping and halting node rabbit@rabbit1 ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit3,rabbit@rabbit2]}]
# => ...done.
# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]}]
# => ...done.
# on rabbit3
rabbitmqctl stop
# => Stopping and halting node rabbit@rabbit3 ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit2]}]
# => ...done.
In the below example, the nodes are started back, checking on the cluster status as we go along:
# on rabbit1
rabbitmq-server -detached
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.
# on rabbit3
rabbitmq-server -detached
# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]
# => ...done.
# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.
Forcing Node Boot in Case of Unavailable Peers
In some cases the last node to go offline cannot be brought back up. It can be removed from the cluster using the forget_cluster_node
rabbitmqctl command.
Alternatively force_boot
rabbitmqctl command can be used on a node to make it boot without trying to sync with any peers (as if they were last to shut down). This is usually only necessary if the last node to shut down or a set of nodes will never be brought back online.
Breaking Up a Cluster
Sometimes it is necessary to remove a node from a cluster. The operator has to do this explicitly using a rabbitmqctl
command.
Some peer discovery mechanisms support node health checks and forced removal of nodes not known to the discovery backend. That feature is opt-in (deactivated by default).
We first remove rabbit@rabbit3
from the cluster, returning it to independent operation. To do that, on rabbit@rabbit3
we stop the RabbitMQ application, reset the node, and restart the RabbitMQ application.
# on rabbit3
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit3 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit3 ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit3 ...done.
Note that it would have been equally valid to list rabbit@rabbit3
as a node.
Running the cluster_status command on the nodes confirms that rabbit@rabbit3
now is no longer part of the cluster and operates independently:
# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# => {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.
# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.
We can also remove nodes remotely. This is useful, for example, when having to deal with an unresponsive node. We can for example remove rabbit@rabbit1
from rabbit@rabbit2
.
# on rabbit1
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit1 ...done.
# on rabbit2
rabbitmqctl forget_cluster_node rabbit@rabbit1
# => Removing node rabbit@rabbit1 from cluster ...
# => ...done.
Note that rabbit1
still thinks it's clustered with rabbit2
, and trying to start it will result in an error. We will need to reset it to be able to start it again.
# on rabbit1
rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...
# => Error: inconsistent_cluster: Node rabbit@rabbit1 thinks it's clustered with node rabbit@rabbit2, but rabbit@rabbit2 disagrees
rabbitmqctl reset
# => Resetting node rabbit@rabbit1 ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...
# => ...done.
The cluster_status
command now shows all three nodes operating as independent RabbitMQ brokers:
# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
# => ...done.
# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}]
# => ...done.
# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.
Note that rabbit@rabbit2
retains the residual state of the cluster, whereas rabbit@rabbit1
and rabbit@rabbit3
are freshly initialised RabbitMQ brokers. If we want to re-initialise rabbit@rabbit2
we follow the same steps as for the other nodes:
# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit2 ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.
Besides rabbitmqctl forget_cluster_node
and the automatic cleanup of unknown nodes by some peer discovery plugins, there are no scenarios in which a RabbitMQ node will permanently remove its peer node from a cluster.
How to Reset a Node
Sometimes it may be necessary to reset a node (wipe all of its data) and later make it rejoin the cluster. Generally speaking, there are two possible scenarios: when the node is running, and when the node cannot start or won't respond to CLI tool commands e.g. due to an issue such as ERL-430.
Resetting a node will delete all of its data, cluster membership information, configured runtime parameters, users, virtual hosts and any other node data. It will also permanently remove the node from its cluster.
To reset a running and responsive node, first stop RabbitMQ on it using rabbitmqctl stop_app
and then reset it using rabbitmqctl reset
:
# on rabbit1
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit1 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit1 ...done.
In case of a non-responsive node, it must be stopped first using any means necessary.
Cluster Best Practise
配置好hostname
1.3台机器,全部修改主机名
# 修改主机名
hostnamectl set-hostname rabbit1
# 查询主机名
hostnamectl
3台主机名修改完成后,现在就有3台主机,如下:
10.206.0.8 rabbit1 rabbit1
10.206.0.9 rabbit2 rabbit2
10.206.0.16 rabbit3 rabbit3
在rabbit1上,做如下操作:
vim /etc/hosts
追加以下配置:
10.206.0.9 rabbit2 rabbit2
10.206.0.16 rabbit3 rabbit3
同样,rabbit2 和 rabbit3 这2个节点,也需要修改/etc/hosts
接下来,我们测试下,看配置是否生效了
refer to : https://www.rabbitmq.com/networking.html#dns-verify-resolution
在rabbit1上,做如下操作:
rabbitmq-diagnostics resolve_hostname rabbit2
rabbitmq-diagnostics resolve_hostname rabbit3
配置第一个node
启动第一个节点rabbit1
安装erlang 和 rabbit-server
需要打开的端口:
systemctl start firewalld.service
firewall-cmd --state
firewall-cmd --add-port=4369/tcp --add-port=5672/tcp --add-port=15672/tcp --add-port=15675/tcp --add-port=1883/tcp --add-port=25672/tcp --add-port=35672-35682/tcp --permanent
firewall-cmd --reload
添加配置文件
cd /etc/rabbitmq/
vim rabbitmq.conf
内容如下:
# 7 days
mqtt.max_session_expiry_interval_seconds = 604800
# governing the maximum number of unacknowledged messages that will be delivered
mqtt.prefetch = 10
## use DETS (disk-based) store for retained messages
mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_dets
## only used by DETS store (in milliseconds)
mqtt.retained_message_store_dets_sync_interval = 2000
第一次启动命令使用:
rabbitmq-server -detached
在rabbit1上,添加user 、virtualhost、permission等
rabbitmqctl add_user weipeng weipeng
rabbitmqctl set_permissions -p / weipeng ".*" ".*" ".*"
rabbitmqctl set_user_tags weipeng administrator
rabbitmq-plugins enable rabbitmq_management
rabbitmq-plugins enable rabbitmq_mqtt
查看给节点的信息
rabbitmqctl cluster_status
# 显示的信息,如下:
Cluster status of node rabbit@rabbit1 ...
Basics
Cluster name: rabbit@rabbit1
Total CPU cores available cluster-wide: 2
Disk Nodes
rabbit@rabbit1
Running Nodes
rabbit@rabbit1
上面的node: rabbit@rabbit1
,是很关键的,后面的rabbit2 和 rabbit3,如果想要加入到这个集群中,需要用到上面的rabbit@rabbit1
配置剩余节点
安装剩余节点
需要注意的是,这一步,只能安装,不能启动。因为必须要在启动之前,先保证.erlang.cookier文件一致
安装erlang 和 rabbit-server
需要打开的端口:
systemctl start firewalld.service
firewall-cmd --state
firewall-cmd --add-port=4369/tcp --add-port=5672/tcp --add-port=15672/tcp --add-port=15675/tcp --add-port=1883/tcp --add-port=25672/tcp --add-port=35672-35682/tcp --permanent
firewall-cmd --reload
复制.erlang.cookie文件
1.查询erlang_cookie文件的位置
在rabbit1服务器上,做如下操作:
rabbitmq-diagnostics erlang_cookie_sources
# 显示信息,如下:
Listing Erlang cookie sources used by CLI tools...
Cookie File
Effective user: rabbitmq
Effective home directory: /var/lib/rabbitmq
Cookie file path: /var/lib/rabbitmq/.erlang.cookie
Cookie file exists? true
Cookie file type: regular
Cookie file access: read
Cookie file size: 20
Cookie CLI Switch
--erlang-cookie value set? false
--erlang-cookie value length: 0
Env variable (Deprecated)
RABBITMQ_ERLANG_COOKIE value set? false
RABBITMQ_ERLANG_COOKIE value length: 0
2.复制.erlang.cookie文件,各节点保持一致
scp /var/lib/rabbitmq/.erlang.cookie root@rabbit2:/var/lib/rabbitmq/.erlang.cookie
注意复制过来的,属于root用户,需要改成rabbitmq用户
chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
ls -la
剩余节点修改配置文件
cd /etc/rabbitmq/
vim rabbitmq.conf
内容如下:
# 7 days
mqtt.max_session_expiry_interval_seconds = 604800
# governing the maximum number of unacknowledged messages that will be delivered
mqtt.prefetch = 10
## use DETS (disk-based) store for retained messages
mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_dets
## only used by DETS store (in milliseconds)
mqtt.retained_message_store_dets_sync_interval = 2000
剩余节点,加入集群
剩余节点,单独启动
rabbitmq-server -detached
rabbitmq-plugins enable rabbitmq_management
rabbitmq-plugins enable rabbitmq_mqtt
查看上面的配置文件,是否生效
rabbitmq-diagnostics environment
加入集群
# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit2 ...
rabbitmqctl join_cluster rabbit@rabbit1
# => Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.
执行以下命令,查看集群信息:
rabbitmqctl cluster_status
# 显示的信息,如下:
Cluster status of node rabbit@rabbit2 ...
Basics
Cluster name: rabbit@rabbit2
Total CPU cores available cluster-wide: 4
Disk Nodes
rabbit@rabbit1
rabbit@rabbit2
Running Nodes
rabbit@rabbit1
rabbit@rabbit2
上面的操作,是将rabbit2加入到了集群中。接下来,在rabbit3上,重复上面操作,即可,将rabbit3加入到了集群中
因为,已经在rabbit@rabbit1
上,执行了添加user 、virtualhost、permission等操作,所以,在rabbit2和rabbbit3等node上,就不需要重复执行添加user 、virtualhost、permission等操作了。
Enable Feature Flag
在15672的management ui上,启用相关的stable feature
mqtt_v5
rabbit_mqtt_qos0_queue
detailed_queues_endpoint
上面的Enable Feature,只需要在一个node对应的management ui上,启用即可。其他的node会自动同步数据,相应的feature也会自动启用,即其他的node,不需要再使用management ui,来启用相关的feature了.
set_cluster_name
https://github.com/rabbitmq/rabbitmq-server/discussions/11355
https://www.rabbitmq.com/docs/man/rabbitmqctl.8#set_cluster_name
在任意一个node上,执行以下命令:
rabbitmqctl set_cluster_name wpcluster