Kafka¶
Rôle¶
Bus de messages central du pipeline de logs SOC — voir Vue d'ensemble SOC pour le flux complet et le schéma d'ensemble. Deux catégories de topics coexistent :
- 5 topics historiques (
logs-cloud,logs-firewall,logs-identity,logs-infra,logs-windows) alimentés par une source externe au périmètre IaC (non documentée) — le pont Logstash vers Elasticsearch/syslog-ng pour ces 5 topics n'est, au moment de la rédaction, toujours pas construit. logs-audit(2026-07-17) : seul topic dont le pont Logstash → ELK est fonctionnel de bout en bout — voir section dédiée ci-dessous et ELK.
Le cluster réel n'a jamais été déployé par ce projet IaC
Ce dépôt (/root/kafka, Terraform + Ansible) décrit un cluster Kafka
théorique, jamais appliqué contre les nœuds réels. Le cluster
effectivement en production (INDSOC002/003/004) a été construit
entièrement à la main par l'exploitant, diverge du code sur plusieurs
points structurels (version, chemins, service) et contient des
données réelles de production. Voir « Architecture » ci-dessous pour
le détail complet de ces divergences, et « Procédure de déploiement »
pour l'avertissement explicite à ne jamais exécuter ce playbook contre
les hôtes réels.
Architecture¶
Projet IaC local vs cluster réel — la distinction centrale de cette page¶
flowchart TD
subgraph reel["Cluster réel -- EN PRODUCTION, construit manuellement"]
direction LR
K1["kafka-node01<br/>INDSOC002 . 10.100.20.131"]
K2["kafka-node02<br/>INDSOC003 . 10.100.20.132"]
K3["kafka-node03<br/>INDSOC004 . 10.100.20.133"]
end
LocalRole["Projet /root/kafka<br/>Terraform + Ansible<br/>NE JAMAIS EXECUTER contre le cluster reel"]
LocalRole -.->|"decrit un cluster distinct,<br/>jamais applique ici"| reel
Fleet["Flotte Indio<br/>rsyslog omkafka"] -->|"topic logs-audit"| reel
ExtSrc["Source externe<br/>non documentee"] -->|"5 topics historiques"| reel
reel -->|"topic logs-audit"| LSA["Logstash audit-kafka<br/>INDSOC014-015"]
LSA -->|"data stream logs-audit"| ES[("Elasticsearch soc-indio")]
reel -.->|"5 topics, pont non construit"| Gap["pont Logstash prevu<br/>NON CONSTRUIT"]
Projet local /root/kafka (Terraform + Ansible) |
Cluster réel en production | |
|---|---|---|
| Origine | Décrit un cluster théorique — jamais appliqué contre les nœuds réels | Construit manuellement par l'exploitant (confirmé le 2026-07-16 : « ouais c'est moi qui l'ai déjà fait, garde le tel quel ») |
| Version Kafka | Confluent Platform 8.3, jar effectif 8.3.0-ccs |
4.3.1 |
| Fichier de configuration | /etc/kafka/server.properties |
/opt/kafka/config/server.properties |
| Résolution des noms de nœuds | Inventaire Ansible (INDSOC002-004) |
/etc/hosts local sur chacun des 3 nœuds (kafka-node01/02/03) — voir « Résolution DNS » ci-dessous |
| JMX | Non configuré par le rôle (l'override systemd du rôle ne fixe que le heap JVM et Restart=on-failure) |
JMX_PORT=9999 fixe (override systemd posé manuellement) |
| Données | Aucune — jamais exécuté | 5 topics historiques + logs-audit, données réelles de production (~737 000 messages mesurés sur une seule partition lors d'un audit du 2026-07-16 — total réel plus élevé) |
Le reste de cette page documente les deux en parallèle : le code tel qu'il existe dans le dépôt (Provisioning/Configuration ci-dessous), et l'état réel du cluster tel que constaté en lecture seule.
Cluster réel — topologie constatée¶
3 VM Rocky 9.6 (tmpl-rocky96-hardened), segment seg-soc :
| Nœud | IP | kafka_node_id |
|---|---|---|
| INDSOC002 | 10.100.20.131 | 1 |
| INDSOC003 | 10.100.20.132 | 2 |
| INDSOC004 | 10.100.20.133 | 3 |
- Mode KRaft (sans Zookeeper) : les 3 nœuds combinent les rôles broker +
controller (
process.roles=broker,controller). - Réplication : 3 partitions par défaut,
default.replication.factor=3,min.insync.replicas=2— tolère la perte d'un nœud sur 3. - Disque de données dédié (250 Go par défaut côté Terraform, second disque
vSphere monté sur
/var/lib/kafka), séparé du disque OS. - Historique : ce cluster a été détruit puis reconstruit le 2026-07-06, avec
migration vers le segment
seg-socréel et passage d'une installation tarball Apache Kafka à une installation par paquet RPM Confluent officiel — voir Historique des décommissionnements. Le cluster réellement en production aujourd'hui (4.3.1) a depuis divergé de cette version RPM 8.3 — reconstruit à la main par l'exploitant sans repasser par ce rôle Ansible (voir tableau ci-dessus).
Topic logs-audit¶
Reçoit les événements auditd de toute la flotte (~53 VM), relayés par
audisp-syslog (plugin auditd) puis un module omkafka rsyslog dédié — en
parallèle du forward générique existant vers syslog-ng, sans
le remplacer (deux destinations indépendantes pour le même flux
audisp-syslog, le reste du trafic syslog continue de suivre uniquement le
chemin syslog-ng). Mêmes paramètres que les 5 topics historiques (3
partitions, réplication 3, min.insync.replicas=2, rétention Kafka 3 jours
retention.ms=259200000).
Chaîne complète : auditd → audisp-syslog → rsyslog (omkafka, filtre
$programname=='audisp-syslog') → logs-audit → Logstash (parsing, 2
nœuds INDSOC014/INDSOC015, même group_id donc partitions réparties
automatiquement) → data stream Elasticsearch logs-audit (ILM 90 jours) —
voir ELK pour le
détail du pipeline Logstash et du parsing.
Piège SELinux : blocage silencieux du trafic sortant vers Kafka
Le domaine SELinux syslogd_t (rsyslogd) n'a par défaut aucune règle
pour initier une connexion TCP sortante vers un port applicatif non
répertorié dans la policy de référence (9092 tombe dans la catégorie
générique unreserved_port_t). Sans correctif, le trafic omkafka
échoue en boucle silencieuse (retry infini configuré côté rsyslog, donc
pas de perte de données, mais rien n'atteint Kafka — un consumer lag à 0
est trompeur, il signifie juste "rien de nouveau à consommer", pas "tout
va bien en amont"). Diagnostiqué via une AVC denial réelle
(ausearch -m avc, name_connect refusé, permissive=0). Corrigé par
un module de policy SELinux dédié (allow syslogd_t
unreserved_port_t:tcp_socket name_connect;, généré via audit2allow et
déployé fleet-wide via semodule -i) plutôt que le booléen nis_enabled
suggéré par l'outil (sans rapport sémantique avec Kafka). Le déblocage a
fait remonter d'un coup ~460 000 messages accumulés côté queue disque
rsyslog depuis le déploiement initial.
Résolution DNS kafka-node01/02/03¶
Le broker Kafka publie advertised.listeners=PLAINTEXT://kafka-node0N:9092
(noms internes, pas les IP nues) — tout producteur externe au cluster
(rsyslog omkafka fleet-wide, futur pont Logstash pour les 5 topics
historiques) doit donc pouvoir résoudre ces 3 noms après la connexion
bootstrap initiale. Enregistrements A créés sur le DNS AD (INDIDEN008,
kafka-node01/02/03.infra.indio → 10.100.20.131/132/133) — une IP nue
suffit pour le premier contact mais pas au-delà. Ces enregistrements DNS
servent uniquement aux clients externes : les 3 nœuds entre eux
continuent de se résoudre via leur /etc/hosts local (voir tableau
comparatif ci-dessus), jamais via ce DNS.
Procédure manuelle¶
Le cluster réellement en production n'a été construit par aucun script ni playbook — ce qui suit est une reconstitution à partir de l'état système observé (mêmes constats que le tableau comparatif ci-dessus), pas une transcription d'un historique de commandes réel : personne n'a journalisé la séquence exacte suivie par l'exploitant le 2026-07-06. L'objectif est de documenter une procédure qui reproduirait l'état constaté, en distinguant ce qui est confirmé de ce qui est déduit.
- Provisioning des 3 VM — hors de ce dépôt Terraform (jamais appliqué,
voir avertissement en tête de page) : VM
INDSOC002/003/004créées côté vCenter par l'exploitant, segmentseg-soc, mêmes caractéristiques que décrites dans « Cluster réel — topologie constatée » ci-dessus. - Installation du logiciel Kafka 4.3.1 (déduit, non confirmé
directement — reconnexion SSH impossible depuis le nœud de contrôle au
moment de la rédaction, cf. Points d'attention) : le chemin de
configuration réel (
/opt/kafka/config/server.properties) correspond à une extraction manuelle du tarball Apache Kafka officiel sous/opt/kafka— pas une installation par paquet RPM (le rôle Ansible local, qui installe le paquet Confluent, écrit lui dans/etc/kafka/, jamais utilisé sur ce cluster). Prérequis : un JRE compatible Kafka 4.x (Java 17 minimum requis par Apache Kafka 4.x ; le rôle Ansible local installejava-21-openjdk-headlesscomme référence, non confirmé identique sur le cluster réel). - Génération d'un
cluster.idKRaft partagé : un UUID généré une seule fois (kafka-storage.sh random-uuid) puis utilisé identique sur les 3 nœuds pour le formatage (kafka-storage.sh format --cluster-id <uuid> -c server.properties) — uncluster.iddifférent par nœud empêcherait le quorum KRaft de se former. server.propertiespar nœud :process.roles=broker,controller,node.idunique (1/2/3, voir table ci-dessus),controller.quorum.voterslistant les 3 nœuds sur le port controller (9093 par convention Kafka),listeners/advertised.listenerspubliant les noms interneskafka-node0N(voir « Résolution DNS » ci-dessus) plutôt que les IP nues./etc/hostslocal sur chacun des 3 nœuds, une entrée par nœud du cluster (les 3 nœuds se résolvent entre eux uniquement par ce fichier, jamais par DNS AD — voir « Résolution DNS » ci-dessus).- Service systemd + override JMX : une unité de service (nom non
confirmé — le rôle Ansible local utilise
confluent-kafka.service, mais ce cluster n'étant pas issu de ce rôle, le nom réel pourrait différer, ex.kafka.servicepour une installation tarball) et un drop-in (systemctl edit <service>) ajoutantEnvironment="JMX_PORT=9999"— c'est cette variable d'environnement qui explique le port JMX fixe constaté (voir tableau comparatif), absent du rôle Ansible local. - Ouverture firewalld : au minimum 9092/tcp (client) et 9093/tcp (controller, trafic inter-nœuds) — par analogie avec les ports standards Kafka/KRaft et avec ce que fait le rôle Ansible local pour un cluster équivalent ; non revérifié directement sur le cluster réel.
- Démarrage puis vérification du quorum avant toute création de topic :
- Création des topics (
kafka-topics.sh --create), un par flux :logs-cloud,logs-firewall,logs-identity,logs-infra,logs-windows, puis plus tardlogs-audit— mêmes paramètres pour tous (3 partitions,--replication-factor 3,--config min.insync.replicas=2;logs-auditajoute--config retention.ms=259200000, voir section dédiée ci-dessus). - Enregistrements DNS A côté AD (
kafka-node01/02/03.infra.indio→10.100.20.131/132/133) — ajoutés pour permettre aux clients externes (rsyslogomkafkafleet-wide, futur pont Logstash) de résoudre les noms publiés paradvertised.listeners, en plus du/etc/hostsinterne au cluster (voir « Résolution DNS » ci-dessus pour le détail de cette distinction).
Degré de confiance de cette reconstitution
Les étapes 1, 3 (le fait qu'un cluster.id partagé existe), 4 (le
contenu logique de server.properties), 5, 6 (l'existence d'un override
JMX_PORT=9999), 9 et 10 s'appuient sur des faits directement constatés
(fichiers, ports, comportement réel du cluster). Le mode
d'installation exact (tarball vs autre méthode), la version de Java
et le nom du service systemd sont déduits par recoupement, pas
vérifiés en direct — une inspection SSH du cluster réel (lecture seule)
permettrait de lever ces dernières incertitudes.
Provisioning Terraform¶
Fichiers : main.tf, variables.tf, versions.tf, terraform.tfvars.example.
vsphere_virtual_machine.kafkaenfor_eachsurvar.nodes(3 nœuds par défaut), 2 disques par VM (OS + disque datakafka_data_disk_gb, 250 Go par défaut).- 2 vCPU / 4096 Mo RAM par défaut, réseau
seg-soc, réservation IP via NetBox. - Mêmes providers/conventions que les autres dépôts VM (
hashicorp/vsphere,e-breuninger/netbox).
Rappel : ce Terraform n'a jamais été appliqué pour créer les VM
INDSOC002-004 réelles — celles-ci ont été construites manuellement par
l'exploitant, en dehors de tout provisioning Indio.
Configuration Ansible¶
Rôle unique kafka :
- Installe Java (
java-21-openjdk-headless, recommandation officielle Kafka 4.x), ajoute le dépôt YUM Confluent (packages.confluent.io) et installe uniquement le paquetconfluent-kafka(broker seul, ~125 Mo — pas le méta-paquetconfluent-communityqui embarque en plus ksqlDB/Schema Registry/REST proxy, +700 Mo inutiles). - Confluent Platform 8.3 (bundle Apache Kafka 4.3, jar effectif
8.3.0-ccs), branche KRaft uniquement (Zookeeper totalement retiré). - Partitionne/monte le disque data dédié en XFS, génère
server.properties(template calculantcontroller.quorum.votersdepuis l'inventaire), formate le répertoire KRaft une seule fois (kafka-storage format,cluster.idfixe partagé par les 3 nœuds), ouvre les ports 9092 (client) et 9093 (controller) dans firewalld, démarreconfluent-kafka.service. - Override systemd (
confluent-kafka-override.conf.j2) : fixe uniquementKAFKA_HEAP_OPTSetRestart=on-failure/RestartSec=5— ne configure pas de port JMX (contrairement au cluster réel, voir tableau comparatif ci-dessus).
Procédure de déploiement¶
Ne pas exécuter ce playbook contre le cluster réel
L'inventory.ini du projet porte un avertissement explicite : le
cluster Kafka réellement en production a été reconstruit manuellement
par l'exploitant (confirmé le 2026-07-16) et diverge fortement de ce
que ce rôle déploierait — voir le tableau comparatif en haut de cette
page pour le détail complet (version, chemins, JMX, données).
Un kafka-storage format ou un remplacement de server.properties
détruirait ces données. Ce projet Terraform/Ansible est conservé
pour référence et trace IaC, pas comme procédure à rejouer en l'état.
Procédure telle qu'écrite dans le rôle (pertinente uniquement pour un nouveau cluster ou un environnement de test isolé, pas contre le cluster réel actuel — voir avertissement ci-dessus) :
cd /root/kafka/ansible
ansible-playbook --syntax-check site.yml
ansible-playbook site.yml -e @/root/.hap_secrets.yml
Vérification (sur un cluster nouvellement formé) :
kafka-broker-api-versions --bootstrap-server 10.100.20.131:9092
kafka-metadata-quorum --bootstrap-server 10.100.20.131:9092 describe --status
Points d'attention¶
-
Inspection SSH en direct du cluster réel non aboutie le 2026-07-19 (tentative en lecture seule,
no route to hostdepuis le nœud de contrôle vers10.100.20.131) — cause non déterminée (règle DFW SOC scopée n'incluant pas SSH depuis mgmt, ou indisponibilité transitoire). La section « Procédure manuelle » ci-dessus s'appuie donc sur les faits déjà connus (mémoire opérationnelle du projet) plutôt que sur une vérification directe pour ses points marqués comme déduits — à refaire si un accès SSH redevient possible. -
Voir l'avertissement de la section précédente : le cluster réel diverge du code IaC et contient des données de production — toute action destructive (formatage, remplacement de configuration) est à proscrire sans vérification préalable exhaustive.
MANUEL_INSTALLATION.md(racine du projet) décrit une procédure encore antérieure (tarball Apache Kafka 3.9.0 téléchargé depuisarchive.apache.org, servicekafka.service) : ne correspond ni au rôle Ansible actuel (paquet RPM Confluent, serviceconfluent-kafka), ni à la version réellement en production (4.3.1) — trois versions distinctes cohabitent donc dans l'historique de ce composant, à ne pas confondre : procédure manuelle périmée (3.9.0), rôle Ansible actuel (8.3/RPM Confluent, jamais exécuté contre le réel), cluster réel (4.3.1, construit à la main).- Le rôle protège le (re)formatage KRaft par un test d'idempotence sur
meta.properties, mais cela ne protège pas contre un rejeu de playbook qui pointerait le service vers uncluster.idou une configuration incompatible avec les données déjà en place. - Supervision JMX incomplète : le port JMX fixe (9999) répond, mais la négociation RMI de Kafka ouvre ensuite un second port aléatoire par connexion — non ouvert côté DFW SOC, jamais résolu (plage de ports fixe ou socket factory dédiée non mises en place). Le template Zabbix « Apache Kafka by JMX » reste donc incomplet ; jugé non critique par l'exploitant le 2026-07-17, non retenu comme prioritaire. Le reste de la supervision Kafka (agent Zabbix classique, port 10050) fonctionne normalement — voir Zabbix.