Erstellen gut pflegbarer und wiederverwendbarer Logstash-Pipelines
Logstash ist eine Open-Source-Pipeline zur Datenverarbeitung, die Ereignisdaten aus einer oder mehreren Quellen (Inputs) ingestiert und transformiert und das Ergebnis dann an einen oder mehrere Zielorte (Outputs) sendet. Es gibt Logstash-Implementierungen mit einer großen Menge von Codezeilen, die Ereignisdaten aus mehreren Quellen verarbeiten. Ich werde Ihnen zeigen, wie Sie mithilfe von Pipelines für modulare Komponenten die Wiederverwendbarkeit von Code verbessern können, damit sich solche Implementierungen leichter pflegen lassen.
Motivation
Bei Logstash kommt es häufig vor, dass auf Ereignisdaten aus mehreren Quellen eine bestimmte Menge gemeinsamer Logik angewendet werden muss. Dies lässt sich in der Regel auf eine der beiden folgenden Arten erledigen:
- Es gibt eine gemeinsame Pipeline, in der die Ereignisdaten aus allen Quellen zusammengefasst werden. Das macht es einfach, die gemeinsame Logik auf alle Ereignisdaten aus allen Quellen anzuwenden. Derartige Implementierungen weisen zusätzlich zur gemeinsamen Logik in der Regel eine beträchtliche Menge konditionaler Logik auf. Diese Herangehensweise kann daher dazu führen, dass die resultierenden Logstash-Implementierungen kompliziert und schwer durchschaubar sind.
- Es gibt für jede Quelle eine eigene Pipeline zur Verarbeitung der Ereignisdaten aus genau dieser Quelle. Bei dieser Methode müssen die für alle Daten benötigten Funktionen dupliziert und in jede einzelne Pipeline kopiert werden, was den Aufwand für die Pflege der gemeinsamen Teile des Codes erhöht.
Das hier beschriebene Verfahren hat zum Ziel, den Mängeln der beiden oben aufgeführten Verfahren entgegenzuwirken, indem wir modulare Komponenten in unterschiedlichen Dateien speichern und dann diese Komponenten zu Pipelines kombinieren. So lässt sich die Komplexität der Pipelines reduzieren und es muss kein Code mehr dupliziert werden.
Aufbau einer modularen Pipeline
Eine Logstash-Konfigurationsdatei besteht aus Inputs, Filtern und Outputs, die von einer Logstash-Pipeline ausgeführt werden. In komplexeren Setups übernimmt eine Logstash-Instanz die Ausführung mehrerer Pipelines. Wenn man Logstash ohne Argumente startet, liest diese Instanz standardmäßig eine Datei namens pipelines.yml
und instanziiert die angegebenen Pipelines.
Logstash-Inputs, ‑Filter und ‑Outputs können in mehreren Dateien gespeichert und über einen Glob-Ausdruck für die Aufnahme in eine Pipeline ausgewählt werden. Die Dateien, die mit dem Glob-Ausdruck übereinstimmen, werden dann in alphabetischer Reihenfolge miteinander kombiniert. Da es bei der Ausführung von Filtern häufig auf die Reihenfolge ankommt, kann es sinnvoll sein, in den Dateinamen Nummerierungen zu verwenden, damit die Dateien in der gewünschten Reihenfolge kombiniert werden.
Im Folgenden definieren wir zwei getrennte Pipelines, in denen mehrere modulare Logstash-Komponenten miteinander kombiniert sind. Wir speichern unsere Logstash-Komponenten in den folgenden Dateien:
- Input-Deklarationen:
01_in.cfg
,02_in.cfg
- Filter-Deklarationen:
01_filter.cfg
,02_filter.cfg
,03_filter.cfg
- Output-Deklarationen:
01_out.cfg
Anschließend nutzen wir Glob-Ausdrücke, um in pipelines.yml
Pipelines zu definieren, die aus den gewünschten Komponenten bestehen:
- pipeline.id: my-pipeline_1 path.config: "<path>/{01_in,01_filter,02_filter,01_out}.cfg" - pipeline.id: my-pipeline_2 path.config: "<path>/{02_in,02_filter,03_filter,01_out}.cfg"
In der oben beschriebenen Pipeline-Konfiguration gibt es in beiden Pipelines die Datei 02_filter.cfg
. Sie zeigt, wie der in beiden Pipelines vorhandene Code in einer gemeinsamen Datei definiert und verwaltet und von mehreren Pipelines ausgeführt werden kann.
Testen der Pipelines
In diesem Abschnitt stellen wir ein konkretes Beispiel für die Dateien in den Pipelines vor, die in der oben erwähnten Datei pipelines.yml
definiert sind. Anschließend führen wir Logstash mit diesen Dateien aus und zeigen, was Logstash für einen Output generiert.
Konfigurationsdateien
Input-Datei: 01_in.cfg
Diese Datei definiert einen Input, der als Generator fungiert. Der Generator-Input dient zum Testen von Logstash und generiert in unserem Fall ein einziges Ereignis.
input { generator { lines => ["Generated line"] count => 1 } }
Input-Datei: 02_in.cfg
Diese Datei definiert einen Logstash-Input, der den Datenstrom „stdin“ abhört.
input { stdin {} }
Filter-Datei: 01_filter.cfg
filter { mutate { add_field => { "filter_name" => "Filter 01" } } }
Filter-Datei: 02_filter.cfg
filter { mutate { add_field => { "filter_name" => "Filter 02" } } }
Filter-Datei: 03_filter.cfg
filter { mutate { add_field => { "filter_name" => "Filter 03" } } }
Output-Datei: 01_out.cfg
output { stdout { codec => "rubydebug" } }
Ausführen der Pipeline
Wenn Logstash ohne Optionen gestartet wird, wird die zuvor von uns definierte Datei pipelines.yml
ausgeführt. Zum Starten von Logstash geben wir Folgendes ein:
./bin/logstash
Die Pipeline my-pipeline_1
führt zum Simulieren eines Input-Ereignisses einen Generator aus. Daher wird nach der Initialisierung von Logstash Folgendes ausgegeben, was zeigt, dass die Inhalte von 01_filter.cfg
und 02_filter.cfg
so ausgeführt werden, wie dies von dieser Pipeline erwartet wird:
{ "sequence" => 0, "host" => "alexandersmbp2.lan", "message" => "Generated line", "@timestamp" => 2020-02-05T22:10:09.495Z, "@version" => "1", "filter_name" => [ [0] "Filter 01", [1] "Filter 02" ] }
Da die andere Pipeline namens my-pipeline_2
auf stdin auf Input wartet, haben wir bisher noch keine Ereignisse gesehen, die von dieser Pipeline verarbeitet wurden. Wir können jetzt etwas in das Terminal für Logstash eingeben und durch Drücken der Eingabetaste ein Ereignis für diese Pipeline erstellen. Daraufhin dürfte in etwa Folgendes angezeigt werden:
{ "filter_name" => [ [0] "Filter 02", [1] "Filter 03" ], "host" => "alexandersmbp2.lan", "message" => "I’m testing my-pipeline_2", "@timestamp" => 2020-02-05T22:20:43.250Z, "@version" => "1" }
Das zeigt uns, dass die Logik aus 02_filter.cfg
und 03_filter.cfg
erwartungsgemäß angewendet wird.
Reihenfolge der Ausführung
Zu beachten ist, dass Logstash die Reihenfolge der Dateien im Glob-Ausdruck komplett egal ist. Der Glob-Ausdruck wird nur verwendet, um zu bestimmen, welche Dateien einbezogen werden sollen. Die Dateien werden dann alphabetisch geordnet. Das bedeutet, dass selbst dann, wenn wir die Definition von my-pipeline_2
so ändern würden, dass 03_filter.cfg
im Glob-Ausdruck vor 02_filter.cfg
erscheint, jedes einzelne Ereignis vor dem Durchlaufen des in 03_filter.cfg
definierten Filters den Filter in 02_filter.cfg
passieren muss.
Fazit
Mit Glob-Ausdrücken lassen sich Logstash-Pipelines aus modularen Komponenten zusammensetzen, die als einzelne Dateien gespeichert werden. Das kann die Pflege, Wiederverwendbarkeit und Lesbarkeit von Code erleichtern.
Nebenbei bemerkt: Bei den Überlegungen dazu, wie sich die Modularität von Logstash-Implementierungen verbessern lässt, lohnt es sich, zusätzlich zum hier beschriebenen Verfahren auch einen Blick auf die Pipeline-zu-Pipeline-Kommunikation zu werfen.