你好,我是看山。
这是一次比较苦逼的运维,完全不熟悉的系统、不清楚环境、不清楚配置,两眼一抹黑。为啥?就是因为原来的负责人撤了、交接人休假、再次交接人也休假,再再次交接人只有一份不全的文档。而我是再、再、再次交接人,连文档也没有。更要命的是,这是生产环境,大家都懂得,生产环境就是不能出问题,自封一个“奉命于危难之间”吧。
抱怨了一整段了,还是简单的说下这次运维吧,运维的是 RabbitMQ 集群,3 个节点 A、B、C,每个节点上启动了 2 个实例 a1/a2、b1/b2、c1/c2,其中 a1、b1、c1 组成一套集群环境 rabbit cluster1,a1 是磁盘节点;a2、b2、c2 组成一套集群环境 rabbit cluster2,c2 是磁盘节点。
就是因为完全不熟悉 RabbitMQ 集群,所以基本上趟了一堆的坑,碰到了一堆不应该出现的错误,也算是新手村长经验了。按照套路,这里先说说正确的启动方式,然后再说说碰到的异常。
1. 正确启动
1.1 启动各个节点
因为找不到自己启动的历史了,就是摘取 官网 的部分内容放在这里。有 3 个服务器节点,hostname 分别是:rabbit1、rabbit2、rabbit3。
分别在 3 个节点上启动 Rabbit MQ:
rabbit1$ rabbitmq-server -detached
rabbit2$ rabbitmq-server -detached
rabbit3$ rabbitmq-server -detached
复制代码
通过命令rabbitmq-server -detached
就可以启动 rabbit 服务。
然后在每个节点上查看集群状态:
rabbit1$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
...done.
rabbit2$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}]
...done.
rabbit3$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
...done.
复制代码
RabbitMQ 服务节点的名字是 rabbit@shorthostname,所以上面 3 个节点分别是 rabbit@rabbit1、rabbit@rabbit2、rabbit@rabbit3。需要注意的是使用rabbitmq-server
基本执行的名字都是小写的,如果是在 Windows 中使用rabbitmq-server.bat
,那节点名字就是大写的了,比如 rabbit@RABBIT1。
1.2 创建集群
这里把 rabbit@rabbit2 和 rabbit@rabbit3 加入 rabbit@rabbit1 中,也就是说 rabbit@rabbit1 是磁盘节点,其他两个都是内存节点。
先把 rabbit@rabbit2 加入到 rabbit@rabbit1 中:
rabbit2$ rabbitmqctl stop_app
Stopping node rabbit@rabbit2 ...done.
rabbit2$ rabbitmqctl join_cluster rabbit@rabbit1
Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.
rabbit2$ rabbitmqctl start_app
Starting node rabbit@rabbit2 ...done.
复制代码
如果没有报错,rabbit@rabbit2 就已经加入到 rabbit@rabbit1 中了,可以使用命令rabbitmqctl cluster_status
查看集群状态:
rabbit1$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
{running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
...done.
rabbit2$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
{running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
...done.
复制代码
通过join_cluster --ram
可以指定节点以内存节点的形式加入集群。然后在 rabbit@rabbit3 上执行相同的操作即可,这里不再赘述。
1.3 拆分集群
拆分集群实际上就是在想要从集群中删除的节点上执行reset
即可,他会通知集群中所有的节点不要再理这个节点了。
rabbit3$ rabbitmqctl stop_app
Stopping node rabbit@rabbit3 ...done.
rabbit3$ rabbitmqctl reset
Resetting node rabbit@rabbit3 ...done.
rabbit3$ rabbitmqctl start_app
Starting node rabbit@rabbit3 ...done.
复制代码
在各个节点上查看集群状态:
rabbit1$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
{running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
...done.
rabbit2$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
{running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
...done.
rabbit3$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
...done.
复制代码
还可以在某节点上删除别的节点,可以使用forget_cluster_node
来进行,这里不进行赘述,后面的一种异常中会用到这个命令。
2. 几个异常
2.1 一台机器上启动多个实例
RabbitMQ 允许在一台机器上启动多个实例,自己在这次运维中占用时间最长的就是不知道这 3 个节点上部署了两套集群,通过ps -ef|grep rabbit
命令看到有两个实例,就天真的以为是有一个没有成功关闭,所以直接把两个都kill
了。所以这里记录一下如果在一台机器上启动多个实例。
$ RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=hare rabbitmq-server -detached
$ rabbitmqctl -n hare stop_app
$ rabbitmqctl -n hare join_cluster rabbit@rabbit1
$ rabbitmqctl -n hare start_app
复制代码
执行命令需要使用-n
来指定执行命令的实例,这个是需要记住的。
2.2 Bad cookie in table definition rabbit_durable_queue
这个已经找不到错误的具体描述了,就从 Google 上找了一条,基本类似。
rabbitmqctl cluster rabbit@vmrabbita
Clustering node rabbit@vmrabbitb with [rabbit@vmrabbita] ...
Error: {unable_to_join_cluster,
[rabbit@vmrabbita],
{merge_schema_failed,
"Bad cookie in table definition rabbit_durable_queue: rabbit@vmrabbitb = {cstruct,rabbit_durable_queue,set,[],[rabbit@vmrabbitb],[],0,read_write,[],[],false,amqqueue,[name,durable,auto_delete,arguments,pid],[],[],{{1266,16863,365586},rabbit@vmrabbitb},{{2,0},[]}}, rabbit@vmrabbita = {cstruct,rabbit_durable_queue,set,[],[rabbit@vmrabbitc,rabbit@vmrabbita],[],0,read_write,[],[],false,amqqueue,[name,durable,auto_delete,arguments,pid],[],[],{{1266,14484,94839},rabbit@vmrabbita},{{4,0},{rabbit@vmrabbitc,{1266,16151,144423}}}}\n"}}
复制代码
主要的就是Bad cookie in table definition rabbit_durable_queue
这句,这是因为节点之间是通过“the Erlang Cookie”彼此识别的,存储在 $HOME/.erlang.cookie 中。如果因为某种原因,集群中几个节点服务器上的 cookie 不一致,就会不能够彼此识别,出现这样那样的错误。更多的是上面的这个"Bad cookie。",还会有"Connection attempt from disallowed node"、 "Could not auto-cluster"。
2.3 already_member
这个问题就是比较 2 的一个问题了,自己给自己挖的坑,只能自己填了。集群几个节点之间不能通信,然后我就把一个内存节点的var/lib/rabbitmq/mnesia
中的文件夹删了,然后又执行了reset
,当执行join_cluster
命令的时候,就会报出错误:
Error: {ok,already_member}
复制代码
分析一下可以明白,当前节点上没有待加入集群的信息,但是待加入集群中已经有了该节点的信息,但是发现两个信息不一致。所以当当前节点期望加入到集群的时候,出于安全考虑,集群就说你小子已经是集群里的一员了,不要再加了。扒出日志来看:
=INFO REPORT==== 25-Jul-2016::20:11:10 ===
Error description:
{could_not_start,rabbitmq_management,
{{shutdown,
{failed_to_start_child,rabbit_mgmt_sup,
{'EXIT',
{{shutdown,
[{{already_started,<23251.1658.0>},
{child,undefined,rabbit_mgmt_db,
{rabbit_mgmt_db,start_link,[]},
permanent,4294967295,worker,
[rabbit_mgmt_db]}}]},
{gen_server2,call,
[<0.618.0>,{init,<0.616.0>},infinity]}}}}},
{rabbit_mgmt_app,start,[normal,[]]}}}
Log files (may contain more information):
./../var/log/rabbitmq/hare.log
./../var/log/rabbitmq/hare-sasl.log
复制代码
既然集群中已经有个该节点信息,所以不要该节点重复加入。那就把集群里该节点信息删了,再加入集群,不就应该类似与一个全新的节点加入集群一样吗?
rabbitmqctl -n hare forget_cluster_node hare@rabbit1
复制代码
这样,集群中就没有 hare@rabbit1 的信息了,之后就重新执行join_cluster
命令即可。
2.4 千万不要在磁盘节点上删除 var/lib/rabbitmq/mnesia 中的文件
这个文件夹中的内容是磁盘节点用于记录集群信息的文件,一旦删除,会出现各种各样的异常。
如果是磁盘节点,集群中配置的 Exchanges、Queues、User 等信息全都丢失
如果是内存节点,连接集群的信息丢失,重新加入集群是会失败
因为是两套集群,这两个问题我都碰到了。生产环境啊,可以想象当我正在为集群正常启动后得意的时候,突然发现所有的 Exchanges、Queues 信息全都没了的时候的心情吗?幸亏还有一套预生产环境,直接把预生产环境的内容导出,然后在生产环境导入。
唉,有种劫后余生的感动。
你好,我是看山,公众号:看山的小屋,10 年老猿,开源贡献者。游于码界,戏享人生。关注我,领取资料。
评论