Aller au contenu principal

ADR-005 : Architecture événementielle — Conception des topics Kafka

Métadonnées

ChampValeur
StatutAccepté
Date2026-02-06
LinearN/A (Fondamental)

Contexte

La plateforme STOA nécessite une communication asynchrone entre ses composants :

  • L'API Control Plane publie des événements lors des modifications de ressources
  • AWX/Ansible consomme les demandes de déploiement et remonte les résultats
  • Le MCP Gateway synchronise les catalogues d'outils en fonction des événements GitOps
  • Le service d'audit ingère toutes les actions à des fins de conformité
  • L'observabilité suit la santé des gateways et les dérives

Le problème

« Comment permettre un couplage lâche entre services tout en maintenant cohérence et auditabilité ? »

Les appels REST synchrones traditionnels créent un couplage fort et des pannes en cascade. STOA nécessite une architecture événementielle qui :

  • Découple les producteurs des consommateurs
  • Permet le rejeu et la récupération
  • Supporte l'isolation multi-tenant
  • Fournit une piste d'audit

Décision

Adopter Apache Kafka (compatible Redpanda) comme colonne vertébrale événementielle avec une structure de topics standardisée et un format d'enveloppe d'événement.

Schéma d'enveloppe d'événement

Tous les événements suivent une enveloppe cohérente :

{
"id": "uuid-v4",
"type": "nom-type-evenement",
"tenant_id": "identifiant-tenant",
"timestamp": "2026-02-06T10:30:00.000Z",
"user_id": "utilisateur-declencheur",
"payload": {
// Données spécifiques à l'événement
}
}

Inventaire des topics

TopicTypes d'événementsProducteurConsommateur
api-eventsapi-created, api-updated, api-deletedAPI Control PlaneMCP Gateway, Audit
deploy-requestsdeploy-requestAPI Control PlaneAWX
deploy-resultsdeploy-success, deploy-failureAWXAPI Control Plane
app-eventsapp-created, app-updated, app-deletedAPI Control PlaneGateway, Audit
tenant-eventstenant-created, tenant-updated, tenant-deletedAPI Control PlaneKeycloak, Audit
audit-logauditTous les servicesService Audit, OpenSearch
mcp-server-eventsmcp-server-registered, mcp-server-updatedAPI Control PlaneMCP Gateway
mcp-sync-requestssync-requestConsole UIMCP Gateway
mcp-sync-resultssync-success, sync-failureMCP GatewayAPI Control Plane
gateway-sync-requestssync-requestAPI Control PlaneAdaptateur Gateway
gateway-sync-resultssync-success, sync-failureAdaptateur GatewayAPI Control Plane
gateway-eventshealth-changed, drift-detected, reconciledAdaptateur GatewayObservabilité

Architecture

┌──────────────────────────────────────────────────────────────────────┐
│ PRODUCTEURS │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Control Plane│ │ Console │ │ AWX │ │
│ │ API │ │ UI │ │ (Ansible) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼───────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ CLUSTER KAFKA / REDPANDA │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ api-events │ │deploy-req. │ │ audit-log │ │gateway-sync │ │
│ │ (3 partns) │ │ (3 partns) │ │ (6 partns) │ │ (3 partns) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ app-events │ │tenant-event │ │mcp-server │ │mcp-sync-* │ │
│ │ (3 partns) │ │ (3 partns) │ │ events │ │ req/results │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ CONSOMMATEURS │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ MCP Gateway │ │ Service Audit│ │ Observabilité│ │
│ │ (registre │ │ (OpenSearch) │ │ (Grafana) │ │
│ │ d'outils) │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

Stratégie de partitionnement

Les événements sont partitionnés par tenant_id pour garantir :

  1. Ordonnancement — Les événements du même tenant arrivent dans l'ordre
  2. Parallélisme — Les différents tenants sont traités en parallèle
  3. Isolation — Les groupes de consommateurs peuvent filtrer par tenant
async def publish(self, topic: str, event_type: str, tenant_id: str, payload: dict):
event = self._create_event(event_type, tenant_id, payload)
partition_key = tenant_id # Partition par tenant

future = self._producer.send(topic, value=event, key=partition_key)
future.get(timeout=10) # Attendre l'acquittement

Groupes de consommateurs

Groupe de consommateursTopicsObjectif
mcp-gateway-syncapi-events, mcp-server-eventsMises à jour du registre d'outils
awx-workerdeploy-requestsTraitement des jobs de déploiement
audit-ingesteraudit-log, api-events, app-eventsIndexation OpenSearch
gateway-orchestratorgateway-sync-requestsRéconciliation gateway
observability-collectorgateway-eventsMétriques/alertes

Exemples de flux d'événements

Flux de création d'API

Flux de déploiement

Résilience des connexions

Les connexions Kafka incluent une logique de retry :

async def connect(self):
max_retries = 5
retry_delay = 2

for attempt in range(max_retries):
try:
self._producer = KafkaProducer(
bootstrap_servers=settings.KAFKA_BOOTSTRAP_SERVERS.split(","),
acks="all", # Attendre tous les réplicas
retries=3,
request_timeout_ms=10000,
)
return
except Exception as e:
if attempt < max_retries - 1:
logger.warning(f"Tentative {attempt + 1} échouée, nouvelle tentative...")
time.sleep(retry_delay)
else:
raise

Compatibilité Redpanda

STOA utilise Redpanda comme alternative compatible Kafka :

FonctionnalitéKafkaRedpanda
ProtocoleKafka 2.xCompatible
JVMRequisAucun (C++)
ZooKeeperRequis (< 3.x)Aucun
Nœud uniqueComplexeSimple
PerformancesBonnesMeilleure latence

Configuration :

# docker-compose.yml
services:
redpanda:
image: redpandadata/redpanda:latest
command:
- redpanda start
- --kafka-addr 0.0.0.0:9092
- --advertise-kafka-addr redpanda:9092

Configuration des topics

Paramètres recommandés par catégorie de topic :

CatégoriePartitionsRétentionRéplication
Événements (api, app, tenant)37 jours3
Requêtes (deploy, sync)31 jour3
Résultats31 jour3
Audit690 jours3
Santé gateway31 jour3

Conséquences

Positives

  • Couplage lâche — Les services communiquent via des événements, pas par appels directs
  • Fiabilité — Kafka persiste les événements ; les consommateurs peuvent les rejouer
  • Scalabilité — Les partitions permettent un traitement parallèle
  • Auditabilité — Tous les événements sont enregistrés à des fins de conformité
  • Observabilité — Les flux d'événements alimentent les tableaux de bord de surveillance

Négatives

  • Cohérence éventuelle — L'UI peut afficher brièvement des données périmées
  • Surcoût opérationnel — Le cluster Kafka nécessite une administration
  • Ordonnancement des messages — Garanti uniquement au sein d'une partition
  • Complexité de débogage — Des traces distribuées sont nécessaires

Atténuations

DéfiAtténuation
Cohérence éventuelleMises à jour optimistes de l'UI + polling
OpérationsRedpanda géré ou Confluent Cloud
OrdonnancementPartition par tenant_id
DébogagePropagation de trace OpenTelemetry

Références


Standard Marchemalo : Un architecte vétéran de 40 ans comprend en 30 secondes