Skip to main content

Command Palette

Search for a command to run...

🐇 RabbitMQ dans AKS : le messaging sans dépendance

Intégrez RabbitMQ dans AKS pour un messaging flexible, performant, résilient, et multi-cloud

Updated
12 min read
🐇 RabbitMQ dans AKS : le messaging sans dépendance

Introduction

Dans un environnement Kubernetes tel qu’AKS, les micro-services communiquent souvent via des systèmes de messaging. Azure Service Bus est une solution robuste, mais elle reste fortement liée à l’écosystème Azure. Pour des architectures plus agnostiques ou multi-cloud, RabbitMQ s’impose comme une alternative crédible.

RabbitMQ est un broker open-source, léger et performant, qui supporte plusieurs protocoles de communication. Son intégration dans AKS est facilitée par l’usage de Helm charts, permettant un déploiement rapide et configurable.

Dans mon cas, j’ai opté pour RabbitMQ afin de garantir une meilleure portabilité de nos micro-services, tout en conservant un haut niveau de fiabilité. Le déploiement dans AKS inclut des considérations de sécurité (authentification TLS, gestion des secrets), de persistance (volumes persistants) et de scalabilité (horizontal pod autoscaler).


Limitations d’Azure Service Bus dans un contexte multi-cloud ou agnostique

Azure Service Bus est une solution de messaging robuste et bien intégrée à l’écosystème Azure. Toutefois, dans des architectures orientées micro-services déployées sur AKS (Azure Kubernetes Service), et surtout dans des contextes multi-cloud ou agnostiques, certaines limitations deviennent rapidement contraignantes.

Couplage fort à Azure

Azure Service Bus repose sur des services managés et des configurations spécifiques à Azure. Cela implique un verrouillage technologique qui rend difficile la portabilité vers d’autres environnements cloud ou on-premise. Les micro-services deviennent dépendants d’un fournisseur, ce qui limite la flexibilité stratégique et technique.

Coût et flexibilité

Le modèle de tarification d’Azure Service Bus, basé sur le nombre de messages, les connexions et les opérations, peut devenir difficile à maîtriser dans des architectures à forte volumétrie. De plus, certaines fonctionnalités avancées (comme les topics ou les sessions) sont limitées à des niveaux de service premium, ce qui restreint la flexibilité pour les équipes techniques souhaitant expérimenter ou évoluer progressivement.

Limitations de personnalisation

Azure Service Bus est une solution managée, ce qui signifie que Microsoft en contrôle l’infrastructure, les mises à jour et les fonctionnalités. Si cela simplifie la maintenance, cela limite aussi les possibilités de personnalisation pour les équipes techniques qui souhaitent adapter le comportement du broker à des besoins spécifiques.

Contrairement à des solutions comme RabbitMQ ou Apache Kafka, il n’est pas possible de modifier ou d’étendre le fonctionnement interne d’Azure Service Bus.

Par exemple :

  • Impossible d’ajouter des plugins ou des extensions personnalisées.

  • Pas de gestion fine des priorités de messages ou des politiques de retry.

  • Pas de configuration avancée des dead-letter queues ou des mécanismes de routage.

      # Politique spécifique pour les queues critiques
      apiVersion: rabbitmq.com/v1beta1
      kind: Policy
      metadata:
        name: critical-queue-policy
      spec:
        name: critical-queue-policy
        pattern: "^critical\\..*"  # Queues commençant par "critical."
        applyTo: queues
        definition:
          # Configuration Dead Letter Exchange pour queues critiques
          dead-letter-exchange: deadletter.exchange
          dead-letter-routing-key: deadletter.rejected
          # TTL plus long pour les queues critiques (48 heures)
          message-ttl: 172800000
          # Plus de tentatives pour les queues critiques
          max-delivery-attempts: 5
          # Haute disponibilité
          ha-mode: exactly
          ha-params: 3
          ha-sync-mode: automatic
        priority: 10  # Priorité plus élevée que la politique générale
        rabbitmqClusterReference:
          name: rabbitmq
    

Pourquoi RabbitMQ ?

Dans une architecture de micro-services déployée sur AKS, le choix du système de messaging est stratégique. RabbitMQ s’impose comme une solution agnostique, performante et flexible, particulièrement adaptée aux environnements cloud hybrides ou multi-cloud.

Open-source, mature et largement adopté

RabbitMQ est un broker open-source basé sur Erlang, reconnu pour sa stabilité et sa robustesse. Il est utilisé dans des milliers d’organisations à travers le monde, ce qui garantit :

Une communauté active et une documentation riche, Une évolution continue sans dépendance à un fournisseur cloud, Une interopérabilité forte avec les outils DevOps et les frameworks modernes. Cette maturité en fait un choix rassurant pour les équipes techniques souhaitant une solution éprouvée et indépendante.

Support de multiples protocoles

RabbitMQ supporte plusieurs protocoles de communication, ce qui le rend particulièrement flexible :

  • AMQP 0.9.1 : le protocole natif, fiable et performant,

  • MQTT : idéal pour les architectures IoT ou les communications légères,

  • STOMP, HTTP, WebSockets : pour des cas d’usage spécifiques ou des intégrations front-end.

Cette diversité permet de connecter facilement des micro-services hétérogènes, sans imposer un protocole unique ou propriétaire.

Facilité d’intégration avec des micro-services

RabbitMQ s’intègre naturellement dans des architectures Kubernetes :

  • Déploiement via Helm charts ou opérateurs,

  • Intégration avec des frameworks comme .Net, Spring Boot, NestJS, Node.js, Go, etc.

  • Compatibilité avec des outils de monitoring comme Prometheus, Grafana, ou Datadog.

Il permet également une gestion fine des files d’attente, des stratégies de retry, du routing dynamique et des dead-letter queues, offrant une grande souplesse pour orchestrer les échanges entre micro-services.

Exemple simplifié en C

  1. Secret Kubernetes

     apiVersion: v1
     kind: Secret
     metadata:
       name: rabbitmq-credentials
       namespace: default
     type: Opaque
     stringData:
       RABBITMQ_HOST: rabbitmq.default.svc.cluster.local
       RABBITMQ_USER: myuser
       RABBITMQ_PASSWORD: mypassword
    
  2. Publisher

     using RabbitMQ.Client;
     using System;
     using System.Text;
    
     class Publisher
     {
         static void Main(string[] args)
         {
             var host = Environment.GetEnvironmentVariable("RABBITMQ_HOST");
             var user = Environment.GetEnvironmentVariable("RABBITMQ_USER");
             var password = Environment.GetEnvironmentVariable("RABBITMQ_PASSWORD");
    
             var factory = new ConnectionFactory()
             {
                 HostName = host,
                 UserName = user,
                 Password = password,
                 VirtualHost = "/",
                 Port = 5672,
                 Ssl = new SslOption
                 {
                     Enabled = false // mettre à true si TLS est activé
                 }
             };
    
             using var connection = factory.CreateConnection();
             using var channel = connection.CreateModel();
    
             channel.QueueDeclare(queue: "my-queue",
                                  durable: true,
                                  exclusive: false,
                                  autoDelete: false,
                                  arguments: null);
    
             string message = "Hello RabbitMQ!";
             var body = Encoding.UTF8.GetBytes(message);
    
             channel.BasicPublish(exchange: "",
                                  routingKey: "my-queue",
                                  basicProperties: null,
                                  body: body);
    
             Console.WriteLine($"[x] Sent: {message}");
         }
     }
    
  3. Consumer

     using RabbitMQ.Client;
     using RabbitMQ.Client.Events;
     using System;
     using System.Text;
    
     class Consumer
     {
         static void Main(string[] args)
         {
             var host = Environment.GetEnvironmentVariable("RABBITMQ_HOST");
             var user = Environment.GetEnvironmentVariable("RABBITMQ_USER");
             var password = Environment.GetEnvironmentVariable("RABBITMQ_PASSWORD");
    
             var factory = new ConnectionFactory()
             {
                 HostName = host,
                 UserName = user,
                 Password = password,
                 VirtualHost = "/",
                 Port = 5672,
                 Ssl = new SslOption
                 {
                     Enabled = false // mettre à true si TLS est activé
                 }
             };
    
             using var connection = factory.CreateConnection();
             using var channel = connection.CreateModel();
    
             channel.QueueDeclare(queue: "my-queue",
                                  durable: true,
                                  exclusive: false,
                                  autoDelete: false,
                                  arguments: null);
    
             var consumer = new EventingBasicConsumer(channel);
             consumer.Received += (model, ea) =>
             {
                 var body = ea.Body.ToArray();
                 var message = Encoding.UTF8.GetString(body);
                 Console.WriteLine($"[x] Received: {message}");
             };
    
             channel.BasicConsume(queue: "my-queue",
                                  autoAck: true,
                                  consumer: consumer);
    
             Console.WriteLine("Press [enter] to exit.");
             Console.ReadLine();
         }
     }
    
  4. Déploiement dans AKS, injectez les variables d’environnement à partir du secret :

     env:
       - name: RABBITMQ_HOST
         valueFrom:
           secretKeyRef:
             name: rabbitmq-credentials
             key: RABBITMQ_HOST
       - name: RABBITMQ_USER
         valueFrom:
           secretKeyRef:
             name: rabbitmq-credentials
             key: RABBITMQ_USER
       - name: RABBITMQ_PASSWORD
         valueFrom:
           secretKeyRef:
             name: rabbitmq-credentials
             key: RABBITMQ_PASSWORD
    

Architecture proposée dans AKS

Dans une approche agnostique du messaging, RabbitMQ s’intègre parfaitement dans un cluster AKS (Azure Kubernetes Service). L’architecture proposée repose sur un déploiement maîtrisé, sécurisé et évolutif, adapté aux environnements de production.

Déploiement de RabbitMQ via Helm

RabbitMQ peut être déployé dans AKS à l’aide du Helm chart officiel, ce qui facilite la configuration, la mise à jour et la gestion du cycle de vie.

  • Chart utilisé : bitnami/rabbitmq ou rabbitmq/rabbitmq

  • Commandes typiques :

      helm repo add bitnami https://charts.bitnami.com/bitnamihelm install rabbitmq bitnami/rabbitmq --namespace rabbitmq --create-namespace
    
  • Avantages :

    • Déploiement rapide et reproductible

    • Paramétrage via values.yaml pour la persistance, la sécurité, les ressources

    • Intégration native avec Kubernetes (services, secrets, configmaps)

Configuration des services

L’architecture repose sur une séparation claire des rôles :

  • RabbitMQ : déployé comme StatefulSet avec un service interne (ClusterIP) pour les communications internes.

  • Micro-services : communiquent avec RabbitMQ via des producers/consumers, en utilisant des bibliothèques comme RabbitMQ.Client (C#), amqplib (Node.js), ou pika (Python).

  • Service Mesh (optionnel) : Istio ou Linkerd peuvent être utilisés pour gérer les communications, la sécurité et l’observabilité.

Les échanges sont orchestrés via des queues et des exchanges configurés dynamiquement ou via des scripts d’initialisation.

Sécurité

La sécurité est assurée à plusieurs niveaux :

  • Authentification : RabbitMQ utilise des credentials stockés dans des Kubernetes Secrets.

  • Chiffrement :

    • TLS pour les connexions entre micro-services et RabbitMQ

    • Possibilité d’activer TLS entre les nœuds RabbitMQ

  • Réseau :

    • Utilisation de NetworkPolicies pour limiter les accès

    • Intégration possible avec Azure Private Link ou Azure Firewall

Persistance

RabbitMQ peut être configuré pour utiliser un PersistentVolumeClaim (PVC) afin de garantir la durabilité des messages et des métadonnées :

  • Stockage Azure Disk ou Azure Files

  • Exemple de configuration :

      ---
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: rabbitmq-azurefile-premium
      provisioner: file.csi.azure.com
      parameters:
        skuName: Premium_LRS  # available values: Standard_LRS, Premium_LRS, Standard_GRS, Standard_RAGRS
        protocol: smb # available values: smb, nfs
        resourceGroup: {{ .Values.resourceGroup }}  # e.g. myresourcegroup
        storageAccount: {{ .Values.storageAccount }}  # e.g. mystorageaccount
        server: {{ .Values.storageAccount }}.privatelink.file.core.windows.net
      reclaimPolicy: Delete
      volumeBindingMode: Immediate
      allowVolumeExpansion: true
      mountOptions:
        - dir_mode=0777
        - file_mode=0777
        - mfsymlinks
        - cache=strict  # https://linux.die.net/man/8/mount.cifs
        - nosharesock  # reduce probability of reconnect race
        - actimeo=30  # reduce latency for metadata-heavy workload
      ---
    
💡
Cela permet de redémarrer les pods sans perte de données.

Scalabilité

RabbitMQ peut être scalé horizontalement et verticalement :

  • Verticale : ajustement des ressources CPU/mémoire via les resources dans le chart Helm.

  • Horizontale :

    • Utilisation du mode cluster de RabbitMQ (avec plusieurs nœuds)

    • Déploiement en HA avec des policies de mirroring

    • Intégration avec des outils comme KEDA pour scaler les consumers en fonction du nombre de messages

        apiVersion: keda.sh/v1alpha1
        kind: ScaledObject
        metadata:
          name: rabbitmq-consumer-scaler
          namespace: default
        spec:
          scaleTargetRef:
            name: rabbitmq-consumer-deployment  # Nom du Deployment à scaler
          pollingInterval: 30                   # Intervalle de vérification (en secondes)
          cooldownPeriod: 300                   # Temps avant de réduire le nombre de pods
          minReplicaCount: 1
          maxReplicaCount: 10
          triggers:
            - type: rabbitmq
              metadata:
                queueName: my-queue             # Nom de la file RabbitMQ à surveiller
                host: RabbitMQConnection        # Nom du secret contenant la connexion
                protocol: amqp
                mode: QueueLength               # Mode de déclenchement basé sur la longueur de la file
                value: "5"                      # Seuil de messages pour déclencher le scaling
      

Performance

La performance d’un système de messaging comme RabbitMQ dépend fortement de son dimensionnement initial et de sa capacité à évoluer en fonction de la charge. Dans un environnement AKS, plusieurs leviers permettent d’optimiser les performances tout en garantissant la stabilité du système.

Facteurs influençant la performance

  1. Volume de messages

  • Taille moyenne des messages : les messages volumineux (> 1 Mo) peuvent impacter la latence.

  • Fréquence d’envoi : un pic de plusieurs milliers de messages par seconde nécessite un sizing adapté.

  1. Complexité des échanges

  • Nombre de queues et exchanges : plus ils sont nombreux, plus la charge CPU/mémoire augmente.

  • Routing dynamique : les exchanges de type topic ou headers sont plus coûteux que les direct.

  1. Mode de consommation

  • AutoAck vs ManualAck : le mode manuel permet un meilleur contrôle mais consomme plus de ressources.

  • Prefetch count : un réglage fin permet d’optimiser le débit sans surcharger les consumers.

Sizing recommandé dans AKS

  • CPU : 1 à 2 vCPU minimum par nœud RabbitMQ

  • Mémoire : 2 à 4 Go RAM par instance pour des charges moyennes

  • Stockage : Azure Disk (Premium SSD) avec persistance activée (8–32 Gi selon le volume)

Optimisations mises en œuvre

  • Activation du clustering RabbitMQ : pour répartir la charge et améliorer la tolérance aux pannes.

  • Utilisation de lazy queues : pour les files à forte volumétrie, stockées sur disque plutôt qu’en mémoire.

  • Monitoring des métriques clés : taux de delivery, latence, saturation des queues, via Datadog ou Prometheus.

  • Réglage du prefetch côté consumer : pour éviter les surcharges et optimiser le débit.

Utiliser l’outil pivotalrabbitmq/perf-test

Pour valider les capacités de traitement de RabbitMQ dans notre cluster AKS, j’ai utilisé l’outil ​https://github.com/rabbitmq/rabbitmq-perf-test​, qui permet de simuler des charges réalistes et de mesurer précisément le débit, la latence et la stabilité du système dans différents scénarios.

docker run -it --rm pivotalrabbitmq/perf-test:latest \
  --uri amqp://user:pass@host.docker.internal:5672 \
  --producers 8 --consumers 8 \
  --rate 75 --consumer-rate 75 \
  --time 180 --size 1024 \
  --queue-pattern 'scale-test-%d' \
  --queue-pattern-from 1 --queue-pattern-to 5
  • --uri : URI de connexion à RabbitMQ (peut être injectée via des variables d’environnement ou des secrets Kubernetes).

  • --producers / --consumers : nombre de producteurs et consommateurs simulés.

  • --rate : nombre de messages par seconde à produire.

  • --size : taille des messages en octets.

  • --queue : nom de la file cible.

  • --time : durée du test en secondes.


Retour d’expérience / bénéfices observés

La mise en place de RabbitMQ dans AKS a permis de répondre efficacement aux besoins de messaging dans un environnement agnostique et distribué. Voici les principaux bénéfices constatés après plusieurs mois d’exploitation en production.

Performance

Le déploiement de RabbitMQ dans AKS a démontré une excellente réactivité dans le traitement des messages :

  • Latence faible : les échanges entre micro-services sont quasi instantanés, même en cas de forte charge.

  • Débit élevé : le système a supporté plusieurs milliers de messages par seconde sans saturation, grâce à une configuration optimisée des queues et des exchanges.

  • Scalabilité maîtrisée : l’ajout de nœuds RabbitMQ dans le cluster permet d’absorber les pics de trafic sans interruption de service.

Ces performances ont été rendues possibles par l’utilisation de Persistent Volumes, de ressources Kubernetes ajustées et d’un monitoring proactif.

Monitoring

L’intégration de RabbitMQ avec Datadog a permis de mettre en place un monitoring centralisé et granulaire, adapté aux environnements distribués.

  • Métriques RabbitMQ : grâce à l’agent Datadog déployé dans le cluster AKS, les métriques clés (nombre de messages, taux d’erreur, temps de traitement, saturation des queues) sont collectées et visualisées dans des dashboards personnalisés.

  • Logs centralisés : les logs des pods RabbitMQ sont collectés via l’agent Datadog et envoyés vers la plateforme, permettant une corrélation directe entre logs et métriques. Cela facilite les analyses post-mortem et le diagnostic en temps réel.

  • Alerting intelligent : des alertes ont été configurées dans Datadog pour détecter les anomalies (latence élevée, erreurs de connexion, queues bloquées), avec envoi de notifications vers Slack ou Microsoft Teams.

  • Traces distribuées (optionnel) : pour les micro-services intégrés avec OpenTelemetry, Datadog permet de suivre les traces de bout en bout, incluant les interactions avec RabbitMQ.

Cette solution offre une visibilité complète sur le comportement du système, tout en s’intégrant naturellement dans les workflows DevOps existants.

Résilience

La résilience du système a été validée dans plusieurs scénarios :

  • Redémarrage de pods RabbitMQ sans perte de messages, grâce à la persistance activée.

  • Failover automatique entre nœuds RabbitMQ en cas de panne, avec maintien de la disponibilité.

  • Retry intelligent côté consommateurs, avec gestion des dead-letter queues pour les messages non traitables.

Ces mécanismes ont permis de garantir une haute disponibilité et une continuité de service, même en cas de défaillance partielle du cluster AKS.


Conclusion

La mise en œuvre de RabbitMQ dans AKS s’est révélée être une solution robuste, flexible et agnostique, parfaitement adaptée aux architectures micro-services modernes. Ce choix a permis de répondre aux exigences de performance, de résilience et de visibilité, tout en s’affranchissant des limitations des solutions managées comme Azure Service Bus.

Résumé des avantages

  • Performance : traitement rapide et fluide des messages, même en forte charge.

  • Scalabilité : adaptation dynamique du nombre de consumers grâce à KEDA.

  • Monitoring : visibilité complète via Datadog, avec alerting et corrélation des logs.

  • Résilience : haute disponibilité assurée par la persistance et le clustering RabbitMQ.

  • Flexibilité : support de multiples protocoles et intégration facile avec les micro-services C#.

Prochaines étapes

Dans une logique d’amélioration continue, plusieurs évolutions sont envisagées :

  • Migration du monitoring vers Prometheus & Grafana : afin de renforcer l’indépendance vis-à-vis des solutions SaaS et de centraliser les métriques dans une stack open-source.

  • Optimisation du clustering RabbitMQ : pour améliorer la tolérance aux pannes et la répartition de la charge.

  • Automatisation des dashboards et des alertes : via des templates Grafana et des règles Prometheus, pour faciliter le déploiement multi-environnement.

Ces évolutions permettront de consolider l’architecture tout en gardant une maîtrise complète de l’infrastructure et des outils de supervision.