前面我们已经在Kubernetes上部署了Redis – 《在Kubernetes上使用Sateful Set部署Redis》。 本篇我们继续把RabbitMQ也跑在K8S上。
在正式开始部署工作之前,我们先来复习一下RabbitMQ的一些基础知识。
RabbitMQ内建的集群功能可以实现其高可用,允许消费者和生产者在RabbitMQ节点崩溃的情况下继续工作,同时可以通过添加更多的节点来提高消息处理的吞吐量。
RabbitMQ内部主要包含以下四种Meta Data:
单个节点的RabbitMQ会将这些meta data保存到内存中,同时对于那些属性为持久化的信息,例如durable的Exchange、Queue等持久化到硬盘上,持久化到硬盘上的Exchange和Queue可以在RabbitMQ节点重启后被重新创建。
当以集群形式部署RabbitMQ的多个节点时,RabbitMQ集群需要新的meta data来保存集群的信息。RabbitMQ集群有以下两种模式:
接下来看一下镜像队列的声明,可以通过rabbitmqctl命令或在RabbitMQ Management WebUI中通过创建Policies的方式来声明镜像队列。例如:
rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'
上面这个命令配置了策略,所有名称以ha.开始的队列,都会在集群的所有节点上成为镜像队列。这里使用的ha模式是all,另外还有exactly, nodes两种模式,分别可以指定具体的镜像节点数量,镜像节点名称,可以参考Highly Available (Mirrored) Queues,这里不再展开。
通过上面对RabbitMQ基础知识的一个简单的回顾,在使用RabbitMQ需要考虑一下几点:
RabbitMQ提供了一个Autocluster插件,可以自动创建RabbitMQ集群。下面我们将基于RabbitMQ的官方docker镜像,添加这个autocluster插件,构建我们自己的Rabbit镜像,以便在Kubernetes上使用这个镜像。
首选需要从这里下载autocluster和rabbitmq_aws插件,我这里下载的是0.8.0的最新版本。
mkdir -p rabbitmq/plugins cd rabbitmq/plugins wget https://github.com/rabbitmq/rabbitmq-autocluster/releases/download/0.8.0/autocluster-0.8.0.ez wget https://github.com/rabbitmq/rabbitmq-autocluster/releases/download/0.8.0/rabbitmq_aws-0.8.0.ez cd ..
我的Dockerfile的内容如下:
FROM rabbitmq:3.6.11-management-alpine MAINTAINER frognew RUN apk update && apk add ca-certificates && \ apk add tzdata && \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone ADD plugins/*.ez /opt/rabbitmq/plugins/ RUN rabbitmq-plugins enable --offline autocluster
下面构建这个镜像,并将其推送到我们的私有仓库:
docker build -t harbor.frognew.com/library/rabbitmq:3.6.11 . docker push harbor.frognew.com/library/rabbitmq
接下来将以以StatefulSet部署RabbitMQ集群,我们继续使用Ceph的块存储RBD作为存储卷,将RabbitMQ的数据保存在Ceph RBD中。 这就需要对我们的Kubernetes和Ceph集群做一些准备工作,需要在Ceph中创建专门给Kubernetes使用的存储池,同时配置Kubernetes的Node节点访问Ceph,并在Kubernetes上创建StorageClass,关于这些内容不再展开,可以参考之前写的在Kubernetes上使用Sateful Set部署Redis中2.1~2.3的内容。
前面在构建RabbitMQ的Docker镜像时,我们添加了autocluster插件,这个插件基于很多种backend做服务发现自动将发现的RabbitMQ节点添加到RabbitMQ集群中,autocluster当前支持如下几种backend:
Kubernetes赫然在列,实际上当使用Kubernetes作为rabbitmq-autocluster的backend时,autocluster会通过访问Kubernetes的API Server获取RabbitMQ服务的endpoints,这样就能拿到Kubernete集群中的RabbitMQ的Pod的信息,从而可以将它们添加到RabbitMQ的集群中去。 这里也就是说要在autocluster实际上是在RabbitMQ Pod中要访问Kubernetes的APIServer。
可是然后呢?因为已经对Kubernetes的API Server启用了TLS认证,同时也为API Server起到用了RBAC,要想从Pod中访问API Server需要借助Kubernetes的Service Account。 Service Account是Kubernetes Pod中的程序用于访问Kubernetes API的Account(账号),它为Pod中的程序提供访问Kubernetes API的身份标识。下面我们创建rabbitmq Pod的ServiceAccount,并针对Kubernetes的endpoint资源做授权,创建相关的role和rolebinding。
先说明一下,假设我们的部署是在dev这个namespace下的。创建如下的rabbitmq.rbac.yaml文件:
--- apiVersion: v1 kind: ServiceAccount metadata: name: rabbitmq namespace: dev --- kind: Role apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: rabbitmq namespace: dev rules: - apiGroups: - "" resources: - endpoints verbs: - get --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: rabbitmq namespace: dev roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: dev subjects: - kind: ServiceAccount name: rabbitmq namespace: dev
在Kubernetes上创建rabbitmq这个ServiceAccount以及相关的role和rolebinding:
kubectl create -f rabbitmq.rbac.yaml
下面创建rabbitmq.statefulset.yaml文件:
--- apiVersion: v1 kind: Service metadata: name: rabbitmq-management namespace: dev labels: app: rabbitmq spec: ports: - port: 15672 name: http nodePort: 32001 - port: 5672 name: amqp nodePort: 32002 selector: app: rabbitmq type: NodePort --- apiVersion: v1 kind: Service metadata: name: rabbitmq namespace: dev labels: app: rabbitmq spec: clusterIP: None ports: - port: 5672 name: amqp selector: app: rabbitmq --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: rabbitmq namespace: dev spec: serviceName: rabbitmq replicas: 3 template: metadata: labels: app: rabbitmq spec: serviceAccountName: rabbitmq imagePullSecrets: - name: regsecret containers: - name: rabbitmq image: harbor.frognew.com/library/rabbitmq:3.6.11 imagePullPolicy: IfNotPresent resources: requests: memory: "256Mi" cpu: "150m" limits: memory: "512Mi" cpu: "250m" ports: - containerPort: 5672 name: amqp env: - name: RABBITMQ_DEFAULT_USER value: rabbituser - name: RABBITMQ_DEFAULT_PASS valueFrom: secretKeyRef: name: devsecret key: rabbitDefaultPass - name: RABBITMQ_ERLANG_COOKIE valueFrom: secretKeyRef: name: devsecret key: rabbitmqErlangCookie - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: K8S_SERVICE_NAME value: "rabbitmq" - name: RABBITMQ_USE_LONGNAME value: "true" - name: RABBITMQ_NODENAME value: "rabbit@$(MY_POD_NAME).$(K8S_SERVICE_NAME)" - name: RABBITMQ_NODE_TYPE value: disc - name: AUTOCLUSTER_TYPE value: "k8s" - name: AUTOCLUSTER_DELAY value: "10" - name: AUTOCLUSTER_CLEANUP value: "true" - name: CLEANUP_WARN_ONLY value: "false" - name: K8S_ADDRESS_TYPE value: "hostname" - name: K8S_HOSTNAME_SUFFIX value: ".$(K8S_SERVICE_NAME)" volumeMounts: - name: rabbitmq-volume mountPath: /var/lib/rabbitmq volumeClaimTemplates: - metadata: name: rabbitmq-volume spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 5Gi
为了在Kubernetes上运行RabbitMQ集群,必须保证各个RabbitMQ节点之间可以通信,也就是SatefulSet的Pod可以通信。 采用的RabbitMQ节点的命名方式为rabbit@hostdomainname的形式:
rabbit@rabbitmq-0.rabbit (rabbit@rabbitmq-0.rabbit.dev.svc.cluster.local) rabbit@rabbitmq-1.rabbit (rabbit@rabbitmq-1.rabbit.dev.svc.cluster.local) rabbit@rabbitmq-2.rabbit (rabbit@rabbitmq-2.rabbit.dev.svc.cluster.local)
可以看出采用的是长节点名的命名方式,因此设置了RABBITMQ_USE_LONGNAME为true。为了保证节点间可以通过访问rabbitmq-0.rabbit, rabbitmq-1.rabbit, rabbitmq-2.rabbit这些域名通信,必须使用Headless Service,上面rabbitmq Service的clusterIP: None这个必须设置。
在Kubernetes上创建Service和StatefulSet:
kubectl create -f rabbitmq.statefulset.yaml kubectl get statefulset rabbitmq -n dev NAME DESIRED CURRENT AGE rabbitmq 3 3 25m
最后可以在RabbitMQ Management中查看RabbitMQ的3个节点已经组成了集群:
本文转自中文社区- 在Kubernetes上使用Sateful Set部署RabbitMQ集群