Создание RabbitMQ сущностей через Terragrunt командами разработки
Общая схема
RabbitMQ сущности теперь описываются централизованно в репозитории terraform.
Для stage конфигурация живет в ветке stage и применяется компонентом:
live/stage/rabbitmq
Для prod конфигурация находится в ветке master и применяется аналогичным компонентом:
live/prod/rabbitmq
Пайплан terraform сам генерирует джобы по всем файлам live/*/*/terragrunt.hcl. Если в репозитории есть компонент live/stage/rabbitmq/terragrunt.hcl, то в пайплайне появляются джобы:
validate-stage-rabbitmq
plan-stage-rabbitmq
apply-stage-rabbitmq
Для prod по той же логике появляются:
validate-prod-rabbitmq
plan-prod-rabbitmq
apply-prod-rabbitmq
validate и plan запускаются в обычном пайплайне, apply ручной, как и в остальных джобах.
Если приложению нужен готовый секрет с реквизитами доступа, после успешного apply для rabbitmq нужно отдельно запустить apply-stage-secrets или apply-prod-secrets.
Где описывать сущности
RabbitMQ сущности описываются в infrastructure.yaml внутри нужного окружения в блоке environments.<env>.rabbitmq.
Пример для stage:
environments:
stage:
rabbitmq:
management_endpoint: http://rabbitmq.rabbitmq.svc.cluster.local:15672
amqp_host: rabbitmq.rabbitmq.svc.cluster.local
amqp_port: 5672
policy:
allow_create: true
allow_delete: false
vhosts:
- name: my-service
users:
- name: my-service
permissions:
- vhost: my-service
user: my-service
configure: "^my\\.service\\..*"
write: "^my\\.service\\..*"
read: "^my\\.service\\..*"
exchanges:
- vhost: my-service
name: my.service.events
type: topic
queues:
- vhost: my-service
name: my.service.queue
arguments:
x-queue-type: quorum
bindings:
- vhost: my-service
source: my.service.events
destination: my.service.queue
destination_type: queue
routing_key: my.service.created
policies:
- vhost: my-service
name: my-service-queue-ttl
pattern: "^my\\.service\\..*"
apply_to: queues
priority: 1
definition:
message-ttl: 600000
Что поддерживается
Сейчас через terraform поддерживаются:
vhost
user
permissions
exchange
queue
binding
policy
Для пользователей пароль генерируется автоматически.
Для критичных сущностей на уровне terraform стоит защита от удаления.
Для паролей пользователей настроен ignore_changes, чтобы не было фантомных изменений и случайной ротации.
Формат конфигурации
Минимальная рабочая конфигурация обычно состоит из:
rabbitmq:
vhosts:
- name: my-service
users:
- name: my-service
permissions:
- vhost: my-service
user: my-service
configure: "^my\\.service\\..*"
write: "^my\\.service\\..*"
read: "^my\\.service\\..*"
exchanges:
- vhost: my-service
name: my.service.events
type: topic
queues:
- vhost: my-service
name: my.service.queue
arguments:
x-queue-type: quorum
bindings:
- vhost: my-service
source: my.service.events
destination: my.service.queue
destination_type: queue
routing_key: my.service.created
management_endpoint это адрес RabbitMQ Management API, через который terraform создает сущности.
amqp_host и amqp_port используются для сборки секрета приложения.
vhosts это список виртуальных хостов.
users это список пользователей RabbitMQ. Пароль для них генерируется terraform.
permissions задают шаблоны configure, write, read для пользователя внутри vhost.
exchanges описывают exchange, обычно типа topic.
queues описывают очереди. Для прикладных очередей обычно имеет смысл использовать x-queue-type: quorum.
bindings связывают exchange и очередь через routing_key.
policies позволяют задавать TTL и другие политики на очереди или exchange.
Какие сценарии поддерживаются
Полный блок со всеми секциями нужен не всегда.
Можно описывать только те сущности, которые реально нужны, но важно учитывать зависимости текущего модуля.
Только vhost
Если нужен только виртуальный хост, достаточно:
rabbitmq:
vhosts:
- name: my-service
Только пользователь
Если нужен только пользователь RabbitMQ без прав и без секрета приложения:
rabbitmq:
users:
- name: my-service
В этом случае terraform создаст пользователя и сгенерирует ему пароль, но доступа к какому-либо vhost у него еще не будет.
vhost + пользователь + права
Если приложению нужен доступ в свой vhost, но очереди и exchange пока не нужны:
rabbitmq:
vhosts:
- name: my-service
users:
- name: my-service
permissions:
- vhost: my-service
user: my-service
configure: "^my\\.service\\..*"
write: "^my\\.service\\..*"
read: "^my\\.service\\..*"
Это минимальный рабочий контур для подключения приложения к RabbitMQ со своим пользователем.
Только очередь в управляемом vhost
Если нужен только queue, то текущая реализация все равно требует, чтобы vhost был описан в этом же terraform-контуре:
rabbitmq:
vhosts:
- name: my-service
queues:
- vhost: my-service
name: my.service.queue
arguments:
x-queue-type: quorum
Только exchange в управляемом vhost
rabbitmq:
vhosts:
- name: my-service
exchanges:
- vhost: my-service
name: my.service.events
type: topic
Очередь + exchange + binding
Если нужен уже рабочий маршрут доставки сообщений:
rabbitmq:
vhosts:
- name: my-service
exchanges:
- vhost: my-service
name: my.service.events
type: topic
queues:
- vhost: my-service
name: my.service.queue
arguments:
x-queue-type: quorum
bindings:
- vhost: my-service
source: my.service.events
destination: my.service.queue
destination_type: queue
routing_key: my.service.created
Для binding в текущей реализации source должен быть описан в exchanges этого же контура.
Только policy
Если нужно навесить policy на очереди внутри управляемого vhost:
rabbitmq:
vhosts:
- name: my-service
policies:
- vhost: my-service
name: my-service-queue-ttl
pattern: "^my\\.service\\..*"
apply_to: queues
priority: 1
definition:
message-ttl: 600000
Секрет для приложения
Если приложению нужен kubernetes-секрет с реквизитами RabbitMQ, нужен не только блок secrets, но и сам rabbitmq-контур, из которого модуль возьмет данные.
Минимально для секрета должны быть:
rabbitmq:
vhosts:
- name: my-service
users:
- name: my-service
permissions:
- vhost: my-service
user: my-service
configure: "^my\\.service\\..*"
write: "^my\\.service\\..*"
read: "^my\\.service\\..*"
И отдельно в secrets:
- name: my-service-rabbitmq
namespace: my-service
type: rabbitmq
dependencies:
rabbitmq_vhost: my-service
rabbitmq_user: my-service
lifecycle:
ignore_changes: true
Без permissions такой секрет сейчас автоматически не соберется, потому что output модуля строится по связке vhost + user именно из блока permissions.
Ограничения текущей реализации
Не все комбинации сейчас работают.
Если vhost уже существует в RabbitMQ, но раньше не управлялся terraform, то просто сослаться на него из queues, exchanges, permissions или policies не получится. В текущем модуле эти сущности ссылаются на управляемый rabbitmq_vhost.this, значит такой vhost сначала нужно завести в конфиг и при необходимости отдельно адоптнуть.
Если пользователь уже существует в RabbitMQ и его пароль неизвестен, безопасный адопт такого пользователя в terraform отдельно требует инвентаризации. Для новых пользователей это ограничение не мешает, потому что пароль генерируется terraform автоматически.
Как добавить секрет для приложения
Если приложению нужен kubernetes-секрет с готовыми реквизитами доступа, его нужно добавить в infrastructure.yaml в блоке environments.<env>.secrets.
Пример:
- name: my-service-rabbitmq
namespace: my-service
type: rabbitmq
dependencies:
rabbitmq_vhost: my-service
rabbitmq_user: my-service
lifecycle:
ignore_changes: true
После этого модуль secrets создаст kubernetes-секрет с полями:
host
hostname
port
vhost
user
username
password
uri
management_endpoint
user и username дублируются специально для совместимости с разными приложениями.
Как добавить новый RabbitMQ контур
Нужно сделать MR в нужную ветку и добавить в infrastructure.yaml в блок environments.<env>.rabbitmq новый набор сущностей:
vhosts:
- name: my-service
users:
- name: my-service
permissions:
- vhost: my-service
user: my-service
configure: "^my\\.service\\..*"
write: "^my\\.service\\..*"
read: "^my\\.service\\..*"
exchanges:
- vhost: my-service
name: my.service.events
type: topic
queues:
- vhost: my-service
name: my.service.queue
bindings:
- vhost: my-service
source: my.service.events
destination: my.service.queue
destination_type: queue
routing_key: my.service.created
Если нужен секрет приложению, нужно дополнительно добавить его в environments.<env>.secrets.
После мержа в ветку создадутся джобы validate-stage-rabbitmq и plan-stage-rabbitmq для stage, либо validate-prod-rabbitmq и plan-prod-rabbitmq для prod.
После успешного plan нужно вручную запустить apply-stage-rabbitmq или apply-prod-rabbitmq.
После успешного применения rabbitmq нужно вручную запустить apply-stage-secrets или apply-prod-secrets, если добавлялся секрет для приложения.