使用的环境信息如下:
Docker Engine: 18.09.5
Linux: CentOS Linux release 7.6.1810 (Core)
RabbitMQ: 3.11.2
一、安装前的环境准备
1、获取RabbitMQ的镜像
执行一下命令:
docker pull rabbitmq:3.11.2-management
镜像拉去之后,需要重新TAG后上传到Harbor中,该流程省略。上传到Harbor中的镜像为:
10.18.245.184/rabbitmq/release:3.11.2-management
2、为节点准备服务器资源
RabbitMQ集群有多种不同类型的节点,它们的行为都不太一样。当向集群中添加节点时,它必须是磁盘节点或者内存节点。磁盘节点将集群的运行时状态会同时存储在内存和磁盘上。在RabbitMQ中,运行时状态包括集群、队列、绑定、虚拟主机、用户和策略等信息的定义。鉴于此,如果集群拥有大量的运行时状态时,相比内存节点,磁盘节点更容易受到磁盘I/O问题的困扰。
内存节点仅将运行时状态信息存储在内存数据库中。
节点类型和消息持久化问题
不论是哪种节点类型都不会影响消息持久化的行为。当通过消息属性delivery-mode将一条消息标记为持久化时,这条消息会被写入磁盘,而无关乎节点类型。因此,考虑磁盘I/O对RabbitMQ集群节点的影响就显得很尤为重要。如果需要持久化消息的话,你应当提供磁盘子系统来满足集群节点中队列写入速度的要求。
节点类型与崩溃行为
当节点或者集群崩溃时,在磁盘节点启动并重新加入集群时,会被用来重建集群的运行时状态。而对于内存节点来说,当加入集群时则不会包含任何运行时状态数据。在重新加入集群时,集群中的其他节点会将诸如队列定义等信息发送给它。
当创建集群时,确保至少存在一个磁盘型节点。当集群中拥有多个磁盘节点时,就能在发生硬件故障时更加游刃有余。但多个磁盘节点在某些故障场景下是一把双刃剑。当集群中有多个节点故障时,如果其中两个磁盘节点对集群的共享状态不一致,那么你在尝试将集群恢复至先前状态时就会遇到困难。假设这事儿真的发生了,那么建议将整个集群关闭并按顺序重启节点。启动那个持有最多正确状态数据的磁盘节点,然后再添加其他节点。本章后面部分,我们将讨论故障排除与集群恢复的其他策略。
状态节点
如果你使用rabbitmq管理插件的话,那么你可以使用另一种节点类型,即统计节点(Stats node)。它只能和磁盘节点搭配使用。统计节点负责收集集群中每个节点的全部统计数据和状态数据。在任意时刻,一个集群只能有一个统计节点。对于大型集群设置来说,最佳策略是配置专门的管理节点,即主磁盘节点和统计节点,并再至少配置一个磁盘节点以提供故障转移的能力(见图7.3)。
图7.3 带有备用磁盘节点和两个内存节点的集群
根据管理API的使用频率和用途以及RabbitMQ中使用的资源数量,运行管理API可能会带来较高的CPU成本。运行专们用于管理的节点服务器可确保消息投递和统计数据收集这两之间互不影响。
在拥有两个磁盘节点的集群拓扑设置中,如果主节点发生故障的话,统计节点将会被指派给备用磁盘节点。当主磁盘节点恢复并重新加入集群后,它并不会重新获得统计节点,除非被指派为统计节点的备用节点停止运行或者离开集群。
在RabbitMQ集群的管理方面统计节点扮演着重要的角色。要是没有了RabbitMQ管理插件和统计节点的话,获取集群范围内的性能、连接、队列深度和运营问题等数据将变得十分困难。
基于以上观点,节点服务器如下:
IP | 节点类型 |
10.18.247.55 | 磁盘节点(统计节点、状态节点) |
10.18.247.53 | 磁盘节点 |
10.18.247.48 | 内存节点 |
10.18.247.46 | 内存节点 |
因为RabbitMQ节点使用短域名或全限定域名 (FQDN) 相互寻址,所以以上四台服务器都要设置如下解析。
cat >> /etc/hosts <<'EOF'
10.18.247.55 node01
10.18.247.53 node02
10.18.247.48 node03
10.18.247.46 node04
EOF
二、集群搭建
1、Erlang Cookie的配置
RabbitMQ使用Erlang內建的多节点通信机制来实现节点间的通信。Erlang和RabbitMQ进程使用共享的安全文件cookie。Erlang cookie文件包含在RabbitMQ的数据目录下。在*NIX平台上,该文件通常位于/var/lib/rabbitmq/.erlang.cookie中,不同的版本间会有所差异。cookie文件包含了一个短字符串。集群中的每个节点的这个短字符串都是相同的。如果集群中的每个节点上的cookie文件都不一样的话,那么它们之间就不能相互通信了。
在首次启动RabbitMQ时或者该文件丢失时,cookie文件会被创建出来。在设置集群时,请确保RabbitMQ没有运行,并在再次启动RabbitMQ前将共享的cookie文件覆盖原有的那个cookie文件。
PS:使用rabbitmqctl是添加和删除集群节点的简单方法。我们也可以用它来将节点在磁盘节点和内存节点间来回切换。rabbitmqctl本质上包装了Erlang应用程序。该程序负责和RabbitMQ通信。正因如此,它需要访问Erlang cookie。当以root用户身份运行命令时,rabbitmqctl知道如何找到并使用cookie文件。如果你在产品环境中使用rabbitmqctl时遇到问题,请确保你运行rabbitmqctl的用户拥有访问RabbitMQ Erlang cookie的权限,或者在home目录下存有该文件的副本。
接下来我们进行erlang.cookie的配置步骤,登录10.18.247.55,以该服务器第一次生成的Cookie为集群统一Cookie配置。
登录服务器后,拉取镜像:
docker pull 10.18.245.184/rabbitmq/release:3.11.2-management
拉取完成后执行,开始运行容器:
docker run --name rabbitmqNode01 --hostname=node01 \
-v /home/rabbitmq/data:/var/lib/rabbitmq \
--net=host \
-d 10.18.245.184/rabbitmq/release:3.11.2-management
重点参数解释如下:
- --hostname: RabbitMQ节点使用短域名或全限定域名 (FQDN) 相互寻址。因此,所有集群成员的主机名必须可以从所有集群节点以及可能使用rabbitmqctl等命令行工具的机器上解析。解析可以使用任何标准操作系统提供的方法,例如:DNS记录、主机解析文件(/etc/hosts)。docker启动方式设置之后,需要在/etc/hosts中默认添加 10.18.247.55 node01 解析记录;
- -v:挂在文件夹路径;
- --net=host:网络模式使用host。
- 5672端口:5672, 5671 (AMQP 0-9-1 without and with TLS)AMQP 是 Advanced Message Queuing Protocol 的缩写,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,专为面向消息的中间件设计。基于此协议的客户端与消息中间件之间可以传递消息,并不受客户端/中间件不同产品、不同的开发语言等条件的限制。Erlang 中的实现有 RabbitMQ 等;
- 15672端口:(if management plugin is enabled)通过
http://serverip:15672
访问 RabbitMQ 的 Web 管理界面,默认用户名密码都是 guest。(注意:RabbitMQ 3.0之前的版本默认端口是55672);- 4369端口:集群通信端口。
复制Cookie信息:
docker cp rabbitmqNode01:/var/lib/rabbitmq/.erlang.cookie /home/rabbitmq/data/.erlang.cookie
获取信息并在其他服务器节点创建Cookie信息:
cat /home/rabbitmq/data/.erlang.cookie
复制上面命令展示的内容后,依次在相关节点创建/home/rabbitmq/data/.erlang.cookie,设置相同的Cookie就可以了。
PS:如果在容器启动的状态下,修改了Cookie信息,在进入容器使用rabbitmqctl命令时候会报错,如果遇到这种情况,删除容器后,在此启动就可以了;记得docker run中--hostname的值要指定当前宿主机的ip解析配置;--name的值尽量命名为rabbiamqNode01、rabbitmqNode02、rabbitmqNode03.....。
2、容器节点加入集群
上面也说了,以10.18.247.55为统计节点,我们需要进入该容器中,来重置节点,将 RabbitMQ 节点返回到其原始状态。
# 进入第一个rabbitmq节点容器
docker exec -it rabbitmqNode01 /bin/bash
# rabbitmqctl stop 会将 Erlang 虚拟机关闭,rabbitmqctl stop_app只关闭RabbitMQ 服务
rabbitmqctl stop_app
# 重置节点,将 RabbitMQ 节点返回到其原始状态。
rabbitmqctl reset
# 只启动应用服务
rabbitmqctl start_app
# 退出容器
exit
分别进入到10.18.247.53、10.18.247.48、10.18.247.46服务器中,执行以下命令:
// 进入容器
docker exec -it rabbitmqNode02 /bin/bash
// 停止
rabbitmqctl stop_app
// 重置
rabbitmqctl reset
// 加入集群
rabbitmqctl join_cluster rabbit@node01
// 启动
rabbitmqctl start_app
// 推出容器
exit
这里对以上的操作命令进行解释:
- rabbitmqctl stop_app 不会停止RabbitMQ服务器进程本身,而是使用rabbitmq来指示RabbitMQ停止Erlang中的内部进程;
- rabbitmqctl reset 清除RabbitMQ节点的状态,包括任何运行时配置数据和状态;特别是节点之前单独运行,后来需要加入到某个集群的情况,如果不清除相关数据,可能会导致节点数据不一致等问题;
- rabbitmqctl join_cluster rabbit@node01 加入主节点集群;
- rabbitmqctl start_app 启动服务;
每执行完一台服务器之后,我们可以执行以下命令查看集群状态:
rabbitmqctl cluster_status
全部执行完成之后,我们会看到如下信息:
三、RabbitMQ Management的使用
1、设置用户
# 进入任一节点容器创建
# 创建账号
rabbitmqctl add_user admin admin123
# 设置用户角色
rabbitmqctl set_user_tags admin administrator
# 设置用户权限
rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
设置完成之后,可访问地址如下:
用户就是上面设置的用户。
四、移除节点操作
# 在要关闭的节点上操作
$ rabbitmqctl stop_app
# 在主节点上操作: 删除要移除的节点:-->例如要移除node02的操作如下
$ rabbitmqctl forget_cluster_node rabbit@node02
五、RAM(内存)节点与磁盘节点的修改
主要区别为:
- 磁盘节点:保存所有数据信息。
- 内存节点:仅在RAM中存储内部数据库表。这不包括消息、消息存储索引、队列索引和其他节点状态。
理论上俩讲,应用中使用rabbitMQ的场景都是基于数据一致性的场景;RAM节点如果出现故障,节点中的消息将会丢失(当然是非HA的场景,也就是delivery-mode设置为2时,RAM节点一样不会丢失数据,但是性能将会下降,这个根据具体的实际场景而定),目前官方的建议是最好集群中全部都是磁盘节点。
修改内存节点的方法如下:
// 停止服务
rabbitmqctl stop_app
// 修改节点类型为 RAM
rabbitmqctl change_cluster_node_type ram
// 启动服务
rabbitmqctl start_app
使用rabbitmqctl cluster_status来查看修改结果:
问题整理
1、因为某些原因调整防火墙后报错
详细错误信息如下:
docker: Error response from daemon: driver failed programming external connectivity on endpoint rabbitmqNode02 (7309c7aae79cda2135fbf44de850cce723ceec77068aaa962ed9b75369c4c09c): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 15672 -j DNAT --to-destination 172.17.0.2:15672 ! -i docker0: iptables: No chain/target/match by that name.
(exit status 1)).
导致该问题的原因如下:
容器和物理机的通信是通过内核转发实现的,具体体现为iptables里添加的nat规则。之前没有安装iptables直接搭docker也会在iptables生成关于docker的nat规则,因为系统自带了iptables,只不过没有以服务的方式启动。 重新安装iptables会覆盖掉之前的规则,导致docker运行报错。只需要重启一下docker就会重新生成iptables规则了。
解决方式如下:
// 停止docker服务
systemctl stop docker
// 保存iptables
iptables-save > /etc/sysconfig/iptables
// 启动docker服务
systemctl start docker
// 设置开机重启
systemctl enable docker
以上流程操作完成之后,可以再次执行docker run了。
2、反复要证的过程中执行rabbitmqctl stop_app报错问题
如果有反复验证的小伙伴,可能会出现,执行rabbitmqctl stop_app报错问题。大致错误信息如下:
attempted to contact: [rabbit@node01]
rabbit@node01:
* connected to epmd (port 4369) on node01
* epmd reports: node 'rabbit' not running at all
no other nodes on node01
* suggestion: start the node
Current node details:
* node name: 'rabbitmqcli-761-rabbit@node01'
* effective user's home directory: /var/lib/rabbitmq
* Erlang cookie hash: sw8BWCJdrbFzTgkPibvwug==
这是由于我们之前启动容器的时候执行命令如下:
docker run --name rabbitmqNode01 --hostname=node01 \
-v /home/rabbitmq/data:/var/lib/rabbitmq \
--net=host \
-d 10.18.245.184/rabbitmq/release:3.11.2-management
其中有一段-v /home/rabbitmq/data:/var/lib/rabbitmq的配置,在反复验证的过程中生成的数据冲突导致,执行以下命令清除该文件后,重新启动容器即可:
rm -rf /home/rabbitmq/data/*
参考《深入RabbitMQ》