Podman Container Logs mit Fluent Bit, Postgres und TimescaleDB verarbeiten

Posted on So 10 Juli 2022 in Blog

Fluent Bit ist eine Open-Source-Software um Log-Dateien zu verarbeiten und zeichnet sich durch eine einfache Installation und Anwendung aus. Fluent Bit bietet eine hohe Flexibilität hinsichtlich Input und auch Output. In diesem Beitrag möchte ich zeigen, wie man mithilfe von Fluent Bit und Postgres Container-Logs von Podman verarbeiten und speichern kann.

Die Befehle für diesen Beitrag wurden unter Rocky Linux 8.5 getestet, aber ich denke, sie sollten auch auf anderen Derivaten ohne Probleme laufen.

Der Container, dessen Log-Outputs gespeichert werden soll, wird im rootless-Modus laufen. Als Benutzer für diesen Testlauf habe ich den Benutzer webapp verwendet, aber grundsätzlich ist jeder Benutzer möglich. Über Podman und die Möglichkeit, Container rootless laufen zu lassen, habe ich auch in einem anderen Beitrag etwas geschrieben: Container mit Podman

Inhalt:

Postgres

Wie könnte es auch anders sein, möchte ich die Log-Dateien der Container in einer Postgres-Datenbank weiterverarbeiten. Da es sich bei Log-Dateien um klassische Zeitreihendaten handelt, bietet es sich an, die Postgres-Erweiterung TimescaleDB zu nutzen.

Die Dokumentation gibt einen guten Überblick, welche Möglichkeiten es gibt, Postgres mit der Timescale Erweiterung laufen zu lassen: Installation TimescaleDB

Für dieses Beispiel habe ich mich entschieden, die Datenbank in einem Container laufen zu lassen.

podman run -d --name timescaledb -p 5432:5432 -e POSTGRES_PASSWORD=password timescale/timescaledb:latest-pg14

Um später die Daten für Fluent Bit zu speichern, werden wir auch den entsprechenden Benutzer und die Datenbank anlegen. Dafür verwende ich psql, aber man kann natürlich auch jeden anderen Postgres-Client verwenden:

-- Datenbankbenutzer anlegen
postgres=# create user fluentbit with encrypted password 'fluentbit';
CREATE ROLE
-- Datenbank für die Logdateien anlegen
postgres=# create database fluentbit;
CREATE DATABASE
-- in die neu erstelle Datenbank wechseln
postgres=# \c fluentbit
You are now connected to database "fluentbit" as user "postgres".
-- die Tabelle für die Logs erstellen
fluentbit=# create table fluentbit (tag text, time timestamp without time zone, data jsonb);
-- TimescaleDB mitteilen, welche Tabelle eine Zeitreihentabelle ist
fluentbit=# SELECT create_hypertable('fluentbit', 'time');
-- und dem fluentbit Benutzer noch die Rechte geben, um die Datenbank zu nutzen
fluentbit=# grant select, insert on table fluentbit to fluentbit;

Die Tabellenstruktur für den Fluent Bit Log-Output habe ich der Dokumentation entnommen: Fluent Bit PostgreSQL Output

Durch den create_hypertable erstellt TimescaleDB automatische Partitionen der Daten. Siehe dazu auch: Create hypertable

Fluent Bit Installation

Die Installation von Fluent Bit ist sehr simpel und auch sehr gut dokumentiert: https://docs.fluentbit.io/manual/installation/linux/redhat-centos#install-on-redhat-centos

Wenn man diesen Schritten folgt, dann sollte sich Fluent Bit auf Rocky Linux und ähnlichen Plattformen ohne Probleme installieren lassen.

Der Konfiguration von Fluent Bit widmen wir uns in einem nächsten Schritt: Fluent Bit Konfiguration

Testanwendung

Als Testanwendung verwenden wir einfach einen Nginx-Container. Daher habe ich mich als webapp-Benutzer am System angemeldet und die folgenden Befehle werden als ebendieser Benutzer ausgeführt. In den Container kopieren wir dann einfach eine simple HTML-Datei und schon ist unser "Webanwendung" fertig.

[webapp@rockylinux]$ mkdir webapp
[webapp@rockylinux]$ tee webapp/index.html <<EOL
> <html>
> <head>
> <title>Postgres, Timescale und Fluent Bit</title>
> </head>
> <body>
> <h1>Meine Webapp</h1>
> </body>
> </html>
> EOL
[webapp@rockylinux]$ podman run --name nginx -v /home/webapp/webapp:/usr/share/nginx/html:z -p 8080:80 --log-driver='k8s-file' --log-opt path=/tmp/webapp.json -d nginx

Beim Befehl podman run hat man die Möglichkeit Log-Optionen zu setzen. Einerseits wurde hier mittels --log-driver der verwendete Log-Typ angegeben und andrerseits über --log-opt path der Speicherort der Log-Datei angeben (Natürlich ist der /tmp prinzipiell ein ungeeigneter Ort, um Log-Dateien abzulegen und wurde hier nur zu Demonstrationszwecke verwendet, um sich eine entsprechende Konfiguration der Rechte in /var/log zu sparen ;-).

Fluent Bit Konfiguration

Um später auf die Postgres-Datenbank zugreifen zu können, muss in /root die Datei .pgpass mit den entsprechenden Verbindungsparametern gespeichert werden. Dadurch braucht man dann in der Fluent Bit Konfiguration selbst kein Passwort angeben. Der Inhalt von .pgpass ist dann wie folgt:

localhost:5432:fluentbit:fluentbit:fluentbit

Damit die Datei auch gelesen werden kann, müssen die Berechtigungen noch entsprechend gesetzt werden:

chmod 600 .pgpass

Nachdem die Testanwendung läuft, muss man Fluent Bit "nur" noch mitteilen, wo die Log-Datei gefunden werden kann und um welche Art von Log-Datei es sich handelt. Einerseits müssen der Input (Log-Datei unsere Webanwendung) und andrerseits der Output (unsere Postgres-Datenbank) definiert werden.

Hierfür wird nun die Datei /etc/fluent-bit/fluent-bit.conf bearbeitet:

[INPUT]
    Name tail
    Path /tmp/webapp.json
    Parser cri
    Tag webapp
    Mem_Buf_Limit 5MB
    Skip_Long_Lines On
[OUTPUT]
    Name          pgsql
    Match         *
    Host          localhost
    Port          5432
    User          fluentbit
    Database      fluentbit
    Table         fluentbit
    Timestamp_Key ts

Im ersten Teil wird der Input definiert. Hier wird angegeben, wo sich die zu lesende Log-Datei befindet, welcher Parser verwendet werden soll und welche Bezeichnung (Tag) die Log-Dateien in der Datenbank dann auffindbar machen sollen. Für den Podman-Container wird der CRI (Container Runtime Interface)-Parser verwendet. Beispiel hierfür findet sich im Kubernetes-Teil von Fluentbit: Container Runtime Interface (CRI) parser

Eine Alternative zum CRI-Parser wäre der docker-Parser, der im Prinzip einfach den JSON-Output weitergibt. Die Unterschiede sind jetzt auch nicht sehr gravierend, wie nachfolgendes Beispiel der Nginx-Outputs, einmal mit dem CRI-Parser und einmal mit dem docker-Parser, zeigt.

-- cri-parser
webapp | 2022-07-10 13:47:21.786909 | {"ts": 1657460841.786909, "logtag": "F", "stream": "stdout", "message": "10.0.2.100 - - [10/Jul/2022:13:47:21 +0000] \"GET / HTTP/1.1\" 200 117 \"-\" \"curl/7.61.1\" \"-\""}
-- docker-parser
webapp | 2022-07-10 13:58:41.912788 | {"ts": 1657461521.912788, "log": "2022-07-10T13:58:41.912651048+00:00 stdout F 10.0.2.100 - - [10/Jul/2022:13:58:41 +0000] \"GET / HTTP/1.1\" 200 117 \"-\" \"curl/7.61.1\" \"-\""}

Im Output-Bereich werden nun die Verbindungsparameter unserer Datenbank angegeben. Ein Passwort muss nicht angegeben werden. Dafür sorgt die .pgpass-Datei, die wir erstellt haben. Mehr Informationen zu Postgres als Fluent Bit Output findet man in der Dokumentation

Nachdem die Konfiguration bearbeitet wurde, sollte auch noch das Fluent Bit Service (neu) gestartet werden.

Analyse der Daten

Eine Stärke von TimescaleDB sind die zusätzlichen Funktionen, die einfach per "normalem“ Postgres-Interface genutzt werden können. Die time_bucket Funktion kann dazu genutzt werden, beispielsweise die Daten für jegliche Art der Visualisierung in gleich große Zeiträume einzuteilen. Im beruflichen Umfeld nutze ich Grafana oder Apache Superset, um auf die Daten zuzugreifen und dabei ist time_bucket eine mehr als nützliche Funktion.

ZUsätzlich bietet TimescaleDB auch noch Funktionen, die die Erstellung einer Data Retention Policy ermöglichen oder eine Kompression der Daten.

Fazit

Ich finde die Möglichkeiten, die Fluent Bit bietet, um Log-Dateien von Podman zentral abzuspeichern, sehr gut. Auch das Zusammenspiel mit TimescaleDB zeigt auch bei der Visualisierung der Daten große Stärken.

Und durch andere bekannte Erweiterungen aus dem Postgres-Umfeld, beispielsweise Postgis, kann man umfangreiche Analysen machen.