Fluent Bit: žurnalų procesorius

Kas tas Fluent Bit ir kodėl turėtum apie jį žinoti

Jei kada nors bandei susigaudyti, kas vyksta tavo aplikacijoje production aplinkoje, kai viskas griūva, tikrai žinai, kad logai yra tavo geriausias draugas. Bet kai tuos logus reikia rinkti iš dešimčių ar šimtų konteinerių, serverių ar mikroservisų, prasideda tikras galvos skausmas. Čia ir ateina į pagalbą Fluent Bit – kompaktiškas, greitas ir efektyvus log procesorius, kuris tapo neatsiejama daugelio modernių infrastruktūrų dalimi.

Fluent Bit yra atvirojo kodo duomenų rinkimo ir log’inimo įrankis, sukurtas būti lengvu ir našiu. Jis gimė kaip Cloud Native Computing Foundation (CNCF) projektas ir yra glaudžiai susijęs su Fluentd – jo „vyresniuoju broliu”. Tačiau skirtingai nuo Fluentd, Fluent Bit sukurtas būti kur kas mažesnis ir efektyvesnis resursų atžvilgiu, kas jį daro idealų sprendimą edge computing, embedded sistemoms ir konteinerizuotoms aplinkoms.

Pagrindinė Fluent Bit užduotis – rinkti duomenis iš įvairių šaltinių (logų failai, metrikos, sistemų įvykiai), juos apdoroti, filtruoti ir persiųsti į pasirinktas paskirties vietas. Tai gali būti Elasticsearch, InfluxDB, Kafka, S3 ar bet kuri kita populiari duomenų saugykla ar analizės platforma.

Architektūra ir veikimo principai

Fluent Bit architektūra yra paremta pipeline principu, kuris susideda iš kelių pagrindinių komponentų: Input, Parser, Filter, Buffer ir Output. Kiekvienas iš šių komponentų atlieka specifinę funkciją duomenų apdorojimo grandinėje.

Input pluginai yra atsakingi už duomenų rinkimą iš įvairių šaltinių. Tai gali būti failų skaitymas (tail), sistemų metrikų rinkimas, Docker ar Kubernetes logai, TCP/UDP srautai ir daug kitų variantų. Vienas iš didžiausių Fluent Bit privalumų – milžiniškas palaikomų input šaltinių skaičius.

Parser komponentas leidžia struktūrizuoti nestruktūrizuotus duomenis. Pavyzdžiui, jei tavo aplikacija generuoja JSON formato logus, parseris gali juos išskaidyti į atskirus laukus, kuriuos vėliau galėsi filtruoti ar transformuoti. Palaikomi įvairūs formatai: JSON, regex, LTSV ir kiti.

Filtrai – čia prasideda tikroji magija. Filtrai leidžia modifikuoti, praturtinti ar išmesti duomenis prieš juos siunčiant toliau. Gali pridėti papildomus laukus (pavyzdžiui, hostname ar environment), pakeisti reikšmes, išfiltruoti nereikalingus įrašus pagal tam tikras sąlygas ar net transformuoti duomenų struktūrą.

Output pluginai užtikrina, kad tavo duomenys pasiektų galutinę paskirties vietą. Fluent Bit palaiko dešimtis output variantų – nuo tradicinių failų ir duomenų bazių iki modernių cloud sprendimų ir streaming platformų.

Praktinis konfigūracijos pavyzdys

Teorija teorija, bet pažiūrėkime, kaip tai atrodo praktikoje. Štai paprastas bet funkcionalus Fluent Bit konfigūracijos pavyzdys, kuris renka Docker konteinerių logus ir siunčia juos į Elasticsearch:

[SERVICE]
    Flush        5
    Daemon       Off
    Log_Level    info

[INPUT]
    Name              tail
    Path              /var/lib/docker/containers/*/*.log
    Parser            docker
    Tag               docker.*
    Refresh_Interval  5

[FILTER]
    Name                kubernetes
    Match               docker.*
    Kube_URL            https://kubernetes.default.svc:443
    Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token

[FILTER]
    Name    modify
    Match   docker.*
    Add     environment production
    Add     cluster main-cluster

[OUTPUT]
    Name            es
    Match           *
    Host            elasticsearch.logging.svc.cluster.local
    Port            9200
    Index           fluent-bit
    Type            _doc
    Logstash_Format On

Šioje konfigūracijoje matome visą pipeline’ą: skaitome Docker logus, praturtinome juos Kubernetes metadata, pridedame papildomus laukus ir siunčiame į Elasticsearch su Logstash formato indeksavimu. Paprasta, bet galinga.

Kubernetes integracija ir DaemonSet deployment

Kubernetes aplinkoje Fluent Bit dažniausiai deployjamas kaip DaemonSet – tai reiškia, kad vienas Fluent Bit pod’as veikia kiekviename cluster node’e. Toks deployment modelis užtikrina, kad visi konteinerių logai būtų renkami nepriklausomai nuo to, kuriame node’e jie veikia.

Štai kaip atrodo bazinis Fluent Bit DaemonSet manifest’as:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    metadata:
      labels:
        app: fluent-bit
    spec:
      serviceAccountName: fluent-bit
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:2.1
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config

Svarbu suprasti, kad Fluent Bit reikia prieigos prie host sistemos direktorijų, kur saugomi konteinerių logai. Todėl naudojame hostPath volumes, kurie suteikia prieigą prie /var/log ir /var/lib/docker/containers.

Dar vienas svarbus aspektas – RBAC (Role-Based Access Control) konfigūracija. Fluent Bit reikia teisių skaityti Kubernetes API, kad galėtų praturtinti logus metadata apie pod’us, namespace’us ir kitus Kubernetes objektus:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluent-bit
  namespace: logging
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluent-bit-read
rules:
- apiGroups: [""]
  resources:
  - namespaces
  - pods
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: fluent-bit-read
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: fluent-bit-read
subjects:
- kind: ServiceAccount
  name: fluent-bit
  namespace: logging

Performance tuning ir resursų optimizavimas

Vienas iš pagrindinių Fluent Bit privalumų – jo efektyvumas. Tačiau netinkamai sukonfigūravus, net ir šis lengvasvoris įrankis gali tapti resursų rijiku. Štai keletas praktinių patarimų, kaip išspausti maksimalų našumą:

Buffer konfigūracija yra kritiškai svarbi. Fluent Bit naudoja memory ir filesystem bufferius duomenims laikyti prieš juos siunčiant į output. Jei tavo output paskirties vieta lėta ar laikinai nepasiekiama, bufferiai užpildys atmintį. Rekomenduoju naudoti filesystem bufferius production aplinkoje:

[SERVICE]
    storage.path /var/log/flb-storage/
    storage.sync normal
    storage.checksum off
    storage.max_chunks_up 128

Flush intervalas nusako, kaip dažnai duomenys siunčiami į output. Mažesnis intervalas reiškia mažesnį latency, bet didesnį network overhead. Paprastai 5-10 sekundžių yra geras balansas:

[SERVICE]
    Flush 5

Mem_Buf_Limit parametras input lygyje riboja, kiek atminties gali naudoti konkretus input plugin’as. Tai apsaugo nuo situacijų, kai vienas input „suėda” visą atmintį:

[INPUT]
    Name tail
    Path /var/log/*.log
    Mem_Buf_Limit 5MB

Workers – nuo Fluent Bit 1.9 versijos galima konfigūruoti output workers skaičių, kas leidžia paralelizuoti duomenų siuntimą:

[OUTPUT]
    Name es
    Match *
    Host elasticsearch
    Workers 2

Praktikoje pastebėjau, kad Kubernetes aplinkoje Fluent Bit pod’ui pakanka 100-200Mi atminties ir 100-200m CPU limito normaliam darbui. Žinoma, tai priklauso nuo log’ų kiekio ir apdorojimo sudėtingumo.

Multiline logų apdorojimas

Viena iš dažniausių problemų, su kuriomis susiduriama renkant logus – multiline įrašai. Java stack trace’ai, Python exception’ai ar bet kokie kiti logai, kurie tęsiasi per kelias eilutes, gali tapti tikru košmaru, jei jų tinkamai neapdorosi.

Fluent Bit turi puikų multiline parser’į, kuris leidžia sujungti kelias eilutes į vieną log įrašą. Štai pavyzdys, kaip apdoroti Java stack trace’us:

[MULTILINE_PARSER]
    name          java_multiline
    type          regex
    flush_timeout 1000
    rule          "start_state" "/^\d{4}-\d{2}-\d{2}/" "cont"
    rule          "cont" "/^(?!\d{4}-\d{2}-\d{2})/" "cont"

[INPUT]
    Name              tail
    Path              /var/log/app.log
    multiline.parser  java_multiline

Šis parser’is naudoja regex taisykles nustatyti, kur prasideda naujas log įrašas (pagal datą formato pradžioje) ir tęsia rinkti eilutes, kol nepasirodo kitas įrašas su data.

Alternatyvus būdas – naudoti multiline filter’į, kuris veikia po duomenų surinkimo:

[FILTER]
    Name                multiline
    Match               *
    multiline.key_content log
    multiline.parser    java_multiline

Svarbu suprasti skirtumą: multiline input lygyje apdoroja duomenis jų rinkimo metu, o filter’is – jau surinktus duomenis. Input lygio apdorojimas paprastai efektyvesnis, bet filter’is suteikia daugiau lankstumo.

Troubleshooting ir debugging patarimai

Net ir su geriausiai sukonfigūruotu Fluent Bit, kartais kažkas nepavyksta. Štai keletas patikrintų būdų, kaip diagnozuoti problemas:

Log level kėlimas – pirmasis žingsnis visada turėtų būti debug režimo įjungimas:

[SERVICE]
    Log_Level debug

Tai išspausdins daug daugiau informacijos apie tai, kas vyksta Fluent Bit viduje. Tik nepamirškite grąžinti į info ar warn production’e, nes debug logai gali būti labai verbose.

HTTP server įjungimas leidžia gauti realtime metrikas ir health check endpoint’ą:

[SERVICE]
    HTTP_Server  On
    HTTP_Listen  0.0.0.0
    HTTP_Port    2020

Tada galite pasiekti http://localhost:2020/api/v1/metrics ir pamatyti detalizuotas metrikas apie kiekvieną plugin’ą – kiek įrašų apdorota, kiek klaidų įvyko, buffer’ių būsena ir t.t.

Stdout output testavimui – kai neveikia output į Elasticsearch ar kitą sistemą, laikinai pridėkite stdout output’ą, kad pamatytumėte, ar duomenys iš viso pasiekia output stage’ą:

[OUTPUT]
    Name   stdout
    Match  *
    Format json_lines

Tag matching tikrinimas – dažna klaida yra neteisingi tag’ai ar match pattern’ai. Įsitikinkite, kad jūsų filter’iai ir output’ai tikrai atitinka input tag’us. Galite naudoti wildcard’us: Match kube.* atitiks visus tag’us, prasidedančius „kube.”.

Dar vienas naudingas triukas – naudoti rewrite_tag filter’į, kad geriau kontroliuotumėte duomenų srautą:

[FILTER]
    Name    rewrite_tag
    Match   kube.*
    Rule    $kubernetes['namespace_name'] ^production$ production.logs false
    Rule    $kubernetes['namespace_name'] ^staging$ staging.logs false

Tai leidžia perrašyti tag’us pagal tam tikras sąlygas ir nukreipti skirtingus duomenis į skirtingus output’us.

Alternatyvos ir kada rinktis Fluent Bit

Būtų nesąžininga nekalbėti apie alternatyvas. Log’ų rinkimo ir apdorojimo srityje yra nemažai konkurentų: Fluentd, Logstash, Filebeat, Vector ir kiti. Kiekvienas turi savo nišą ir privalumus.

Fluentd vs Fluent Bit – tai dažniausias palyginimas. Fluentd yra brandus, funkcionalus ir turi didžiulę plugin’ų ekosistemą. Tačiau jis parašytas Ruby ir naudoja daugiau resursų. Fluent Bit parašytas C kalba, yra kur kas lengvesnis (apie 450KB binary) ir efektyvesnis. Tipinis use case’as: naudoti Fluent Bit edge’e (Kubernetes node’uose, IoT įrenginiuose) duomenų rinkimui ir pradiniam apdorojimui, o Fluentd – centralizuotam apdorojimui ir agregavimui.

Filebeat – Elastic stack dalis, puikiai integruojasi su Elasticsearch. Jei jau naudojate ELK stack’ą ir jums nereikia siųsti duomenų į kitas sistemas, Filebeat gali būti paprastesnis pasirinkimas. Tačiau Fluent Bit yra vendor-neutral ir lankstesnis.

Vector – naujesnis žaidėjas, parašytas Rust kalba. Turi įspūdingą našumą ir modernią architektūrą. Tačiau ekosistema dar ne tokia brandi kaip Fluent Bit, ir community mažesnė.

Logstash – galingas, bet sunkus. Jei jums reikia sudėtingo duomenų transformavimo ir praturtinimo, Logstash gali būti geras pasirinkimas. Bet Kubernetes aplinkoje jis dažniausiai per daug „turtingas” paprastam log’ų rinkimui.

Fluent Bit yra idealus pasirinkimas, kai:
– Dirbate su Kubernetes ar konteinerizuota aplinka
– Svarbu efektyvus resursų naudojimas
– Reikia siųsti duomenis į kelias skirtingas sistemas
– Norite CNCF palaikomo, aktyviai vystomo projekto
– Reikia edge computing ar embedded sistemų palaikymo

Ką reikia žinoti prieš pradedant gamyboje

Prieš deployjant Fluent Bit production aplinkoje, yra keletas dalykų, kuriuos būtina apgalvoti ir sukonfigūruoti.

High availability – nors Fluent Bit kaip DaemonSet jau yra distributed, reikia pagalvoti apie output pasiekiamumą. Jei jūsų Elasticsearch ar kita paskirties sistema nukrenta, kas nutinka su log’ais? Filesystem buffer’iai padeda, bet jie turi limitus. Apsvarstykite multi-output konfigūraciją arba intermediate queue (pvz., Kafka) naudojimą.

Security – logai dažnai turi sensitive informacijos. Įsitikinkite, kad:
– Naudojate TLS komunikacijai su output sistemomis
– Filtruojate sensitive duomenis (slaptažodžius, token’us) prieš siunčiant
– Tinkamai sukonfigūravote RBAC Kubernetes’e
– Apsvarstote log’ų encryption at rest

Štai pavyzdys, kaip išfiltruoti sensitive laukus:

[FILTER]
    Name    modify
    Match   *
    Remove  password
    Remove  token
    Remove  api_key

Monitoring – Fluent Bit pats turi būti monitorinamas. Eksportuokite jo metrikas į Prometheus:

[INPUT]
    Name            prometheus_scrape
    Host            127.0.0.1
    Port            2020
    Scrape_interval 30s

Stebėkite tokius dalykus kaip:
– Buffer’ių užpildymas
– Output retry rate
– Dropped records
– Memory ir CPU naudojimas

Version management – Fluent Bit aktyviai vystomas, naujos versijos išleidžiamos reguliariai. Turėkite aiškų upgrade procesą ir testuokite naujus release’us staging aplinkoje prieš keliant į production. Breaking changes pasitaiko, nors ir retai.

Costs – nors Fluent Bit pats nemokamas, duomenų saugojimas ir perdavimas gali kainuoti. Apskaičiuokite, kiek log’ų generuojate per dieną ir kiek tai kainuos jūsų pasirinktoje storage platformoje. Kartais verta investuoti į agresyvesnį filtravimą ar sampling’ą, kad sumažintumėte kiekį.

Retention policy – neužmirškite sukonfigūruoti, kiek laiko saugoti logus. Elasticsearch index’ai gali greitai išpūsti diskus. Naudokite ILM (Index Lifecycle Management) ar panašius mechanizmus automatiniam senų log’ų valymui.

Realūs scenarijai ir best practices iš praktikos

Per kelerius metus dirbant su Fluent Bit įvairiuose projektuose, susiformavo keletas patikrintų pattern’ų ir best practices, kurie veikia realybėje, ne tik teorijoje.

Namespace segregacija – jei turite multi-tenant Kubernetes cluster’į, būtinai atskiriate log’us pagal namespace’us jau Fluent Bit lygyje:

[FILTER]
    Name    kubernetes
    Match   kube.*
    
[FILTER]
    Name    rewrite_tag
    Match   kube.*
    Rule    $kubernetes['namespace_name'] ^prod-(.*)$ prod.$TAG true
    Rule    $kubernetes['namespace_name'] ^dev-(.*)$ dev.$TAG true

[OUTPUT]
    Name  es
    Match prod.*
    Host  prod-elasticsearch
    Index prod-logs

[OUTPUT]
    Name  es
    Match dev.*
    Host  dev-elasticsearch
    Index dev-logs

Taip užtikrinate, kad production ir development logai nepasimišo ir gali turėti skirtingas retention policies.

Structured logging enforcement – skatinkite development komandas naudoti structured logging (JSON). Tai labai palengvina log’ų analizę. Fluent Bit gali padėti konvertuoti plain text logus į JSON, bet geriau, kad aplikacijos iš karto generuotų struktūrizuotus logus.

Centralizuotas konfigūracijos valdymas – naudokite ConfigMap’us Kubernetes’e ir version control’ę juos Git’e. Tai leidžia track’inti konfigūracijos pakeitimus ir greitai rollback’inti, jei kas negerai:

kubectl create configmap fluent-bit-config \
  --from-file=fluent-bit.conf \
  --from-file=parsers.conf \
  -n logging

Gradual rollout – kai keičiate Fluent Bit konfigūraciją ar versiją, nedarykite to visame cluster’yje iš karto. Naudokite node selector’ius ar taints/tolerations, kad pirmiausia išbandytumėte pokyčius mažoje node’ų grupėje:

spec:
  template:
    spec:
      nodeSelector:
        fluent-bit-version: v2

Log sampling – jei turite labai verbose aplikacijų (pvz., debug logai production’e), apsvarstykite sampling’ą. Galite naudoti grep filter’į su regex, kad išlaikytumėte tik tam tikrą procentą log’ų:

[FILTER]
    Name    grep
    Match   kube.*
    Regex   log ^(?!.*debug).*$

Arba dar geriau – naudokite throttle filter’į, kuris leidžia kontroliuoti log’ų rate per laiko vienetą:

[FILTER]
    Name     throttle
    Match    kube.*
    Rate     1000
    Window   60
    Interval 1m

Tai leis praėjti maksimaliai 1000 log įrašų per minutę iš kiekvieno šaltinio.

Disaster recovery – turėkite backup planą. Jei viskas sugenda ir prarandate logus, kas nutinka? Apsvarstykite dual-write strategiją – siųskite logus į dvi skirtingas sistemas arba bent jau į S3 kaip backup:

[OUTPUT]
    Name  es
    Match *
    Host  primary-elasticsearch

[OUTPUT]
    Name  s3
    Match *
    bucket backup-logs
    region eu-west-1
    store_dir /tmp/fluent-bit/s3
    total_file_size 100M
    upload_timeout 10m

Praktikoje pastebėjau, kad organizacijos, kurios rimtai traktuoja logging infrastruktūrą, turi atskirą komandą ar bent dedicated žmogų, kuris už tai atsakingas. Logging nėra „set and forget” dalykas – tai gyvybiškai svarbi infrastruktūros dalis, kuri reikalauja nuolatinio dėmesio ir optimizavimo.

Dar vienas insight’as – investuokite į mokymą. Įsitikinkite, kad jūsų development ir operations komandos supranta, kaip veikia logging pipeline’as, kaip rašyti efektyvias queries, kaip debug’inti problemas naudojant logus. Geriausia logging infrastruktūra pasaulyje nenaudinga, jei niekas nemoka ja naudotis.

Galiausiai, nepamirškite, kad Fluent Bit yra tik įrankis. Jis neišspręs problemų, kurios kyla iš blogai suprojektuotos aplikacijos ar chaotiško logging’o. Prieš optimizuojant log’ų rinkimą, įsitikinkite, kad pačios aplikacijos generuoja prasmingas, struktūrizuotas ir tinkamo lygio logus. Kartais geriau investuoti laiką į aplikacijos logging strategijos pagerinimą nei bandyti išspręsti viską Fluent Bit konfigūracija.

Daugiau

DNS per HTTPS konfigūravimas