Thématiques principales

jeudi 20 février 2020

RabbitMQ : Configuration

Dans l’article précédent nous avions vu rapidement le fonctionnement de RabbitMQ [1]. Nous y avions vu comment le lancer dans un container Docker et de choisir quel type de binding et d’exchange utiliser selon nos besoins.

Par contre nous n’avions pas vu comment configurer de nouveaux exchanges, de nouvelles queues ou définir de nouveaux binding. Remédions à cela!

Pour réaliser une configuration, on peut utiliser l’interface web permettant de “jouer” et faire une config et des tests rapide. Par contre, il est évident que cela manque de reproductibilité. Il faut donc une approche et quelques outils.



D’un côté pour automatiser, généralement on passe pas une interface CLI (où via une api REST, mais on ne le fera pas ici) et justement, on à de la chance car RabbitMQ propose des outils de configuration et de gestion dont entre autre l’outil “rabbitmqadmin” qui permet l’ajout, la suppression des exchanges, queues et binding.

Pour faire ce genre de chose rien de plus simple, pour créer un exchange par exemple:

1
rabbitmqadmin declare exchange --host=$RMQ_HOST -u $USER -p $PWD name=$exchange_name durable=true type=$exchange_type

Pour créer une queue:

1
rabbitmqadmin declare queue --host=$RMQ_HOST -u $USER -p $PWD name=$queue_name durable=true 

Et finalement créer un binding:

1
rabbitmqadmin declare binding --host=$RMQ_HOST -u $USER -p $PWD source=$exchange_name destination=$queue_name $args


Et la vous allez dire oui c’est cool mais autant ça peut aider de faire ca en ligne de commande mais mon rabbitmq, il est conteneurisé, du coup ca saoule d’aller se connecter dessus à la main pour aller y jouer mes commandes (surtout que rabbitmqadmin n’est plus dans les dernière version conteneurisé de RabbitMQ….).

Comment faire? et bien nous allons exploiter un pattern de conception propre à docker. Celui de lancer un conteneur (identique où non) à la durée de vie courte qui realisera l’initialisation de notre Message Broker préféré.

Ce pattern est simple il utilise un container contenant nos outils donc le scripts de démarrage est le script d’init que l’on souhaite appliquer sur un autre conteneur cible.

En utilisant dans notre cas le conteneur “softonic/rabbitmqadmin”, nous aboutissons alors à une conf docker-compose telle que:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: "3"
services:

  rabbitmq:
    container_name: rabbitmq
    image: bitnami/rabbitmq:latest
    environment:
      RABBITMQ_USERNAME: user
      RABBITMQ_PASSWORD: password
    ports:
      - "15672:15672"

  init-rabbitmq:
    container_name: init-rabbitmq
    image: softonic/rabbitmqadmin
    environment:
      RABBITMQ_USERNAME: user
      RABBITMQ_PASSWORD: password
    volumes:
      - ./:/home:Z
    depends_on:
      - rabbitmq
    command: "/home/test.init.sh"


Maintenant il reste à faire exécuter nos initialisations comme on le ferait avec script d’init classique (en gardant en tête que l’on fait ça à travers le réseau docker….) Ainsi dans notre cas, on veut construire des exchanges/queues/binding à la volée. On va donc se faire une fonction shell incluant ces trois actions simultanée:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
config_flux()
{
    local exchange_name=$1
    local queue_name=$2
    local exchange_type=$3 
    local args=""
    if [ "$4" != "" ]
    then 
      args="routing_key=$4"
    fi
    echo "Declare exchange $exchange_name with type=$exchange_type and queue $queue_name with $args"
    rabbitmqadmin declare exchange --host=$RMQ_HOST -u $USER -p $PWD name=$exchange_name durable=true type=$exchange_type
    rabbitmqadmin declare queue --host=$RMQ_HOST -u $USER -p $PWD name=$queue_name durable=true 
    rabbitmqadmin declare binding --host=$RMQ_HOST -u $USER -p $PWD source=$exchange_name destination=$queue_name $args
}


Et et…. ca marche pas! Quoi! c’est quoi ce bordel! Non bien sur que ca marche pas car on à mis une dépendance de démarrage des conteneur entre eux… mais quand le script de configuration s'exécute, rien ne dit que RabbitMQ, lui a correctement démarré où du moins qu’il a finit sa phase d’init.  Il nous faut donc nous mettre en attente de son démarrage avant de lancer la phase d’init.

Rien de plus simple, RabbitMQ comme de nombreux outils modernes intègre un healthcheck qui passe évidemment à OK quand il est prêt. On va donc ajouter une fonction au script pour interroger ce healthcheck.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
waiting_rabbitmq_started()
{
  local state="nok"
  while [ "$state" != "ok" ]
  do
    echo "http://$USER:$PWD@$RMQ_HOST:15672/api/healthchecks/node"
    state=$(expr $(curl -G "http://$USER:$PWD@$RMQ_HOST:15672/api/healthchecks/node") : ".*:.\(..\).*")
    echo "$state"
    sleep 1s
  done
}


Il ne reste alors plus qu’à appeler ces fonctions dans notre script pour définir une configuration qui s’appliquera à chaque démarrage


1
2
3
4
5
6
7
waiting_rabbitmq_started

config_flux  "myExchangeA" "myQueueA" "direct" "flux1"
config_flux  "myExchangeA" "myQueueB" "direct" "flux2"

config_flux  "myExchangeB" "myQueueA" "topic" "flux1.*"
config_flux  "myExchangeB" "myQueueB" "topic" "*.flux2"


On finit du coup tout ça en lançant notre docker-compose préféré et on regarde les logs:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ docker logs init-rabbitmq 
http://user:password@rabbitmq:15672/api/healthchecks/node
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (7) Failed to connect to rabbitmq port 15672: Connection refused
expr: syntax error: unexpected argument ‘.*:.\\(..\\).*’

[...]

http://user:password@rabbitmq:15672/api/healthchecks/node
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    15  100    15    0     0    127      0 --:--:-- --:--:-- --:--:--   127
ok
Declare exchange myExchangeA with type=direct and queue myQueueA with routing_key=flux1
exchange declared
queue declared
binding declared
Declare exchange myExchangeA with type=direct and queue myQueueB with routing_key=flux2
exchange declared
queue declared
binding declared
Declare exchange myExchangeB with type=topic and queue myQueueA with routing_key=flux1.*
exchange declared
queue declared
binding declared
Declare exchange myExchangeB with type=topic and queue myQueueB with routing_key=*.flux2
exchange declared
queue declared
binding declared


Cool, si on vérifie via l’IHM web, la conf est bien creer… Parfait. il ne reste alors plus qu’à utiliser.

Références:

[1] https://un-est-tout-et-tout-est-un.blogspot.com/2020/02/rabbitmq.html
[2] https://www.rabbitmq.com/management-cli.html
[3] https://hub.docker.com/r/softonic/rabbitmqadmin

Aucun commentaire:

Enregistrer un commentaire