RabbitMQ Docker集群一键部署

jopen 8年前
 

目的

- 允许消费者和生产者在RabbitMQ节点崩溃的情况下继续运行,即保证高可用。

- 通过添加更多的节点来线性扩展消息通信吞吐量,即保证性能线性可扩展。

前提

集群中的队列

在集群中创建队列,集群只会在单个节点而不是所有节点上创建完整的队列信息。所有其他非所有者节点只知道队列的元数据和指向该队列所在节点的指针。因此当集群节点崩溃时,该节点的队列相关的元数据,消息,新消息都将丢失。(注:镜像队列可同步所有信息)

为什么RabbitMQ默认不讲队列内容和状态复制到所有集群节点呢? 原因有二:

- 存储空间 如果每个集群节点都拥有所有队列的完整拷贝,那么添加新节点不会给你带来更多存储空间。

- 性能 消息的发布需要将消息复制到每一个集群节点。对于持久化消息来说,每一条消息都会触发磁盘活动。 每次新增节点,网络和磁盘负载都会增加,最终只能保持集群性能的平稳。

所以通过设置集群中的唯一节点来负责任何特定队列,只有该负责节点才会因队列消息而遭受磁盘活动的影响。所有其他节点需要将接受到的该队列的消息传递给该队列的所有者节点。因此,往RabbitMQ集群添加更多的节点意味着你将拥有更多的节点来传播队列,这些新增节点为你带来了性能的提升。

Erlang cookie

因为RabbitMQ采用Erlang开发,Erlang通过认证Erlang cookie的方式来允许节点间相互通信。所以RabbitMQ需要相互通信的节点必须使用相同的Erlang cookie.

磁盘节点 vs 内存节点

内存节点将所有元数据(队列、交换器、绑定、用户、权限、VHost)存储在内存中. 节点关闭,异常退出后,这些元数据会丢失.

磁盘节点将所有元数据存储于磁盘中。

使用内存节点可使得元数据的声明之类的操作更加快速.对元数据的存取内存节点比磁盘节点更快速.

RabbitMQ要求集群中至少有一个磁盘节点.

集群部署

使用Docker官方RabbitMQ镜像部署不能成功

直接从官方镜像创建后,加入集群提示的错误(Error: unable to connect to nodes [rabbitmq1@rabbitmq1]...), 节点无法直接与另一节点直接通讯 ,增加--link也失败。假如增加--link可行,那必须将所有集群中的节点都使用--link进行链接,这样就存在一个问题,后来新增的节点怎么办?

后来想到直接修改hosts文件,不过这种方式也比较麻烦,需要逐个修改。

最后本人在网络中找到一个使用DNS的方式,这种方式应该更加合理。本人也做了一些简单修改,从最新镜像创建容器.

使用方法:git clone项目后,只需运行其中的launch.sh脚本,即可一键创建集群,默认3个节点。也可阅读后面的步骤,逐步手工创建。

原作者jrlangford的 Shell脚本

本人修改后的 Shell脚本

步骤1. 创建DNS

docker run --name dns \
-v /var/run/docker.sock:/docker.sock \
--restart='always' \
-d \
phensley/docker-dns --domain rabbit.com

步骤2. 创建节点

shell
docker run -d \
--name=rabbitmq1 \
-p 5672:5672 \
-p 15672:15672 \
-e RABBITMQ_NODENAME=rabbitmq1 \
-e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' \
-h rabbitmq1.rabbit.com \
--dns $(docker inspect -f '{{.NetworkSettings.IPAddress}}' dns) \
--dns-search rabbit.com \
rabbitmq:3.5-management

步骤3. 加入集群

rabbitmq2 为磁盘节点,rabbitmq3 为内存节点

```shell

docker exec rabbitmq2 bash -c \

"rabbitmqctl stop_app && \

rabbitmqctl reset && \

rabbitmqctl join_cluster rabbitmq1@rabbitmq1 && \

rabbitmqctl start_app"

docker exec rabbitmq3 bash -c \

"rabbitmqctl stop_app && \

rabbitmqctl reset && \

rabbitmqctl join_cluster --ram rabbitmq1@rabbitmq1 && \

rabbitmqctl start_app"

```

步骤4. 设置集群高可用

置除了amq开头的交换器、队列为镜像队列. 生成环境不建议这种处理, 应该设置部分队列为镜像队列。

shell
docker exec rabbitmq1 rabbitmqctl set_policy HA '^(?!amq\.).*' '{"ha-mode": "all"}'

步骤5. 退出集群

shell
docker exec rabbitmq3 bash -c \
"rabbitmqctl stop_app && \
rabbitmqctl reset && \
rabbitmqctl start_app"

参考资料

> 1. RabbitMQ Doc - Clustering Guide

2. RabbitMQ Doc - Highly Available Queues

3. RabbitMQ 实战 - 第五章 集群并处理失败