Fluentd: De ce este important să configurați tamponul de ieșire

Fluentd: De ce este important să configurați tamponul de ieșire

În zilele noastre, este imposibil să ne imaginăm un proiect bazat pe Kubernetes fără stiva ELK, care salvează jurnalele atât ale aplicațiilor, cât și ale componentelor de sistem ale clusterului. În practica noastră, folosim stiva EFK cu Fluentd în loc de Logstash.

Fluentd este un colector de jurnale modern, universal, care câștigă din ce în ce mai multă popularitate și s-a alăturat Fundației Cloud Native Computing, motiv pentru care vectorul său de dezvoltare se concentrează pe utilizarea împreună cu Kubernetes.

Faptul de a folosi Fluentd în loc de Logstash nu schimbă esența generală a pachetului software, cu toate acestea, Fluentd se caracterizează prin propriile nuanțe specifice rezultate din versatilitatea sa.

De exemplu, când am început să folosim EFK într-un proiect încărcat și cu o intensitate mare de logare, ne-am confruntat cu faptul că în Kibana unele mesaje au fost afișate în mod repetat de mai multe ori. În acest articol vă vom spune de ce apare acest fenomen și cum să rezolvați problema.

Problemă de duplicare a documentelor

În proiectele noastre, Fluentd este implementat ca DaemonSet (lansat automat într-o singură instanță pe fiecare nod al clusterului Kubernetes) și monitorizează jurnalele de containere stdout în /var/log/containers. După colectare și procesare, jurnalele sub formă de documente JSON sunt trimise către ElasticSearch, ridicate în formă de cluster sau de sine stătătoare, în funcție de amploarea proiectului și de cerințele de performanță și toleranță la erori. Kibana este folosit ca interfață grafică.

Când folosim Fluentd cu un plugin de buffering de ieșire, am întâlnit o situație în care unele documente din ElasticSearch aveau exact același conținut și diferă doar în identificator. Puteți verifica că aceasta este o repetare a mesajului folosind jurnalul Nginx ca exemplu. În fișierul jurnal, acest mesaj există într-o singură copie:

127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" -

Cu toate acestea, există mai multe documente în ElasticSearch care conțin acest mesaj:

{
  "_index": "test-custom-prod-example-2020.01.02",
  "_type": "_doc",
  "_id": "HgGl_nIBR8C-2_33RlQV",
  "_version": 1,
  "_score": 0,
  "_source": {
    "service": "test-custom-prod-example",
    "container_name": "nginx",
    "namespace": "test-prod",
    "@timestamp": "2020-01-14T05:29:47.599052886 00:00",
    "log": "127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00  0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" -",
    "tag": "custom-log"
  }
}

{
  "_index": "test-custom-prod-example-2020.01.02",
  "_type": "_doc",
  "_id": "IgGm_nIBR8C-2_33e2ST",
  "_version": 1,
  "_score": 0,
  "_source": {
    "service": "test-custom-prod-example",
    "container_name": "nginx",
    "namespace": "test-prod",
    "@timestamp": "2020-01-14T05:29:47.599052886 00:00",
    "log": "127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00  0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" -",
    "tag": "custom-log"
  }
}

În plus, pot exista mai mult de două repetări.

În timp ce remediați această problemă în jurnalele Fluentd, puteți vedea un număr mare de avertismente cu următorul conținut:

2020-01-16 01:46:46 +0000 [warn]: [test-prod] failed to flush the buffer. retry_time=4 next_retry_seconds=2020-01-16 01:46:53 +0000 chunk="59c37fc3fb320608692c352802b973ce" error_class=Fluent::Plugin::ElasticsearchOutput::RecoverableRequestFailure error="could not push logs to Elasticsearch cluster ({:host=>"elasticsearch", :port=>9200, :scheme=>"http", :user=>"elastic", :password=>"obfuscated"}): read timeout reached"

Aceste avertismente apar atunci când ElasticSearch nu poate returna un răspuns la o solicitare în timpul specificat de parametrul request_timeout, motiv pentru care fragmentul buffer redirecționat nu poate fi șters. După aceasta, Fluentd încearcă să trimită din nou fragmentul de buffer către ElasticSearch și după un număr arbitrar de încercări, operația se finalizează cu succes:

2020-01-16 01:47:05 +0000 [warn]: [test-prod] retry succeeded. chunk_id="59c37fc3fb320608692c352802b973ce" 
2020-01-16 01:47:05 +0000 [warn]: [test-prod] retry succeeded. chunk_id="59c37fad241ab300518b936e27200747" 
2020-01-16 01:47:05 +0000 [warn]: [test-dev] retry succeeded. chunk_id="59c37fc11f7ab707ca5de72a88321cc2" 
2020-01-16 01:47:05 +0000 [warn]: [test-dev] retry succeeded. chunk_id="59c37fb5adb70c06e649d8c108318c9b" 
2020-01-16 01:47:15 +0000 [warn]: [kube-system] retry succeeded. chunk_id="59c37f63a9046e6dff7e9987729be66f"

Cu toate acestea, ElasticSearch tratează fiecare dintre fragmentele de buffer transferate ca fiind unice și le atribuie valori unice de câmp _id în timpul indexării. Așa apar copiile mesajelor.

În Kibana arată astfel:

Fluentd: De ce este important să configurați tamponul de ieșire

Soluția

Există mai multe opțiuni pentru a rezolva această problemă. Unul dintre ele este mecanismul încorporat în pluginul fluent-plugin-elasticsearch pentru generarea unui hash unic pentru fiecare document. Dacă utilizați acest mecanism, ElasticSearch va recunoaște repetițiile în etapa de redirecționare și va preveni duplicarea documentelor. Dar trebuie să ținem cont că această metodă de rezolvare a problemei se luptă cu investigația și nu elimină eroarea cu lipsă de timeout, așa că am abandonat utilizarea ei.

Folosim un plugin de buffering la ieșirea Fluentd pentru a preveni pierderea jurnalelor în cazul unor probleme de rețea pe termen scurt sau a unei intensități crescute de înregistrare. Dacă dintr-un anumit motiv ElasticSearch nu poate scrie instantaneu un document în index, documentul este pus în coadă și stocat pe disc. Prin urmare, în cazul nostru, pentru a elimina sursa problemei care duce la eroarea descrisă mai sus, este necesar să setăm valorile corecte pentru parametrii de tamponare, la care tamponul de ieșire Fluentd va fi de dimensiune suficientă și în același timp reușesc să fie curățate în timpul alocat.

Este de remarcat faptul că valorile parametrilor discutați mai jos sunt individuale în fiecare caz specific de utilizare a buffer-ului în pluginurile de ieșire, deoarece depind de mulți factori: intensitatea scrierii mesajelor în jurnal pe servicii, performanța sistemului de disc, rețea. încărcarea canalului și lățimea de bandă a acestuia. Prin urmare, pentru a obține setări de buffer care sunt potrivite pentru fiecare caz în parte, dar nu redundante, evitând căutările îndelungate în orb, puteți utiliza informațiile de depanare pe care Fluentd le scrie în jurnalul său în timpul funcționării și puteți obține relativ rapid valorile corecte.

La momentul în care problema a fost înregistrată, configurația arăta astfel:

 <buffer>
        @type file
        path /var/log/fluentd-buffers/kubernetes.test.buffer
        flush_mode interval
        retry_type exponential_backoff
        flush_thread_count 2
        flush_interval 5s
        retry_forever
        retry_max_interval 30
        chunk_limit_size 8M
        queue_limit_length 8
        overflow_action block
      </buffer>

La rezolvarea problemei, valorile următorilor parametri au fost selectate manual:
chunk_limit_size — dimensiunea bucăților în care sunt împărțite mesajele din buffer.

  • flush_interval — interval de timp după care tamponul este șters.
  • queue_limit_length — numărul maxim de bucăți din coadă.
  • request_timeout este timpul pentru care se stabilește conexiunea între Fluentd și ElasticSearch.

Dimensiunea totală a tamponului poate fi calculată prin înmulțirea parametrilor queue_limit_length și chunk_limit_size, care pot fi interpretate ca „numărul maxim de bucăți din coadă, fiecare dintre ele având o dimensiune dată”. Dacă dimensiunea tamponului este insuficientă, în jurnale va apărea următorul avertisment:

2020-01-21 10:22:57 +0000 [warn]: [test-prod] failed to write data into buffer by buffer overflow action=:block

Înseamnă că bufferul nu are timp să fie șters în timpul alocat și datele care intră în bufferul complet sunt blocate, ceea ce va duce la pierderea unei părți din jurnalele.

Puteți crește tamponul în două moduri: mărind fie dimensiunea fiecărei bucăți din coadă, fie numărul de bucăți care pot fi în coadă.

Dacă setați dimensiunea chunk_limit_size la mai mult de 32 de megaocteți, atunci ElasticSeacrh nu o va accepta, deoarece pachetul primit va fi prea mare. Prin urmare, dacă trebuie să măriți în continuare tamponul, este mai bine să creșteți lungimea maximă a cozii de așteptare queue_limit_length.

Când tamponul nu mai depășește și rămâne doar mesajul timeout insuficient, puteți începe să creșteți parametrul request_timeout. Cu toate acestea, dacă setați valoarea la mai mult de 20 de secunde, următoarele avertismente vor începe să apară în jurnalele Fluentd:

2020-01-21 09:55:33 +0000 [warn]: [test-dev] buffer flush took longer time than slow_flush_log_threshold: elapsed_time=20.85753920301795 slow_flush_log_threshold=20.0 plugin_id="postgresql-dev" 

Acest mesaj nu afectează în niciun fel funcționarea sistemului și înseamnă că timpul de spălare a tamponului a durat mai mult decât setat de parametrul slow_flush_log_threshold. Acestea sunt informații de depanare și le folosim atunci când alegem valoarea parametrului request_timeout.

Algoritmul generalizat de selecție este următorul:

  1. Setați request_timeout la o valoare garantată a fi mai mare decât este necesar (sute de secunde). În timpul configurării, criteriul principal pentru setarea corectă a acestui parametru va fi dispariția avertismentelor despre lipsa timeout-ului.
  2. Așteptați mesajele despre depășirea pragului slow_flush_log_threshold. Textul de avertizare din câmpul elapsed_time va arăta timpul real în care a fost șters tamponul.
  3. Setați request_timeout la o valoare mai mare decât valoarea maximă elapsed_time obținută în timpul perioadei de observare. Calculăm valoarea request_timeout ca elapsed_time + 50%.
  4. Pentru a elimina din jurnal avertismentele despre spălarile lungi ale bufferului, puteți crește valoarea slow_flush_log_threshold. Calculăm această valoare ca elapsed_time + 25%.

Valorile finale ale acestor parametri, după cum sa menționat mai devreme, sunt obținute individual pentru fiecare caz. Urmând algoritmul de mai sus, suntem garantați să eliminăm eroarea care duce la mesaje repetate.

Tabelul de mai jos arată cum se modifică numărul de erori pe zi, ducând la duplicarea mesajelor, în procesul de selectare a valorilor parametrilor descriși mai sus:

nodul-1
nodul-2
nodul-3
nodul-4

Înainte după
Înainte după
Înainte după
Înainte după

nu a reușit să golească tamponul
1749/2
694/2
47/0
1121/2

reîncercarea a reușit
410/2
205/1
24/0
241/2

În plus, merită remarcat faptul că setările rezultate își pot pierde relevanța pe măsură ce proiectul crește și, în consecință, crește numărul de loguri. Semnul principal de expirare insuficientă este returnarea mesajelor despre o curățare lungă a bufferului în jurnalul Fluentd, adică depășirea pragului slow_flush_log_threshold. Din acest moment, mai există o marjă mică înainte de depășirea parametrului request_timeout, așa că este necesar să răspundeți la aceste mesaje în timp util și să repetați procesul de selectare a setărilor optime descrise mai sus.

Concluzie

Reglarea fină a bufferului de ieșire Fluentd este una dintre principalele etape de configurare a stivei EFK, determinând stabilitatea funcționării acestuia și plasarea corectă a documentelor în indexuri. Pe baza algoritmului de configurare descris, puteți fi sigur că toate jurnalele vor fi scrise în indexul ElasticSearch în ordinea corectă, fără repetări sau pierderi.

Citiți și alte articole de pe blogul nostru:

Sursa: www.habr.com

Adauga un comentariu