Запуск кластера RabbitMQ в Kubernetes

При микросервисной организации приложения существенная работа ложится на механизмы интеграционной связи микросервисов. Причем эта интеграция должна быть отказоустойчива, с высокой степенью доступности.

В наших решениях мы используем интеграцию и с помощью Kafka, и с помощью gRPC, и с помощью RabbitMQ.

В этой статье мы поделимся нашим опытом кластеризации RabbitMQ, ноды которого размещены в Kubernetes.

image

До RabbitMQ версии 3.7 его кластеризация в K8S была не очень тривиальной задачей, со множеством хаков и не очень красивых решений. В версии 3.6 использовался autocluster плагин из RabbitMQ Community. А в 3.7 появился Kubernetes Peer Discovery Backend. Он встроен плагином в базовую поставку RabbitMQ и не требует отдельной сборки и установки.

Мы опишем итоговую конфигурацию целиком, попутно комментируя происходящее.

В теории

У плагина существует репозиторий на гитхабе, в котором есть пример базового использования.
Этот пример не предназначен для Production, о чём явно указано в его описании, и более того, часть настроек в нём установлено вразрез с логикой использования в проде. Также в примере никак не упомянута персистентность хранилища, таким образом при любой нештатной ситуации наш кластер превратится в пшик.

На практике

Сейчас расскажем, с чем столкнулись сами и как установили и настроили RabbitMQ.

Опишем конфигурации всех частей RabbitMQ как сервиса в K8s. Сразу уточним, что мы устанавливали RabbitMQ в K8s как StatefulSet. На каждой ноде кластера K8s будет всегда функционировать один экземпляр RabbitMQ (одна нода в классической конфигурации кластера). Мы также установим в K8s панель управления RabbitMQ и дадим доступ до этой панели за пределы кластера.

Права и роли:

rabbitmq_rbac.yaml

--- apiVersion: v1 kind: ServiceAccount metadata:   name: rabbitmq  --- kind: Role apiVersion: rbac.authorization.k8s.io/v1beta1 metadata:   name: endpoint-reader rules: - apiGroups: [""]   resources: ["endpoints"]   verbs: ["get"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata:   name: endpoint-reader subjects: - kind: ServiceAccount   name: rabbitmq roleRef:   apiGroup: rbac.authorization.k8s.io   kind: Role   name: endpoint-reader

Права доступа для RabbitMQ взяты целиком из примера, никаких изменений в них не требуется. Создаём ServiceAccount для нашего кластера и выдаём ему права на чтение Endpoints K8s.

Персистентное хранилище:

rabbitmq_pv.yaml

kind: PersistentVolume apiVersion: v1 metadata:   name: rabbitmq-data-sigma   labels:     type: local   annotations:     volume.alpha.kubernetes.io/storage-class: rabbitmq-data-sigma spec:   storageClassName: rabbitmq-data-sigma   capacity:     storage: 10Gi   accessModes:     - ReadWriteMany   persistentVolumeReclaimPolicy: Recycle   hostPath:     path: "/opt/rabbitmq-data-sigma"

В качестве персистентного хранилища здесь мы взяли самый простой случай — hostPath (обычную папку на каждой ноде K8s), но можно использовать любой из множества типов персистентных томов, поддерживаемых в K8s.

rabbitmq_pvc.yaml

kind: PersistentVolumeClaim apiVersion: v1 metadata:   name: rabbitmq-data spec:   storageClassName: rabbitmq-data-sigma   accessModes:     - ReadWriteMany   resources:     requests:       storage: 10Gi

Создаём Volume Claim на томе, созданном в предыдущем шаге. Этот Claim затем будет использоваться в StatefulSet как хранилище постоянных данных.

Сервисы:

rabbitmq_service.yaml

kind: Service apiVersion: v1 metadata:   name: rabbitmq-internal   labels:     app: rabbitmq spec:   clusterIP: None   ports:    - name: http      protocol: TCP      port: 15672    - name: amqp      protocol: TCP      port: 5672   selector:     app: rabbitmq

Создаём внутренний headless сервис, через который будет работать Peer Discovery plugin.

rabbitmq_service_ext.yaml

kind: Service apiVersion: v1 metadata:   name: rabbitmq   labels:     app: rabbitmq     type: LoadBalancer spec:   type: NodePort   ports:    - name: http      protocol: TCP      port: 15672      targetPort: 15672      nodePort: 31673    - name: amqp      protocol: TCP      port: 5672      targetPort: 5672      nodePort: 30673   selector:     app: rabbitmq

Для работы приложений в K8s с нашим кластером создаём сервис балансировщика.

Так как нам нужен доступ к кластеру RabbitMQ снаружи K8s, прокидываем NodePort. RabbitMQ будет доступен при обращении к любой ноде кластера K8s по портам 31673 и 30673. В реальной работе большой необходимости в этом нет. Вопрос удобства пользования админкой RabbitMQ.

При создании сервиса с типом NodePort в K8s также неявно создаётся сервис с типом ClusterIP для его обслуживания. Поэтому приложения в K8s, которым нужно работать с нашим RabbitMQ, смогут обращаться к кластеру по адресу amqp://rabbitmq:5672

Конфигурация:

rabbitmq_configmap.yaml

apiVersion: v1 kind: ConfigMap metadata:   name: rabbitmq-config data:   enabled_plugins: |       [rabbitmq_management,rabbitmq_peer_discovery_k8s].    rabbitmq.conf: |       cluster_formation.peer_discovery_backend  = rabbit_peer_discovery_k8s       cluster_formation.k8s.host = kubernetes.default.svc.cluster.local       cluster_formation.k8s.port = 443       

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий