Guide complet de RabbitMQ sur Docker : déploiement, cluster et haute disponibilité

1. RabbitMQ et Docker : principes fondamentaux

1.1 Pourquoi RabbitMQ ?

RabbitMQ est un courtier de messages open source qui permet une communication asynchrone entre applications. Ses atouts principaux sont :

  • Découplage : producteurs et consommateurs s'ignorent mutuellement.
  • Lissage de charge : la file d'attente absorbe les pics de trafic.
  • Asynchronisme : échanges non bloquants.
  • Fiabilité : persistance, accusés de réception, etc.

1.2 Pourquoi Docker ?

Critère Installation classique Docker
Dépendances Erlang à installer manuellement Intégré dans l'image
Temps de déploiement > 30 minutes 1 minute
Cluster Configuration complexe Extensible par commande
Reproductibilité Risque de divergences Image identique partout
Isolation Ressources partagées Conteneur isolé

1.3 Choix du tag d'image

Tag Description Usage recommandé
latest Version la plus récente, sans garantie Éviter en production
X.Y.Z-management Version stable avec interface web Production
X.Y.Z-management-alpine Version légère (Alpine) Ressources limitées
X.Y.Z Sans interface web Personnalisation poussée

Conseil : privilégier un tag fixe avec -management et de préférence alpine pour réduire la taille de l'image.

2. Préparation de l'environnement

2.1 Installation de Docker (Ubuntu 22.04)

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
docker --version

2.2 Installation de Docker Compose

sudo apt-get install docker-compose-plugin
docker compose version

2.3 Arborescence du projet

mkdir -p ~/rabbitmq-docker/{data,plugins,config,logs}
cd ~/rabbitmq-docker

data : persistance des files, plugins : plugins tiers, config : fichiers de configuration, logs : journaux.

3. Déploiement mononœud

3.1 Commande de base

docker run -d \
  --name rabbitmq \
  --hostname rabbitmq-node1 \
  -p 5672:5672 \
  -p 15672:15672 \
  -e RABBITMQ_DEFAULT_USER=admin \
  -e RABBITMQ_DEFAULT_PASS=mon_mot_de_passe \
  -v ~/rabbitmq-docker/data:/var/lib/rabbitmq \
  rabbitmq:4.1.5-management-alpine

3.2 Explication des paramètres

  • -d : mode détaché
  • --name : nom du conteneur
  • --hostname : essentiel car RabbitMQ lie ses données au hostname
  • -p 5672:5672 : port AMQP pour les applications
  • -p 15672:15672 : interface d'administration
  • RABBITMQ_DEFAULT_USER/PASS : identifiants administrateur
  • -v : montage de volume pour persister les données

3.3 Vérification

docker ps
docker logs rabbitmq
curl http://localhost:15672

3.4 Avec Docker Compose

# docker-compose.yml
version: '3.8'

services:
  rabbitmq:
    image: rabbitmq:4.1.5-management-alpine
    container_name: rabbitmq
    hostname: rabbitmq-node1
    restart: unless-stopped
    environment:
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-admin}
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS:-admin123}
      RABBITMQ_ERLANG_COOKIE: ${RABBITMQ_COOKIE:-ma_valeur_secrete}
    ports:
      - "5672:5672"
      - "15672:15672"
    volumes:
      - ./data:/var/lib/rabbitmq
      - ./logs:/var/log/rabbitmq
      - ./plugins:/plugins
    networks:
      - rabbitmq-network

networks:
  rabbitmq-network:
    driver: bridge

Lancer avec : docker compose up -d

4. Gestion des plugins : exemple des messages différés

4.1 Téléchargement du plugin

cd ~/rabbitmq-docker/plugins
wget https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v3.13.0/rabbitmq_delayed_message_exchange-3.13.0.ez

4.2 Installation dans le conteneur

Méthode volume :

docker cp rabbitmq_delayed_message_exchange-*.ez rabbitmq:/opt/rabbitmq/plugins/
docker exec rabbitmq rabbitmq-plugins enable rabbitmq_delayed_message_exchange

Méthode Dockerfile :

FROM rabbitmq:4.1.5-management-alpine
COPY plugins/rabbitmq_delayed_message_exchange-*.ez /opt/rabbitmq/plugins/
RUN rabbitmq-plugins enable --offline rabbitmq_delayed_message_exchange

4.3 Vérification

Dans l'interface web, onglet Admin → Plugins, la présence de rabbitmq_delayed_message_exchange indique que le plugin est actif.

5. Persistance et configuration

5.1 Répertoires importants

Chemin Contenu Persistance
/var/lib/rabbitmq Files, messages, état du cluster Obligatoire
/var/log/rabbitmq Journaux Recommandé
/etc/rabbitmq Fichiers de configuration Recommandé

5.2 Fichier de configuration personnalisé

# rabbitmq.conf
loopback_users.guest = false
listeners.tcp.default = 5672
tcp_listen_options.backlog = 128
heartbeat = 60
consumer_timeout = 1800000
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config

Montage : -v $(pwd)/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf

5.3 Erlang Cookie

# Afficher le cookie
docker exec rabbitmq cat /var/lib/rabbitmq/.erlang.cookie

# Tous les nœuds du cluster doivent partager le même cookie

Le fichier doit avoir les permissions 600.

5.4 Sauvegarde et restauration

# Sauvegarde des données
docker run --rm -v rabbitmq-data:/data alpine tar czf - -C /data . > rabbitmq-backup.tar.gz

# Sauvegarde des définitions (exchanges, queues, bindings)
docker exec rabbitmq rabbitmqadmin export /backup/definitions.json

6. Cluster en mode normal

6.1 Comparaison des modes

Caractéristique Mode normal Mode miroir Queue Quorum
Synchronisation Métadonnées seulement Données complètes Protocole Raft
Haute disponibilité Non Oui (basculement) Oui (basculement)
Performances Élevées Moyennes Moyennes à élevées
Complexité Faible Moyenne Faible (3.8+)
Usage recommandé Développement Production (ancienne) Production (3.8+)

6.2 Architecture du cluster normal

Les métadonnées (exchanges, bindings, vhosts) sont synchronisées, mais les données d'une file ne résident que sur le nœud qui l'a créée.

6.3 Déploiement d'un cluster à trois nœuds

Étape 1 : récupérer le cookie Erlang

docker exec rabbitmq cat /var/lib/rabbitmq/.erlang.cookie
# Exemple : PQJERKAZREMMJWYYWWVD

Étape 2 : créer les fichiers de configuration

# /tmp/rabbitmq.conf
loopback_users.guest = false
listeners.tcp.default = 5672
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = rabbit@mq1
cluster_formation.classic_config.nodes.2 = rabbit@mq2
cluster_formation.classic_config.nodes.3 = rabbit@mq3

Copier le cookie : echo "PQJERKAZREMMJWYYWWVD" > /tmp/.erlang.cookie ; chmod 600 /tmp/.erlang.cookie

Étape 3 : créer les répertoires pour chaque nœud

mkdir -p /tmp/{mq1,mq2,mq3}
cp /tmp/rabbitmq.conf /tmp/.erlang.cookie /tmp/mq1/
cp /tmp/rabbitmq.conf /tmp/.erlang.cookie /tmp/mq2/
cp /tmp/rabbitmq.conf /tmp/.erlang.cookie /tmp/mq3/

Étape 4 : réseau dédié

docker network create mq-net --driver bridge

Étape 5 : lancer les nœuds

# Nœud 1
docker run -d \
  --net mq-net \
  --name mq1 \
  --hostname mq1 \
  -v /tmp/mq1/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
  -v /tmp/mq1/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
  -e RABBITMQ_DEFAULT_USER=admin \
  -e RABBITMQ_DEFAULT_PASS=admin123 \
  -p 8081:15672 \
  -p 8071:5672 \
  rabbitmq:4.1.5-management-alpine

# Nœud 2
docker run -d \
  --net mq-net \
  --name mq2 \
  --hostname mq2 \
  -v /tmp/mq2/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
  -v /tmp/mq2/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
  -e RABBITMQ_DEFAULT_USER=admin \
  -e RABBITMQ_DEFAULT_PASS=admin123 \
  -p 8082:15672 \
  -p 8072:5672 \
  rabbitmq:4.1.5-management-alpine

# Nœud 3
docker run -d \
  --net mq-net \
  --name mq3 \
  --hostname mq3 \
  -v /tmp/mq3/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
  -v /tmp/mq3/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
  -e RABBITMQ_DEFAULT_USER=admin \
  -e RABBITMQ_DEFAULT_PASS=admin123 \
  -p 8083:15672 \
  -p 8073:5672 \
  rabbitmq:4.1.5-management-alpine

6.4 Vérification

docker exec mq1 rabbitmqctl cluster_status

L'interface web (ex: http://localhost:8081) affiche les trois nœuds.

6.5 Limitation du mode normal

Si le nœud propriétaire d'une file tombe, celle-ci devient inaccessible. Ce mode ne garantit pas la haute disponibilité.

7. Haute disponibilité avec le mode miroir

7.1 Principe

Une file miroir possède un nœud maître et un ou plusieurs nœuds esclaves (miroirs). Toutes les opérations de lecture/écriture passent par le maître, qui synchronise les données vers les miroirs. Si le maître tombe, un miroir est promu maître.

7.2 Configuration de la politique de miroir

Mode Exactly (recommandé) :

docker exec mq1 rabbitmqctl set_policy \
  ha-two \
  "^ha\." \
  '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'

Mode All :

docker exec mq1 rabbitmqctl set_policy \
  ha-all \
  "^all\." \
  '{"ha-mode":"all"}'

Mode Nodes :

docker exec mq1 rabbitmqctl set_policy \
  ha-nodes \
  "^nodes\." \
  '{"ha-mode":"nodes","ha-params":["rabbit@mq2","rabbit@mq3"]}'

7.3 Paramètres de la politique

Paramètre Description Valeur recommandée
ha-mode exactly / all / nodes exactly
ha-params Nombre de réplicas (exact) N/2+1
ha-sync-mode manual / automatic automatic
ha-promote-on-shutdown Promotion en cas d'arrêt always

7.4 Validation de la haute disponibilité

# Créer une file dont le nom correspond à la politique (ex: ha.test)
# Observer les esclaves
docker exec mq1 rabbitmqctl list_queues name slave_pids

# Arrêter le nœud maître
docker stop mq1

# Vérifier que la file est toujours accessible
docker exec mq2 rabbitmqctl list_queues

7.5 Avec Docker Compose

version: '3.8'

services:
  rabbitmq1:
    image: rabbitmq:4.1.5-management-alpine
    container_name: rabbitmq1
    hostname: rabbitmq1
    environment:
      RABBITMQ_ERLANG_COOKIE: "ma_valeur_secrete"
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin123
    volumes:
      - ./cluster-data/mq1:/var/lib/rabbitmq
      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
    ports:
      - "8081:15672"
      - "5671:5672"
    networks:
      - rabbitmq-cluster

  rabbitmq2:
    image: rabbitmq:4.1.5-management-alpine
    container_name: rabbitmq2
    hostname: rabbitmq2
    environment:
      RABBITMQ_ERLANG_COOKIE: "ma_valeur_secrete"
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin123
    volumes:
      - ./cluster-data/mq2:/var/lib/rabbitmq
      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
    ports:
      - "8082:15672"
      - "5672:5672"
    networks:
      - rabbitmq-cluster

  rabbitmq3:
    image: rabbitmq:4.1.5-management-alpine
    container_name: rabbitmq3
    hostname: rabbitmq3
    environment:
      RABBITMQ_ERLANG_COOKIE: "ma_valeur_secrete"
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin123
    volumes:
      - ./cluster-data/mq3:/var/lib/rabbitmq
      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
    ports:
      - "8083:15672"
      - "5673:5672"
    networks:
      - rabbitmq-cluster

networks:
  rabbitmq-cluster:
    driver: bridge

Appliquer la politique après démarrage :

docker exec rabbitmq1 rabbitmqctl set_policy \
  ha-policy \
  ".*" \
  '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'

8. Files Quorum : la HA moderne

8.1 Comparaison

Propriété File miroir File Quorum
Protocole GM (multidiffusion) Raft
Consistance Éventuelle Forte
Élection Non déterministe Déterministe (Raft)
Performance Haut débit Haut débit + consistence
Configuration Politique Déclarative (création)

8.2 Création d'une file Quorum

Via l'interface web : onglet Queues → type Quorum.

Via la ligne de commande :

docker exec rabbitmq1 rabbitmqadmin declare queue \
  name=quorum.queue \
  durable=true \
  queue_type=quorum

Via Spring Boot :

@Bean
public Queue quorumQueue() {
    return QueueBuilder.durable("quorum.queue")
            .quorum()
            .build();
}

8.3 Gestion des files Quorum

# État de la file
docker exec rabbitmq1 rabbitmq-queues quorum_status "quorum.queue"

# Ajouter un membre
docker exec rabbitmq1 rabbitmq-queues add_member "quorum.queue" "rabbit@mq4"

# Supprimer un membre
docker exec rabbitmq1 rabbitmq-queues delete_member "quorum.queue" "rabbit@mq4"

8.4 Bonnes pratiques

  • Nombre de réplicas par défaut : 5 (ou tous les nœuds si moins de 5).
  • Les files Quorum utilisent un journal (WAL), la consommation mémoire est stable.
  • Elles supportent les dead-letter exchanges.

9. Intégration avec Spring Boot

9.1 Configuration mononœud

# application.yml
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: admin123
    virtual-host: /
    connection-timeout: 5000

9.2 Configuration cluster

spring:
  rabbitmq:
    addresses: 192.168.1.10:5671,192.168.1.11:5672,192.168.1.12:5673
    username: admin
    password: admin123
    virtual-host: /
    cache:
      channel:
        size: 25
        checkout-timeout: 0
      connection:
        mode: CHANNEL
    connection-timeout: 5000
    requested-heartbeat: 60

9.3 Producteur et consommateur

Producteur :

@Service
public class MessageProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendMessage(String queueName, Object message) {
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("Message envoyé avec succès : {}", correlationData);
            } else {
                log.error("Échec d'envoi : {}", cause);
            }
        });
        rabbitTemplate.convertAndSend(queueName, message);
    }
    
    public void sendWithDelay(String exchange, String routingKey, Object message, long delayMs) {
        rabbitTemplate.convertAndSend(exchange, routingKey, message, msg -> {
            msg.getMessageProperties().setDelay((int) delayMs);
            return msg;
        });
    }
}

Consommateur :

@Component
@Slf4j
public class MessageConsumer {
    
    @RabbitListener(queues = "test.queue")
    public void handleMessage(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        try {
            String body = new String(message.getBody());
            log.info("Message reçu : {}", body);
            processBusiness(body);
            channel.basicAck(tag, false);
        } catch (Exception e) {
            log.error("Erreur lors du traitement", e);
            try {
                channel.basicNack(tag, false, true);
            } catch (IOException ex) {
                log.error("Impossible de rejeter le message", ex);
            }
        }
    }
}

9.4 Configuration avancée de la connection factory

@Configuration
public class RabbitMQConfig {
    
    @Value("${spring.rabbitmq.addresses}")
    private String addresses;
    
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setAddresses(addresses);
        factory.setUsername("admin");
        factory.setPassword("admin123");
        factory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
        factory.setPublisherReturns(true);
        factory.setChannelCacheSize(25);
        return factory;
    }
    
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMandatory(true);
        template.setReturnsCallback(returned -> {
            log.warn("Message non routé : {} -> {}", returned.getMessage(), returned.getReplyText());
        });
        return template;
    }
}

10. Dépannage

10.1 Conteneur qui s'arrête immédiatement

docker logs rabbitmq

Cause fréquente : permissions du fichier Erlang Cookie. Solution : chmod 600 /chemin/.erlang.cookie.

10.2 Interface web inaccessible

  • Vérifier l'image : docker inspect rabbitmq | grep Image (doit contenir management).
  • Vérifier les ports : docker port rabbitmq.
  • Ouvrir le pare-feu : sudo ufw allow 15672.
  • Test interne : docker exec rabbitmq rabbitmq-diagnostics status.

10.3 Échec de l'ajout d'un nœud au cluster

  • Cookie identique sur tous les nœuds.
  • Résolution de nom : docker exec mq2 ping mq1.
  • Réinitialisation :
docker exec mq2 rabbitmqctl stop_app
docker exec mq2 rabbitmqctl reset
docker exec mq2 rabbitmqctl join_cluster rabbit@mq1
docker exec mq2 rabbitmqctl start_app

10.4 Mémoire excessive

docker exec rabbitmq rabbitmq-diagnostics memory_breakdown
docker exec rabbitmq rabbitmqctl set_vm_memory_high_watermark 0.4
docker exec rabbitmq rabbitmqctl set_vm_memory_high_watermark_paging_ratio 0.5

10.5 Accumulation de messages

docker exec rabbitmq rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers

Si consumers = 0, vérifier les connexions ; si messages_unacknowledged est élevé, examiner la logique du consommateur.

11. Bonnes pratiques en production

11.1 Ressources recommandées

Environnement CPU RAM Disque
Développement 1 cœur 512 Mo 10 Go
Petite production 2 cœurs 4 Go 50 Go SSD
Grosse production 4+ cœurs 8+ Go 100+ Go SSD

11.2 Limitation des ressources Docker

docker run -d \
  --name rabbitmq \
  --cpus=2 \
  --memory=4g \
  --memory-swap=6g \
  --ulimit nofile=65536:65536 \
  ...

Ou en Compose :

services:
  rabbitmq:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '1'
          memory: 2G
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

11.3 Sécurisation

# Changer le mot de passe
docker exec rabbitmq rabbitmqctl change_password admin nouveau_mot_de_passe

# Désactiver guest à distance
# (déjà dans rabbitmq.conf)
loopback_users.guest = false

# Créer un vhost dédié
docker exec rabbitmq rabbitmqctl add_vhost /app
docker exec rabbitmq rabbitmqctl set_permissions -p /app admin ".*" ".*" ".*"

# Activer TLS (certificats à monter)
docker run -d \
  --name rabbitmq \
  -v $(pwd)/certs:/etc/rabbitmq/certs \
  -e RABBITMQ_SSL_CERT_FILE=/etc/rabbitmq/certs/server.crt \
  -e RABBITMQ_SSL_KEY_FILE=/etc/rabbitmq/certs/server.key \
  -e RABBITMQ_SSL_CA_FILE=/etc/rabbitmq/certs/ca.crt \
  -e RABBITMQ_SSL_PORT=5671 \
  -p 5671:5671 \
  rabbitmq:management

11.4 Supervision

Métrique Commande Seuil d'alerte
Longueur de file rabbitmqctl list_queues name messages > 10 000
Messages non acquittés rabbitmqctl list_queues name messages_unacknowledged > 1 000
Connexions rabbitmqctl list_connections > 5 000
Utilisation mémoire rabbitmqctl status > 80%
Descripteurs de fichiers rabbitmqctl status > 80%
# Activer l'export Prometheus
docker exec rabbitmq rabbitmq-plugins enable rabbitmq_prometheus
curl http://localhost:15692/metrics

11.5 Commandes d'administration courantes

# Cluster
rabbitmqctl cluster_status
rabbitmqctl stop
rabbitmqctl stop_app
rabbitmqctl start_app
rabbitmqctl reset

# Files
rabbitmqctl list_queues name messages consumers
rabbitmqctl delete_queue queue_name
rabbitmqctl purge_queue queue_name

# Utilisateurs
rabbitmqctl add_user username password
rabbitmqctl set_permissions -p vhost username ".*" ".*" ".*"
rabbitmqctl list_users

# Politiques
rabbitmqctl list_policies
rabbitmqctl clear_policy policy_name

# Diagnostic
rabbitmq-diagnostics status
rabbitmq-diagnostics check_running
rabbitmq-diagnostics environment
rabbitmq-diagnostics listeners
rabbitmq-diagnostics memory_breakdown

11.6 Sauvegarde

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

BACKUP_DIR="/backup/rabbitmq"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

docker exec rabbitmq rabbitmqadmin export $BACKUP_DIR/definitions_$DATE.json
docker exec rabbitmq rabbitmqctl list_users > $BACKUP_DIR/users_$DATE.txt

docker exec rabbitmq rabbitmqctl stop_app
docker exec rabbitmq rabbitmqctl export_definitions $BACKUP_DIR/definitions_full_$DATE.json
docker exec rabbitmq rabbitmqctl start_app

find $BACKUP_DIR -name "*.json" -mtime +7 -delete
find $BACKUP_DIR -name "*.txt" -mtime +7 -delete
echo "Sauvegarde terminée : $BACKUP_DIR/definitions_$DATE.json"

Étiquettes: rabbitmq Docker Cluster mirror-queue quorum-queue

Publié le 5 juillet à 19h45