Перейти к содержанию

Создание 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, если добавлялся секрет для приложения.