Aller au contenu
Outils medium

Fluentd : Agrégation de logs unifiée

17 min de lecture

Fluentd est un collecteur de données open source unifié, projet CNCF Graduated, qui centralise et route les logs vers de multiples destinations. Là où Fluent Bit collecte en périphérie, Fluentd joue le rôle d'agrégateur central, avec un écosystème de plus de 500 plugins.

Ce guide s'adresse aux profils intermédiaires et avancés. Vous allez installer Fluentd (version 1.19), comprendre son pipeline (source, filter, match), écrire une configuration, gérer le buffering et router les logs par tags.

  • Comprendre le pipeline Fluentd : source, filter, match (buffer).
  • Installer Fluentd proprement (Docker épinglé, gem, fluent-package).
  • Configurer collecte, filtres et sorties multiples.
  • Router les événements avec le système de tags.
  • Fiabiliser avec un buffer fichier persistant.

Fluentd est le standard de facto pour l'agrégation de logs dans les environnements cloud-native, avec plus de 500 plugins disponibles.

CaractéristiqueDescription
LicenceApache 2.0
MaturitéCNCF Graduated
LangageRuby + C
Plugins500+
CommunautéTrès active

Fluentd utilise une architecture en pipeline avec des plugins d'entrée, de filtrage et de sortie.

┌─────────────────────────────────────────────────────────────────┐
│ Fluentd │
│ ┌──────────────┐ ┌───────────────┐ ┌───────────────────────┐ │
│ │ Input │──│ Filter │──│ Output │ │
│ │ plugins │ │ plugins │ │ plugins │ │
│ └──────────────┘ └───────────────┘ └───────────────────────┘ │
│ │
│ - tail - parser - elasticsearch │
│ - forward - record_modifier - loki │
│ - http - grep - kafka │
│ - syslog - geoip - s3 │
│ - tcp - throttle - forward │
└─────────────────────────────────────────────────────────────────┘
┌───────────────┐
│ Buffer │
│ (file/memory) │
└───────────────┘

Fluentd est écrit en Ruby, ce qui explique ses trois modes d'installation : le paquet système fluent-package (qui embarque son propre Ruby), la gem pour un environnement Ruby existant, et l'image conteneur pour Docker ou Kubernetes. Le choix a une conséquence directe sur l'emplacement de la configuration et sur la commande d'installation des plugins. Quelle que soit la méthode, épinglez une version précise : latest casse silencieusement les plugins compilés contre une autre version de l'API.

Le paquet historique td-agent est en fin de vie. Son successeur est fluent-package (Fluentd v5), à installer depuis le dépôt signé officiel pour un service système. Pour une installation simple et multiplateforme, Fluentd s'installe aussi comme gem Ruby :

Fenêtre de terminal
# Fluentd via RubyGems (nécessite Ruby)
gem install fluentd
fluentd --version

La configuration Fluentd utilise des directives <source>, <filter> et <match>.

Cette configuration lit un répertoire de logs et les réaffiche sur la sortie standard, de quoi valider la chaîne avant de brancher un vrai backend. Le pos_file est le fichier de position : il mémorise jusqu'où Fluentd a lu dans chaque fichier surveillé. Sans lui, l'agent ne sait pas reprendre où il s'était arrêté après un redémarrage, et les lignes écrites pendant la coupure sont perdues. Le tag (app.logs ici) est la clé qui reliera cette source aux blocs filter et match.

fluent.conf
<source>
@type tail
path /var/log/app/*.log
pos_file /var/log/fluentd/app.log.pos
tag app.logs
<parse>
@type json
</parse>
</source>
<match app.logs>
@type stdout
</match>

Cette configuration enchaîne les quatre étages du pipeline : deux sources (fichiers applicatifs et syslog), deux filtres (ajout de métadonnées puis parsing), et une sortie vers Elasticsearch avec un buffer persistant. L'ordre des blocs compte : les filter s'appliquent dans l'ordre du fichier, et un événement sort par le premier match dont le motif correspond à son tag. Un <match **> placé trop haut capturerait donc tout le trafic.

fluent.conf
# Collecter les logs applicatifs
<source>
@type tail
path /var/log/app/*.log
pos_file /var/log/fluentd/app.pos
tag app.logs
<parse>
@type json
time_key timestamp
time_format %Y-%m-%dT%H:%M:%S.%L%z
</parse>
</source>
# Collecter syslog
<source>
@type syslog
port 5140
bind 0.0.0.0
tag syslog
</source>
# Ajouter des métadonnées
<filter app.**>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
environment production
</record>
</filter>
# Parser les logs multi-format
<filter app.**>
@type parser
key_name message
reserve_data true
<parse>
@type multi_format
<pattern>
format json
</pattern>
<pattern>
format regexp
expression /^(?<time>[^ ]+) (?<level>[^ ]+) (?<message>.*)$/
</pattern>
</parse>
</filter>
# Sortie vers Elasticsearch avec buffer
<match **>
@type elasticsearch
host elasticsearch
port 9200
index_name fluentd-${tag}-%Y.%m.%d
<buffer tag, time>
@type file
path /var/log/fluentd/buffer
flush_mode interval
flush_interval 5s
chunk_limit_size 5MB
queue_limit_length 512
retry_max_interval 30
retry_forever true
</buffer>
</match>

Sur les 500 plugins du registre, une douzaine couvre la quasi-totalité des besoins. Ils se répartissent selon leur position dans le pipeline : input pour collecter, filter pour transformer, output pour expédier. Seuls quelques-uns sont livrés avec Fluentd ; les autres s'installent séparément, une distinction qui explique la plupart des erreurs de démarrage du type Unknown output plugin.

Ces plugins déterminent d'où viennent les données. tail lit des fichiers, les autres écoutent sur un port réseau et attendent qu'on leur envoie les événements.

PluginUsageConfiguration
tailLire des fichierspath, pos_file
forwardRecevoir d'autres Fluentdport 24224
httpAPI HTTPport 9880
syslogRecevoir syslogport 5140
tcpSocket TCPport 5170

Un filtre s'applique aux événements dont le tag correspond à son motif, et peut aussi bien enrichir l'enregistrement que le supprimer. C'est l'endroit où réduire le volume avant l'envoi, donc avant de payer le stockage.

PluginUsageExemple
record_transformerAjouter/modifier des champsAjouter hostname
parserParser le messageJSON, regex
grepFiltrer par patternInclude/exclude
geoipEnrichir avec GeoIPIP → localisation

La destination finale des événements. Rien n'oblige à n'en avoir qu'une : plusieurs blocs match avec des motifs différents permettent d'archiver dans S3 tout en indexant dans Elasticsearch, et le plugin forward sert à chaîner un agent vers un agrégateur.

PluginDestination
elasticsearchElasticsearch / OpenSearch
lokiGrafana Loki
kafkaApache Kafka
s3Amazon S3
forwardAutre Fluentd
prometheusMétriques Prometheus

Les plugins sont des gems Ruby. La commande fluent-gem les installe dans l'environnement Ruby embarqué par Fluentd, ce qui n'est pas la même chose qu'un gem install système. En conteneur, l'installation doit se faire au build de l'image : un plugin ajouté à chaud disparaît au redémarrage du conteneur.

Sur un hôte
fluent-gem install fluent-plugin-elasticsearch
Dockerfile
FROM fluent/fluentd:v1.19-debian-1@sha256:f29b1d1103afd4bbcab8a17ecfefb411674ec57694f0cbd6641b4ae4dd65e13b
USER root
RUN gem install fluent-plugin-elasticsearch
USER fluent

Le buffer est crucial pour la fiabilité. Fluentd supporte deux types.

Rapide mais perte de données en cas de crash.

<buffer>
@type memory
flush_interval 5s
</buffer>

Persistant sur disque, survit aux redémarrages.

<buffer>
@type file
path /var/log/fluentd/buffer
chunk_limit_size 8MB
total_limit_size 2GB
flush_mode interval
flush_interval 5s
flush_thread_count 2
retry_forever true
retry_max_interval 30
</buffer>

Les tags déterminent le routage des événements. Chaque source attribue un tag à ce qu'elle collecte, et chaque bloc match déclare le motif de tags qu'il traite. La notation pointée (nginx.access) crée une hiérarchie exploitable par les jokers : * remplace un seul segment, ** en remplace un nombre quelconque. Un événement quitte le pipeline par le premier match qui l'accepte, donc l'ordre de déclaration prime sur la précision du motif.

# Source avec tag
<source>
@type tail
path /var/log/nginx/access.log
tag nginx.access
</source>
# Match exact
<match nginx.access>
@type elasticsearch
</match>
# Match avec wildcard
<match nginx.**>
@type loki
</match>
# Match multiple tags
<match {nginx.access,app.logs}>
@type s3
</match>
# Match tout
<match **>
@type stdout
</match>

Dans un cluster, les logs des conteneurs sont écrits sur chaque nœud, sous /var/log/containers/. Il faut donc un agent sur chaque nœud, déployé en DaemonSet, et c'est le rôle de Fluent Bit. Fluentd se place derrière, en Deployment, pour concentrer le trafic et gérer le buffer avant l'envoi au backend.

Cette séparation en deux étages évite que chaque nœud ouvre sa propre connexion vers Elasticsearch, et concentre la reprise sur incident au même endroit : si le backend tombe, c'est l'agrégateur qui accumule, pas les dizaines d'agents.

┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Fluent │ │ Fluent │ │ Fluent │ ← DaemonSet │
│ │ Bit │ │ Bit │ │ Bit │ (chaque node) │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Fluentd │ ← Deployment │
│ │ (agrégateur) │ (replicas: 2) │
│ └────────┬────────┘ │
│ │ │
└────────────────────┼──────────────────────────────────────────────┘
┌──────────────────────┐
│ Elasticsearch / │
│ Loki / S3 │
└──────────────────────┘

Côté collecte, Fluent Bit lit les fichiers de logs du nœud et pousse tout vers l'agrégateur avec la sortie forward, le protocole natif de la famille Fluent. Le Host doit correspondre au nom du Service Kubernetes de l'agrégateur, pas à un Pod, sinon la reprise après un redémarrage échoue.

values-forwarder.yaml
config:
inputs: |
[INPUT]
Name tail
Path /var/log/containers/*.log
Tag kube.*
outputs: |
[OUTPUT]
Name forward
Match *
Host fluentd-aggregator
Port 24224

Côté agrégateur, la source forward écoute sur le port 24224 les événements envoyés par les agents. Le filtre kubernetes_metadata interroge l'API du cluster pour ajouter le namespace, le nom du Pod et les labels à chaque enregistrement : sans lui, vous ne savez pas de quelle application vient une ligne de log. Ce plugin ne fait pas partie du cœur de Fluentd, il s'installe séparément et exige des droits de lecture sur les Pods.

fluentd-aggregator.conf
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<filter kube.**>
@type kubernetes_metadata
</filter>
<match kube.**>
@type elasticsearch
host elasticsearch
port 9200
index_name kubernetes-%Y.%m.%d
<buffer>
@type file
path /var/log/fluentd/buffer
</buffer>
</match>

Un collecteur de logs qui tombe est particulièrement pénible : personne ne le voit tant qu'on ne cherche pas un log absent. Fluentd expose donc son propre état par deux voies, une API HTTP interne pour un diagnostic ponctuel, et un endpoint Prometheus pour la surveillance continue. La métrique à suivre en priorité est la taille de la file de buffer : elle monte dès que la destination ralentit.

Le plugin monitor_agent ouvre une petite API HTTP qui expose l'état de chaque plugin chargé, dont la longueur de la file d'attente.

<source>
@type monitor_agent
bind 0.0.0.0
port 24220
</source>

Accédez à http://localhost:24220/api/plugins.json.

Deux blocs sont nécessaires : le premier ouvre l'endpoint /metrics sur le port 24231, le second (prometheus_output_monitor) alimente cet endpoint avec les compteurs des plugins de sortie, dont le nombre d'échecs de retry. Le premier seul exposerait une page presque vide.

<source>
@type prometheus
bind 0.0.0.0
port 24231
metrics_path /metrics
</source>
<source>
@type prometheus_output_monitor
</source>

Ces cinq règles répondent toutes au même risque : la perte de logs lors d'un incident, c'est-à-dire précisément au moment où ils sont indispensables. Les deux premières se configurent dans le bloc <buffer>, les trois autres relèvent de l'architecture et de la surveillance.

  1. Utilisez des buffers fichier en production
  2. Limitez la taille des chunks pour éviter l'OOM
  3. Activez retry_forever pour ne pas perdre de logs
  4. Séparez collecte et agrégation (Fluent Bit + Fluentd)
  5. Monitorez les buffers pour détecter les backlogs

La majorité des incidents Fluentd viennent du buffer ou du parsing, rarement du réseau. Avant de modifier la configuration, validez sa syntaxe avec --dry-run : le démarrage échoue de toute façon si un plugin référencé n'est pas installé, mais l'erreur est bien plus lisible en mode -vvv.

SymptômeCause probableSolution
Buffer pleinDestination lente/downAugmenter queue_limit
Logs perdusMemory buffer + crashPasser en file buffer
Haute mémoireChunks trop grosRéduire chunk_limit_size
Parsing échoueFormat non reconnuVérifier le pattern
Fenêtre de terminal
# Vérifier la config
fluentd --dry-run -c /etc/fluent/fluent.conf
# Mode debug
fluentd -c /etc/fluent/fluent.conf -vvv

Les questions ci-dessous reviennent le plus souvent lors de la mise en place : arbitrage entre Fluentd et Fluent Bit, fonctionnement du routage par tags et nécessité d'un buffer fichier.

  • CNCF Graduated : standard de l'industrie
  • 500+ plugins : intégration avec tout
  • Buffer fichier : obligatoire en production
  • Tags : système de routage flexible
  • Fluent Bit en collecte : Fluentd en agrégation

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn