Skip to content

RabbitMQ Cluster

Introduction

RabbitMQ est le message broker central d'OpenStack. En mode cluster avec queues mirrorées (ou quorum queues), il assure la haute disponibilité de la messagerie asynchrone entre services.

Prérequis

  • Concepts HA
  • Compréhension du protocole AMQP
  • Cluster de 3 controllers minimum

Points à apprendre

Architecture RabbitMQ HA

graph TB
    nova["Nova Services<br/>Conductor, Scheduler<br/>Compute agents"]
    neutron["Neutron Services<br/>Server, L3 Agent<br/>OVS Agent"]
    cinder["Cinder Services<br/>Scheduler<br/>Volume"]

    subgraph cluster["RabbitMQ Cluster"]
        node1["RabbitMQ Node 1<br/>Controller-1<br/>Disk node<br/>Queues mirrored"]
        node2["RabbitMQ Node 2<br/>Controller-2<br/>Disk node<br/>Queues mirrored"]
        node3["RabbitMQ Node 3<br/>Controller-3<br/>Disk node<br/>Queues mirrored"]
    end

    nova -->|"AMQP<br/>5672"| node1
    nova -.->|Failover| node2
    neutron -->|"AMQP<br/>5672"| node2
    cinder -->|"AMQP<br/>5672"| node3

    node1 <-->|"Erlang<br/>Distribution<br/>25672"| node2
    node2 <--> node3
    node3 <--> node1

Types de réplication

graph TB
    subgraph classic["Classic Mirrored Queues (Legacy)"]
        m1["Master Queue<br/>(Node 1)"]
        mi1["Mirror 1<br/>(Node 2)"]
        mi2["Mirror 2<br/>(Node 3)"]

        m1 -->|Sync| mi1
        m1 -->|Sync| mi2
    end

    subgraph quorum["Quorum Queues (Recommandé)"]
        l1["Leader<br/>(Node 1)"]
        f1["Follower 1<br/>(Node 2)"]
        f2["Follower 2<br/>(Node 3)"]

        l1 -->|Raft| f1
        l1 -->|Raft| f2
    end

    classic -.->|"Problèmes:<br/>- Split-brain possible<br/>- Synchronisation lente"| classic
    quorum -.->|"Avantages:<br/>- Consensus Raft<br/>- Pas de split-brain<br/>- Plus performant"| quorum

Configuration Kolla

# /etc/kolla/globals.yml

enable_rabbitmq: "yes"

# Kolla configure automatiquement le cluster
# basé sur les hosts dans le groupe [rabbitmq]

# Pour les quorum queues (OpenStack Yoga+)
# rabbitmq_enable_quorum_queues: "yes"

Configuration RabbitMQ générée

# Voir la configuration
docker exec rabbitmq cat /etc/rabbitmq/rabbitmq.conf
# /etc/rabbitmq/rabbitmq.conf

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = rabbit@controller-1
cluster_formation.classic_config.nodes.2 = rabbit@controller-2
cluster_formation.classic_config.nodes.3 = rabbit@controller-3

# Clustering
cluster_partition_handling = pause_minority

# HA Policy (mirrored queues)
# Appliquée via rabbitmqctl

# Networking
listeners.tcp.default = 5672
management.listener.port = 15672

# Erlang cookie pour l'authentification cluster
# Stocké dans /var/lib/rabbitmq/.erlang.cookie

Politique HA pour les queues

# Voir les policies actuelles
docker exec rabbitmq rabbitmqctl list_policies

# Kolla applique automatiquement cette policy:
docker exec rabbitmq rabbitmqctl set_policy ha-all ".*" \
    '{"ha-mode":"all","ha-sync-mode":"automatic"}' \
    --priority 0 \
    --apply-to queues

# Pour quorum queues (plus performant):
docker exec rabbitmq rabbitmqctl set_policy ha-all ".*" \
    '{"queue-type":"quorum"}' \
    --priority 0 \
    --apply-to queues

Monitoring du cluster

#!/bin/bash
# rabbitmq-status.sh

echo "=== Cluster Status ==="
docker exec rabbitmq rabbitmqctl cluster_status

echo -e "\n=== Node Health ==="
docker exec rabbitmq rabbitmq-diagnostics status

echo -e "\n=== Queue Status ==="
docker exec rabbitmq rabbitmqctl list_queues name messages consumers state

echo -e "\n=== Connections ==="
docker exec rabbitmq rabbitmqctl list_connections user client_properties state

echo -e "\n=== Memory Usage ==="
docker exec rabbitmq rabbitmqctl status | grep -A5 "Memory"

Gestion des partitions

# Stratégies de gestion des partitions (cluster_partition_handling):

# 1. pause_minority (recommandé pour OpenStack)
# - Les nœuds en minorité se mettent en pause
# - Évite le split-brain

# 2. pause_if_all_down
# - Pause si tous les autres nœuds sont down

# 3. autoheal
# - Redémarre automatiquement les nœuds après partition
# - Peut perdre des messages

# Vérifier s'il y a une partition
docker exec rabbitmq rabbitmqctl cluster_status | grep -A5 "Partitions"

Flux de messages

sequenceDiagram
    participant API as Nova API
    participant N1 as RabbitMQ<br/>Node 1 (Leader)
    participant N2 as RabbitMQ<br/>Node 2 (Follower)
    participant N3 as RabbitMQ<br/>Node 3 (Follower)
    participant Conductor as Nova Conductor

    API->>N1: Publish message<br/>(AMQP)
    N1->>N1: Write to queue

    alt Mirrored Queue
        N1->>N2: Sync message
        N1->>N3: Sync message
    else Quorum Queue
        N1->>N2: Raft append
        N1->>N3: Raft append
        N2-->>N1: ACK
        N3-->>N1: ACK
        N1->>N1: Commit (quorum atteint)
    end

    N1-->>API: ACK (confirmed)

    Conductor->>N1: Consume message
    N1-->>Conductor: Deliver message
    Conductor->>N1: ACK (processed)
    N1->>N1: Remove from queue

    Note over N1,N3: Si Node 1 tombe:<br/>Node 2 devient leader<br/>Messages préservés

Récupération après panne

# Cas 1: Un nœud rejoint après redémarrage
docker exec rabbitmq rabbitmqctl cluster_status
# Le nœud doit apparaître dans "running_nodes"

# Cas 2: Nœud avec état incohérent
# Forcer le reset et rejoin
docker exec rabbitmq rabbitmqctl stop_app
docker exec rabbitmq rabbitmqctl reset
docker exec rabbitmq rabbitmqctl join_cluster rabbit@controller-1
docker exec rabbitmq rabbitmqctl start_app

# Cas 3: Forcer la synchronisation des queues
docker exec rabbitmq rabbitmqctl sync_queue <queue_name>

# Cas 4: Cluster complètement down
# Identifier le dernier nœud actif
docker exec rabbitmq rabbitmqctl force_boot
docker restart rabbitmq

Configuration HAProxy pour RabbitMQ

# HAProxy balance les connexions AMQP

listen rabbitmq_management
    bind 10.0.0.10:15672
    mode http
    option httpchk GET /api/healthchecks/node
    http-check expect status 200
    server controller-1 10.0.0.11:15672 check inter 5s rise 2 fall 3
    server controller-2 10.0.0.12:15672 check inter 5s rise 2 fall 3
    server controller-3 10.0.0.13:15672 check inter 5s rise 2 fall 3

# Pour AMQP, connexion directe recommandée (pas de HAProxy)
# Les clients OpenStack utilisent transport_url avec tous les nœuds

Configuration OpenStack

# /etc/kolla/config/nova/nova.conf (et autres services)

[DEFAULT]
transport_url = rabbit://openstack:password@controller-1:5672,controller-2:5672,controller-3:5672//

# Retry settings
rabbit_retry_interval = 1
rabbit_retry_backoff = 2
rabbit_max_retries = 0
rabbit_durable_queues = true
rabbit_ha_queues = true

Exemples pratiques

Test de haute disponibilité

# Publier un message de test
docker exec rabbitmq rabbitmqadmin publish exchange=amq.default \
    routing_key=test payload="test message"

# Arrêter un nœud
ssh controller-2 "docker stop rabbitmq"

# Vérifier le cluster (doit continuer à fonctionner)
docker exec rabbitmq rabbitmqctl cluster_status

# Vérifier que les queues sont toujours accessibles
docker exec rabbitmq rabbitmqctl list_queues

# Redémarrer le nœud
ssh controller-2 "docker start rabbitmq"

# Vérifier le rejoin
sleep 30
docker exec rabbitmq rabbitmqctl cluster_status

Monitoring avancé

# API Management (JSON)
curl -u guest:guest http://10.0.0.10:15672/api/overview | jq .

# Métriques pour Prometheus
curl http://10.0.0.10:15692/metrics

# Alertes sur les queues
docker exec rabbitmq rabbitmqctl list_queues name messages consumers | \
    awk '$2 > 1000 {print "ALERT: Queue", $1, "has", $2, "messages"}'

Purge et maintenance

# Purger une queue (attention: perte de messages!)
docker exec rabbitmq rabbitmqctl purge_queue <queue_name>

# Supprimer une queue inutilisée
docker exec rabbitmq rabbitmqctl delete_queue <queue_name>

# Forcer la synchronisation de toutes les queues mirrorées
docker exec rabbitmq rabbitmqctl list_queues name slave_pids | \
    while read name slaves; do
        docker exec rabbitmq rabbitmqctl sync_queue "$name"
    done

Ressources

Checkpoint

  • Cluster RabbitMQ avec 3 nœuds fonctionnel
  • Politique HA appliquée (ha-all ou quorum)
  • Management UI accessible (port 15672)
  • Failover testé (arrêt d'un nœud)
  • Rejoin automatique après redémarrage
  • Configuration transport_url avec tous les nœuds