Fluentd
Last updated
Was this helpful?
Last updated
Was this helpful?
Fluentd ist im Gegensatz zu ELK keine komplette Lösung (bestehend aus UI, Processing, Storage) - es handelt sich nur um einen Datensammler und integriert sich auch in bereits existierende Logging-Infrastrukturen, ganz ähnlich zu Logstash (aber deutlich performanter). Man kann hier viele Quellen und Ziele anbinden. Zwischendrin verarbeitet Fluentd die Daten (buffer/filter/route)
Buffering
Filtering
Enrichment
Parsing unstructured Data
Format-Transformation
Routing
File
S3
MySql
Nagios
...
So sieht die Processing-Chain (= Data-Pipeline) aus und für jeden Abschnitt gibt es entsprechende (u. a. > 200 Output-Plugins):
Input => Parser => Filter => Buffer => Output => Formatter
Fluentd verarbeitet Nachrichten (Strings, Json), die von einer source
kommen (push) oder aus einer solchen gezogen (pull) werden und transformiert diese zunächst einmal in das Fluentd-JSON-based-Datenmodell basierend auf Events. Ein Event besteht aus
tag
- z. B. myapp.access
nur genau EINS
time
im Unix Time-Format
record
- Log-Daten
attrs
... plugin abhängige Daten ...
ACHTUNG: Parsing kann sehr CPU-intensiv werden (bei hohem Log-Aufkommen) - Input-Plugins sind NIEMALS blockierend
Fluentd ist eine Input-Processing-Output-Komponente und wir häufig bei der Zentralisierung/Konsolidierung von Logs aus verschiedenen Quellen verwendet. Hierbei werden einkommende Log-Nachrichten gefiltert, mit Informationen angereichert, in ein einheitliches Format transformiert und dann zu einem oder mehreren Repositories weitergeleitet.
Diese Ansätze werden häufig beim Logging eingesetzt. Aufgrund der dynamischen Last-Verteilung über Multi-Nodes-Architekturen sind die konversativen (veralteten) Ansätze von vor 10 Jahren nicht mehr geeignet, um diese Use-Cases zu adressieren.
Auf diese Weise entsteht eine konsolidierte Sicht auf die Log-Nachrichten, die aus verschiedensten Quellen mit verschiedensten Formaten kommen. Durch die Sammlung in einer für diese Datenmenge ausgelegten hochverfügbaren Technologie (No-SQL Datenbanken wie ElasticSearch, Graylog, ...) hat man immer schnellen Zugriff auf die relevanten Informationen und kann nach verschiedensten Attributen suchen.
Aus diesem Grund kann Fluentd
Zwischendrin werden Messages
für Custom-Formate schreibt man ein Parser-Plugin
... Sketch ...
Eine Event im Fluentd Datenmodell kann dann z. B. so aussehen:
In diesem Beispiel sah die Konfiguration folgendermaßen aus (nur der Parser-Part):
und der Eintrag wurde folgendermaßen provoziert:
findet im beliebten ELK-Stack Verwendung
[LogPacker]
Fluentd verwendet einen Plugin-Ansatz für diese Aspekte - Erweiterbarkeit war ein wichtiges Qualitätskriterium beim Design der Lösung. Dadurch lassen sich mit einer Logging-Lösung viele verschiedene - auch sehr spezielle - Use-Cases abbilden. Im schlimmsten Fall schreibt man sich sein eigenes Plugin, im einfachsten Fall paßt man nur die Logik über die Fluentd-Sprache an oder verwendet existierende Plugins.
Fluentd verwendet eine Positionsdatei (konfiguriert per pos_file /var/log/fabio/fabio.fluentd.pos
), die kennzeichnet bis wohin die Logeinträge verarbeitet wurden. Selbst wenn Fluentd mal hängen sollte, gehen keine Logs in der Prozessierung verloren
verwendet man Log-Files, so sollte man die besten rotierend definieren, damit man sich das Filesystem nicht zumüllt und irgendwann keinen Platz mehr hat. Man darf die Anzahl/Größe der Files aber auch nicht zu gering wählen, um genügend Puffer zu haben, falls es irgendwann mal stockt
"Fluentd allows you to route events based on their tags"
woher kommen die Tags?
wenn eine Log-Nachricht bereits im JSON-Format kommt (beispielsweise weil man den Logback-GelfAppender verwendet), dann kann dort schon ein tag
verbaut sein
aus dem internen Fluentd-Processing
aus der Source, z. B. bei einer http
Source wird das Tag aus der URL genommen
HTTP-Request http://localhost:9880/pierre?json={"logit":"now"} liefert das Tag pierre
"Fluentd tries to match tags in the order that they appear in the config file."
"Wider match patterns should be defined after tight match patterns."
"If you want to send events to multiple outputs, consider out_copy plugin."
"The common pitfall is when you put a <filter>
block after <match>
."
timestamp
tag
label
record (= Payload)
ACHTUNG: der Record kann auch einen tag
haben, das dann aber nicht zu verwechseln ist mit dem tag
des Fluentd-Modells. Andererseits kann man den Fluentd-Tag in den Record übernehmen
Die wichtigesten Elemente sind:
<system>
<source>
http
hier wird ein HTTP endpoint erzeugt - bedienbar per Browser oder CLI's wie curl
forward
hier wird ein TCP endpoint erzeugt
<filter tag>
Filter basieren auf einem Tag oder einer Tag-Hierarchie (<filter foo.**>
)
filter bedeutet nicht, daß nur ein rausfiltern möglich ist
es können per <record>
weitere Informationen hinzugefügt werden, die dem Event hinzugefügt werden und dann auch in der Output-Phase ausgegeben werden
<match>
trigger action
man kann auch mehrere Tags matchen: <match {app.**,system.logs}>
<label>
Routing für ein Event mit
<store>
@type
hierüber wird das Plugin selektiert und damit die unterstützen nachfolgenden Aktionen/Konfigurationen
es können auch weitere Plugins nachinstalliert werden, die nicht von Fluentd mitgeliefert werden (/usr/sbin/td-agent-gem install fluent-plugin-multi-format-parser
)
tag
einem Event ein Tag verpassen - über die Tags wird bei filter
und match
selektiert
Tags können eine Hierarchie haben (durch Punkte getrennt)... tag "infrastructure.service.consul"
und bei hierarchischen Filtern/Matchern, darf man nicht vergessen, daß man sowas machen kann/muß:
<filter infrastructure.*>
<filter **>
<filter *>
funktioniert dann NICHT!!!
@label
hierbei handelt es sich um leichtgewichtige tag
s:
@include
Der type
kennzeichnet das Plugin.
Aus Dateien lesen - Fluentd kennt schon die bekanntesten Log-Formate, so daß man beispielsweise format apache2
verwenden kann anstatt einen eigenen Parser zu definieren:
Aus Anwendungen Events erhalten:
Daten von einem TCP-Socket erhalten und mit einem Regex-Parser parsen:
Alle Events an ein Ziel weiterleiten/speichern:
Bestimmte Events (match
- hier mit dem tag backend.foo
) an ein Ziel weiterleiten/speichern:
Mit de copy
-Befehl kann man die Daten auch an mehrere Targets verschicken, hier beispielsweise an ElasticSearch und Hadoop:
es kann nur ein Tag vergeben werden - in einem match
(= Output-Phase) kann es per rewrite_tag_filter
überschrieben werden
es können beliebig viele @label @foo
vergeben werden, die dann folgendermaßen genutzt werden können:
zu beachten ist dabei, daß ein Sektion <label @foo>
nur ein einziges mal verwendet werden kann, d. h. darin muß sich dann die komplette Verarbeitung inklusive Output (= match
) befinden. Dieser Ansatz beißt sich gelegentlich in Kombination mit einem multi_format
-Parser und mit dem @include
-Mechanismus, mit dem man die Fluentd-Konfiguration modularisieren möchte.
mit @include
Statements lassen sich Teile wiederverwenden - letztlich wird der Inhalt eines inkludiertes Files einfach an die Stelle kopiert. Beim Start von Fluentd wird das Endeergebnis (= effective configuration) mit aufgelösten inlcudes angezeigt (sehr praktisch)
ACHTUNG: arbeitet man mit Wildcards (@include conf.d/*.conf
), dann ist die Reihenfolge der Inkludierung bestimmt durch: "files are expanded in the alphabetical order" ... somit kann man sich durch Umbenennung der Datei evtl. die Logik zerschießen. Entweder verzichtet man dann auf die Wildcard-Inkludierung oder man schreibt nur Konfigurationen, bei den die Reihenfolge irrelevant ist.
Die Fehlersuche gestaltet sich leider nicht so angenehm, da die Verarbeitung recht komplex werden kann und dementsprechend viele Fehlerquellen möglich sind. Häufig kann man dann auch nur an einem laufenden System herumexperimentieren - nicht in einer schönen kleinen IDE.
Das Fluentd-Log-File ist immer ein guter Anlaufpunkt, um
die Syntax der Konfigurationsdateien zu prüfen
nach einem pattern not matched
Ausschau zu halten - ein Indikator, daß ein Log-Event keinem Pattern entspricht und somit sehr wahrscheinlich ignoriert wurde
Gerade wenn man mit asynchroner Prozessierung arbeitet (z. B. GELF-Messages werden nach Graylog geschickt oder lokale Logs wurden noch nicht prozessiert), dann passiert es gelegentlich, daß die Nachrichten im Ziel verspätet eintreffen. Deshalb habe ich mir angewöhnt, immer Timestamps (z. B. aktuelle Uhrzeit 1340) in die Nachricht zu packen, um den Zeitpunkt zu identifizieren. Das verhindert, daß man denkt "jetzt funktioniert" es ... man sieht aber um 17:28 Uhr eigentlich die Nachricht von 13:40 Uhr.
Statt komplizierte Output-Plugins (zu Graylog, ElasticSearch) zu verwenden, kann man übergangsweise auch einfach mal eine Umleitung in eine lokale Datei machen - auf diese Weise kann man eine komplizierte/asynchrone und damit verzögerte Prozessierung im Endsystem als Fehlerquelle ausschließen:
Ich lege erzeuge /home/pfh/src/de.cachaca.learn.fluentd/fluent.conf
mit diesem Inhalt an:
lege ein Log-Verzeichnis an:
und starte einen Docker-Container mit dem Namen pierre-fluentd
:
Per docker logs pierre-fluentd
kann ich die Initialisierung des Fluentd-Servers einsehen.
Anschließend kann ich über den HTTP-Endpoint Log-Nachrichten erzeugen:
im Browser: http://localhost:9880/pierre?json={"logit":"now"}
per curl: curl -X POST -d 'json={"json":"message"}' http://localhost:9880/pierre
und die Ausgabe im Verzeichnis /tmp/fluentd/log
anschauen.
ACHTUNG:
pierre
wird hier alstag
verwendet, so daß es zumatch pierre
paßt.
Per docker rm -f pierre-fluentd
lösche ich den Fluentd-Container wieder nach getaner Arbeit.
... noch nicht ausprobiert - ich mag die Console lieber
Der Docker Log-Driver kann von Console (default) auf Fluentd umgestellt werden (docker run --log-driver=fluentd my-image
). Dann bekommt man keine /var/lib/docker/**.log
Files mehr erstellt, sondern die Log-Informationen werden direkt nach Fluentd geschickt.
Dieser Ansatz hat allerdings den Nachteil, daß docker logs
nicht mehr funktioniert, weil das auf den Log-Files basiert.
Kommt der Fluentd-server nict mit der Abarbeitung nach oder die Netzwerk-Kommunikation ist gestört, dann werden die Log-Nachrichten im RAM oder auf der Platte gepuffert (fluentd-buffer-limit
).
verwendet man Docker, dann kann in Abhängigkeit des Logging-Drivers (z. B. ) auch noch die enthalten enthalten sein, die vom json
-Fluentd-Parser in die Record Datenstruktur übernommen werden (abfragbar per ${record["attrs"]["os"]
)
Dieses Thema ist unternehmenskritisch, da Logs i. a. in Produktivumgebungen die einzige Möglichkeit sind, um Fehler zu analysieren und den Kunden vor längeren Systemausfällen zu schützen. Die Verwendung von Real-Time-Analyse-Tools wie ist noch besser ...
aus
in
es gibt Standard-Parser für typische Log-Formate wie z. B.
alternativ kann man reguläre Ausdrücker verwenden und
hier stehen auch zur Verfügung, die gerne bei Stack-Traces eingesetzt werden
manchmal sind die Log-Einträge einer Anwendung bzw. eines Log-Files nicht alle im gleichen Format. Wenn man die Logs von der Anwendung her nicht trennen kann, so kann man den verwenden
Ohne das Konzept bzw. die Sprache verstanden zu haben ist die Konfigurationsdatei kaum zu verstehen. bietet einen guten Einstieg.
Konzepte ():
es kann ein Parsing (<parse>
) erfolgen, bei dem beispielsweise
"The "label" directive groups filter and output for internal routing. "label" reduces the complexity of tag handling. [...] "label" is useful for event flow separation without tag prefix." ()