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'administrationRABBITMQ_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"