Obecne systemy chmurowe zmagają się z coraz bardziej rosnącą ilością danych generowanych przez różnorodne serwisy. Skuteczne zarządzanie i analiza logów stają się kluczowymi aspektami utrzymania stabilności i efektywności systemów IT. W tym kontekście narzędzia do zbierania, przetwarzania i analizy danych zdecydowanie odgrywają istotną rolę. Jednym z nich jest Fluent Bit.
Fluent Bit pozwala na gromadzenie logów z różnych źródeł, ich przetwarzanie, filtrowanie oraz przesyłanie dalej do różnych miejsc docelowych. W niniejszym artykule zaprezentuję podstawowe funkcje Fluent Bita. Odczytam logi z przykładowego serwisu znajdującego się na klastrze kubernetesowym, przefiltruję logi w zależności od ich źródła pochodzenia i na koniec, prześlę je dalej do innych serwisów, aby tam mogły zostać poddane analizie.
Przygotowanie klastra
Aby pokazać podstawowe możliwości Fluent Bita, potrzebujemy lokalnego klastra kubernetesowego. Skorzystamy z Kinda. Do tego skomunikujemy się z klastrem za pomocą kubectl. Na koniec zainstalujemy Helma, aby w łatwy sposób dodać Fluent Bita do naszego klastra.
Instalacja Fluent Bita
Gdy nasz lokalny klaster jest gotowy do użytku, przy pomocy Helma instalujemy Fluent Bita:
helm repo add fluent https://fluent.github.io/helm-charts
helm upgrade --install fluent-bit fluent/fluent-bit --namespace default
Po uruchomieniu powyższych komend powinniśmy zobaczyć DaemonSeta, który uruchomi Poda z instancją FluentBita na każdym naszym Nodzie.
Generator logów
Aby sprawdzić, jak Fluent Bit przetwarza i przekazuje dalej logi, potrzebujemy serwisu, który te logi będzie dla nas produkował. Dodamy prosty serwer na naszym lokalnym klastrze:
apiVersion: apps/v1
kind: Deployment
metadata:
name: server
labels:
app: server
spec:
replicas: 1
selector:
matchLabels:
app: server
template:
metadata:
labels:
app: server
spec:
containers:
- name: server
image: pjasiak/log-server:0.1.0
ports:
- containerPort: 8085
---
apiVersion: v1
kind: Service
metadata:
name: server
spec:
selector:
app: server
ports:
- protocol: TCP
port: 8085
targetPort: 8085
kubectl apply -f server.yaml
Podstawowa konfiguracja
Konfiguracja Fluent Bita składa się z czterech podstawowych sekcji:
- Service: definiuje globalne ustawienia instancji Fluent Bita.
- Input: określa źródła logów.
- Filter: definiuje reguły i operacje przetwarzania danych logów. To w tej sekcji można modyfikować lub filtrować strumień logów zgodnie z własnymi potrzebami zanim przekażemy je na wyjście.
- Output: definiuje miejsca docelowe dla przefiltrowanych i/lub zmodyfikowanych logów. Obsługiwane typy miejsc docelowych to np. pliki, połączenia sieciowe (TCP, UDP, HTTP), systemy SIEM czy bazy danych.

Krok I.
Na początek skonfigurujemy Fluent Bita w celu weryfikacji poprawności odczytu logów z naszego przykładowego serwisu. Zastosujemy prostą konfigurację, która przekaże logi do standardowego wyjścia. Umożliwi to obserwację logów w Podzie Fluent Bita, zapewniając wgląd w ich zawartość i poprawność działania.
fluent-bit.conf: |
[SERVICE]
Flush 5
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
Name tail
Tag kube.default.server
Path /var/log/containers/server*.log
Parser cri
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
[OUTPUT]
Name stdout
Match kube.default.server
Format json
json_date_key false
parsers.conf: |
[PARSER]
Name cri
Format regex
W sekcji SERVICE definiujemy globalne parametry Fluent Bita. Określamy tam interwał czasowy (Flush), który odpowiada za częstotliwość wysyłania logów na wyjście. Przykładowo, ustawienie flush: 1s spowoduje wysyłanie logów co sekundę.
Dodatkowo, w tej sekcji możemy włączyć oraz skonfigurować adres i port serwera, używany przez Kubernetes do sprawdzania, czy Fluent Bit jest aktywny i gotowy do obsługi żądań (Liveness i Readiness probe).
Rejestrujemy również parser, który poprawnie odczyta logi z kontenera.
W sekcji INPUT określamy między innymi typ i ścieżkę do pliku z logami. Ustawiamy też pole Tag, które razem z polem Match w innych sekcjach pozwala na łatwe filtrowanie logów.
W ostatniej sekcji OUTPUT również wskazujemy typ, a także format, w jakim logi będą przekazywane na wyjście. Konfiguracja naszego Fluent Bita znajduje się w Config Mapie, dlatego musimy ją zmodyfikować:
kubectl edit configmaps fluent-bit
i zresetować DameonSeta, aby nowa konfiguracja została dodana do Fluent Bita:
kubectl rollout restart daemonset fluent-bit
Gdy nowa konfiguracja zostanie wgrana, musimy przetestować, czy logi z naszego serwera pojawią się na standardowym wyjściu, czyli w logach Fluenta Bita.
kubectl logs -l app.kubernetes.io/name=fluent-bit -f
Przekierujmy port naszego serwera i wyślijmy zapytanie na nasz endpoint:
kubectl port-forward services/server 8085:8085
curl localhost:8085
Jeżeli wszystko działa poprawnie, powinniśmy zobaczyć logi przekierowane z naszego serwera.
Output
Fluent Bit oferuje szeroki wachlarz opcji OUTPUT, umożliwiając elastyczne przekazywanie przetworzonych danych. Dzięki temu, użytkownicy mogą dostosować miejsca docelowe do swoich konkretnych potrzeb integracyjnych i analitycznych. Użytkownicy mogą kierować logi do różnych systemów. Możliwa jest również konfiguracja wielu sekcji OUTPUT jednocześnie, co zapewnia pełną kontrolę nad dystrybucją logów.
W oficjalnej dokumentacji możemy znaleźć wiele przykładów integracji z różnymi serwisami. W naszym przykładzie chcemy skupić się na konfiguracji Fluent Bita, dlatego jako OUTPUT wykorzystamy nasz prosty server. Dodajmy kolejny Deployment z serwisem, który będzie odbierał przesłane logi:
apiVersion: apps/v1
kind: Deployment
metadata:
name: output
labels:
app: output
spec:
replicas: 1
selector:
matchLabels:
app: output
template:
metadata:
labels:
app: output
spec:
containers:
- name: output
image: pjasiak/log-server:0.1.0
ports:
- containerPort: 8085
---
apiVersion: v1
kind: Service
metadata:
name: output
spec:
selector:
app: output
ports:
- protocol: TCP
port: 8085
targetPort: 8085

Musimy wskazać Fluent Bitowi, aby wysyłał logi do naszego nowo powstałego serwisu. W konfiguracji podmieniamy standardowy Output na serwer przetwarzający nasze logi pod adresem output.default.svc.cluster.local:
fluent-bit.conf: |
[SERVICE]
Flush 5
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
Name tail
Tag kube.default.server
Path /var/log/containers/server*.log
Parser cri
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
[OUTPUT]
Name http
Format json
Match kube.default.server
Host output.default.svc.cluster.local
Port 8085
URI /logs
json_date_key false
Kiedy Fluent Bit uaktualni nową konfiguracje, logi z naszego serwera zaczną być przesyłane do serwisu „output”.
Przekierowywanie logów do różnych serwisów
Kolejnym krokiem będzie przekierowanie logów z różnych źródeł do różnych serwisów. W naszym prostym generatorze logów mamy dwa źródła logów:
- wygenerowane przez zewnętrzną bibliotekę oznaczone jako „[GIN]”
- oraz logi w postaci JSONa z takimi polami jak „time” czy „msg”.
Aby różne serwisy konsumowały różne rodzaje logów, potrzebujemy dodać osobny serwis, który wykorzysta dokładnie ten sam obraz naszej aplikacji:
apiVersion: apps/v1
kind: Deployment
metadata:
name: gin-output
labels:
app: gin-output
spec:
replicas: 1
selector:
matchLabels:
app: gin-output
template:
metadata:
labels:
app: gin-output
spec:
containers:
- name: gin-output
image: pjasiak/log-server:0.1.0
ports:
- containerPort: 8085
---
apiVersion: v1
kind: Service
metadata:
name: gin-output
spec:
selector:
app: gin-output
ports:
- protocol: TCP
port: 8085
targetPort: 8085

Musimy także zmodyfikować konfigurację naszego Fluent Bita:
fluent-bit.conf: |
[SERVICE]
Flush 5
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
Name tail
Tag kube.default.server
Path /var/log/containers/server*.log
Parser cri
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
[FILTER]
Name rewrite_tag
Match kube.default.server
RULE $message \[GIN\]. $TAG.gin true
[FILTER]
Name modify
Match kube.default.server.gin
Remove stream
Remove time
Remove logtag
[FILTER]
Name parser
Match kube.default.server
Key_Name message
Parser json
[FILTER]
Name rewrite_tag
Match kube.default.server
RULE $msg . $TAG.json false
[OUTPUT]
Name http
Format json
Match kube.default.server.json
Host output.default.svc.cluster.local
Port 8085
URI /logs
json_date_key false
[OUTPUT]
Name http
Format json
Match kube.default.server.gin
Host gin-output.default.svc.cluster.local
Port 8085
URI /logs
json_date_key false
Po pierwsze, wprowadziliśmy kilka segmentów „Filter”, które poniżej pokrótce omówię:
- pierwszy filtr sprawdza, czy w logu istnieje string [GIN] i jeżeli tak, to dodaje do takich logów przyrostek .gin ,
- w następnym filtrze we wszystkich logach z nowo utworzonym tagiem z przyrostkiem .gin usuwamy zbędne pola w calach czysto kosmetycznych,
- kolejny filtr parser wyciąga z jsona pole message i przekształca je ze stringa na json,
- w ostatnim filtrze sprawdzamy, czy nasz json posiada pole msg (co sugeruje, że nie został wygenerowany przez zewnętrzną bibliotekę) i jeżeli tak, dodajemy do takich tagów przyrostek .json,
W naszym pierwszym segmencie OUTPUT musimy teraz zmienić pole Match, tak aby zajmował się on tylko logami z tagiem kube.default.server.json.
Na koniec dodaliśmy drugi segment OUTPUT, w którym to logi z etykietą kube.default.server.gin zostaną przekierowane na inny adres docelowy: gin-output.default.svc.cluster.local
Po wygenerowaniu kolejnego loga, możemy prześledzić nasze serwisy końcowe. Każdy z nich powinien otrzymać różny rodzaj logów w zależności od ich rodzajów.

Podsumowanie
Powyższy artykuł prezentuje jedynie szczyptę możliwości Fluent Bita. Narzędzie to zachwyca szybkością i wydajnością. Dokładna dokumentacja i intuicyjny interfejs ułatwiają konfigurację, a mnogość wbudowanych opcji zapewnia szybką integrację z różnymi systemami dostępnymi na rynku.
***
Jeżeli interesują Cię inne narzędzia wykorzystywane w barnży IT, zajrzyj koniecznie również do pozostałych artykułów naszych ekspertów.
Zostaw komentarz