/rabbitmq-on-k8s

在kubernetes集群搭建rabbitmq集群

Primary LanguageErlangApache License 2.0Apache-2.0

在Kubernetes集群里安装RabbitMQ集群

1. 准备工作

1.1 Kubernetes集群

在进行安装之前,必须保证先有一个安装好的正常工作的kubernetes集群。并且集群还应该满足以下条件:

  • kubernetes版本1.9。
  • 定义好了一个storageclass资源,并且安装好glusterfs作为文件系统。
  • kube-dns已经正常安装可以工作。

1.2 安装包

注意:

  1. 注意插件和rabbitmq server的版本对应关系。autocluster插件仅支持3.6.X版本的rabbitmq server。从3.7版本开始,使用新的插件。参考:https://github.com/rabbitmq/rabbitmq-peer-discovery-k8s。
  2. 注意rabbitmq server和支持的erlang版本对应关系。可以参考官方文档:http://www.rabbitmq.com/which-erlang.html。

参考:https://github.com/rabbitmq/rabbitmq-autocluster

1.3 制作镜像

1.3.1 为基础镜像添加依赖

​ 为了能够使用特定版本的RPM安装包来安装RabbitMQ server,需要基础镜像里能够支持rpm命令,所以选用了centos镜像(即docker.io/centos:latest)作为基础镜像。

​ 测试过程发现,centos镜像缺少安装rabbitmq-server的依赖,需要先安装依赖:

docker pull centos
docker run centos yum install -y socat logrotate
myid=$(docker ps -a | grep centos | awk '{print $1}')
docker commit -m 'add dependencies' $myid centos-base:latest

1.3.2 制作docker镜像

使用编写的Dockerfile(见附件1)制作镜像:

docker build -t my-registry:5000/rabbitmq-zj:latest ./Dockerfile
docker push my-registry:5000/rabbitmq-zj:latest

目前应该有以下目录结构:

- workdir
	- Dockerfile
	- boot.sh
	- install_rabbitmq.sh
	- files/
		- autocluster-0.10.0.ez
		- rabbitmq_aws-0.10.0.ez
		- erlang-20.1.7-1.el7.centos.x86_64.rpm
		- rabbitmq-server-3.6.13-1.el7.noarch.rpm
		- rabbitmq.config

2. 在k8s集群创建service资源

为了能够让rabbitmq对外k8s集群外和集群里其它pod提供服务,需要为它创建一个service

使用附件3的rabbitmq-service.yaml文件,在k8s集群的master节点执行以下命令创建service资源:

kubectl create -f rabbitmq-service.yaml

说明 YAML文件里ports(端口映射)配置的

  • port是容器内部RabbitMQ监听的内部端口;
  • targetPort是容器将port映射为容器对外开放的端口;

一共配置了两组端口,http表示的是rabbitmq网页监控页面访问的端口,amqp表示的是通过rabbitmq客户端访问的端口。

3. 在k8s集群创建StatefulSet资源

假设现在现在想把rabbitmq集群部署到k8s集群里指定的3个Node上。假设这3个node的名字分别为k8s-node1k8s-node2k8s-node3,那么先应该在master节点执行以下命令,为它们设置标签:

kubectl label node k8s-node1 mq=yes
kubectl label node k8s-node2 mq=yes
kubectl label node k8s-node3 mq=yes

注意rabbitmq-statefulset.yaml里的nodeSelector,它会让k8s将pod调度到上面设置过对应标签的node上。

使用附件5的rabbitmq-statefulset.yaml文件,在k8s集群的master节点执行以下命令创建statefulset资源:

kubectl create -f rabbitmq-statefulset.yaml

说明

为了配合autocluster插件实现rabbitmq节点的服务发现,需要设置一些环境变量,这些环境变量的作用参考github文档:https://github.com/rabbitmq/rabbitmq-autocluster#k8s-configuration.

volumeClaimTemplatesvolumeMounts两个标签的作用是为了将容器内的rabbitmq的数据库文件(/var/lib/rabbitmq)、日志文件目录(/var/log/rabbitmq)挂载出来,以保证pod重新调度容器被被删除后,数据目录还能保存好。

问题

  • 挂载的目录在容器内,会出现无法被rabbitmq server访问的问题。

    解决方法:因为挂载后目录的权限所有者为root:root。所以需要在image标签后面添加一个command标签,用于在容器启动时设置这两个目录的所有者权限为rabbitmq:rabbitmq

  • 在某个rabbitmq节点挂掉之后,重新加入集群,会被拒绝。

    解决方法:挂掉的节点,会被集群其它节点检测到失去连接,所以集群会把它移除。而挂掉的节点重启后,认为自己还在集群,所以尝试加入集群,然后又被拒绝。环境变量AUTOCLUSTER_CLEANUP应该设置为false,禁止自动移除跟集群失去连接的节点。

4. 增加与减少RabbitMQ节点

4.1 增加节点

可以使用类似于以下命令,进行增加rabbitmq节点:

kubectl scale statefulset rabbitmq --replicas=5

通常,增加rabbitmq节点时,也会相应增加新的主机,新加的主机在k8s集群对应的Node应该先设置好标签(如第4节所述)。比如新的主机对应的Node名称分别为k8s-node4k8s-node5,则应该先运行以下命令:

kubectl label node k8s-node4 mq=yes
kubectl label node k8s-node5 mq=yes

注意这里设置的标签与rabbitmq-statefulset.yaml文件里的nodeSelector指定的标签一致。然后,再运行k8s的scale命令进行添加rabbitmq节点。

4.2 减少节点

首先需要清楚的是,使用当前文档的方案在k8s集群里搭建的rabbitmq集群,每个mq节点对应的pod名称都是有编号的,pod名称类似于rabbitmq-0rabbitmq-1……这样的。

创建rabbitmq集群时,在k8s里对应pod的启动顺序是按名称编号从小到大顺序逐个启动。

因此,删除时会按照pod名称的编号从大到小的顺序删除。

比如,现在在k8s集群里有5个rabbitmq节点了,对应5个pod:rabbitmq-0,……,rabbitmq-4。现在想删除一个rabbitmq节点(也即删除pod rabbimq-4),可以在k8s的master节点执行以下命令:

mq_node=$(kubectl exec rabbitmq-4 -- sh -c 'echo $RABBITMQ_NODENAME')
kubectl exec rabbitmq-4 rabbitmqctl stop_app
kubectl exec rabbitmq-0 rabbitmqctl forget_cluster_node $mq_node
kubectl scale statefulset rabbitmq --replicas=4

注意 务必先在待删除的rabbitmq节点执行stop_app让它脱离rabbitmq集群,然后再执行命令将其从rabbitmq集群移除。

5. 其它问题及解决办法

5.1 出现Network partition问题

形成的原因

有时候由于网络故障或者系统挂起(suspend),集群节点之间无法通信,无法通信的双方节点都认为对方发生故障,都认为自己正常运行。等到无法通信的双方重新恢复通信时,这时候网络分区(Network partitions)就会形成。

因为双方无法通信时,双方都在独立作为集群运行,这期间它们都可能单方面地发生队列\Queue\Binding的创建删除。双方恢复通信时,双方集群的状态可能不一致,它们将继续在不同的网络分区以独立集群运行,等待人工修复。

检测是否出现网络分区

最简单的就是在web监控页面,会看到红字警告提示,网络发生分区,可能会有数据丢失。或者在某一节点运行命令rabbitmqctl cluster_status,从输出信息的partitions部分可以看到。

rabbitmqctl cluster_status
# => Cluster status of node rabbit@smacmullen ...
# => [{nodes,[{disc,[hare@smacmullen,rabbit@smacmullen]}]},
# =>  {running_nodes,[rabbit@smacmullen,hare@smacmullen]},
# =>  {partitions,[{rabbit@smacmullen,[hare@smacmullen]},
# =>               {hare@smacmullen,[rabbit@smacmullen]}]}]
# => ...done.

手动恢复

选择一个保留的分区,重启其它分区的所有节点。重启命令为:

rabbitmqctl stop_app
rabbitmqctl start_app

设置自动恢复的策略

在配置文件/etc/rabbitmq/rabbitmq.conf里设置:

# 默认为ignore模式
cluster_partition_handling=ignore
# cluster_partition_handling=pause_minority
# cluster_partition_handling=pause_if_all_down
# cluster_partition_handling=autoheal

参考官方文档:https://www.rabbitmq.com/partitions.html#automatic-handling


附件:

1. Dockerfile

Dockerfile

2. install_rabbitmq.sh

install_rabbitmq.sh

关于rabbitmq.config文件,可以参考在github上的rabbitmq server项目的docs/rabbitmq.config.example文件。地址:https://github.com/rabbitmq/rabbitmq-server。

3. boot.sh

boot.sh

4. rabbitmq-service.yaml

rabbitmq-service.yaml

5. rabbitmq-statefulset.yaml

rabbitmq-statefulset.yaml

6. 参考文档

rabbitmq server的安装文档:http://www.rabbitmq.com/install-rpm.html

官方使用autocluster插件在kubernetes集群实现集群的示例:https://github.com/rabbitmq/rabbitmq-autocluster/tree/master/examples/k8s_rbac_statefulsets

大神项目示例:https://github.com/kuberstack/kubernetes-rabbitmq-autocluster

RabbitMQ配置文件的说明:http://www.rabbitmq.com/configure.html

RabbitMQ自动化组建集群:http://www.rabbitmq.com/cluster-formation.html#peer-discovery-k8s