Praktična primjena ELK. Postavljanje logstasha

Uvod

Prilikom postavljanja drugog sustava suočili smo se s potrebom obrade velikog broja raznih zapisa. Kao instrument odabran je ELK. Ovaj članak će govoriti o našem iskustvu u postavljanju ovog niza.

Ne postavljamo cilj opisati sve njegove mogućnosti, već se želimo usredotočiti na rješavanje praktičnih problema. To je zbog činjenice da uz dovoljno veliku količinu dokumentacije i gotovih slika postoji puno zamki, barem smo ih mi pronašli.

Razmjestili smo stog kroz docker-compose. Štoviše, imali smo dobro napisan docker-compose.yml koji nam je omogućio podizanje stoga gotovo bez problema. I činilo nam se da je pobjeda već blizu, sad ćemo malo iskriviti kako treba i to je to.

Nažalost, pokušaj podešavanja sustava za primanje i obradu zapisa iz naše aplikacije nije bio uspješan odmah. Stoga smo odlučili da je vrijedno proučiti svaku komponentu zasebno, a zatim se vratiti na njihove veze.

Pa počnimo s logstashom.

Okruženje, implementacija, pokretanje Logstasha u spremniku

Za implementaciju koristimo docker-compose, ovdje opisani eksperimenti provedeni su na MacOS-u i Ubuntuu 18.0.4.

Logstash slika koju smo imali u našem izvornom docker-compose.yml je docker.elastic.co/logstash/logstash:6.3.2

Koristit ćemo ga za pokuse.

Da bismo pokrenuli logstash, napisali smo zasebni docker-compose.yml. Naravno, bilo je moguće pokrenuti sliku iz naredbenog retka, ali nakon svega riješili smo konkretan zadatak, gdje nam se pokreće sve od docker-composea.

Ukratko o konfiguracijskim datotekama

Kao što slijedi iz opisa, logstash se može pokrenuti kao za jedan kanal, u ovom slučaju treba prenijeti *.conf datoteku ili za više kanala, u kojem slučaju treba prenijeti pipelines.yml datoteku, koja pak , odnosit će se na datoteke .conf za svaki kanal.
Krenuli smo drugim putem. Činilo nam se svestranijim i skalabilnijim. Stoga smo kreirali pipelines.yml, te napravili direktorij pipelines u koji ćemo stavljati .conf datoteke za svaki kanal.

Unutar spremnika nalazi se još jedna konfiguracijska datoteka - logstash.yml. Ne diramo ga, koristimo ga onakvog kakav jest.

Dakle, naša struktura imenika je:

Praktična primjena ELK. Postavljanje logstasha

Za sada pretpostavljamo da je ovo tcp na portu 5046 za primanje ulaznih podataka, a za izlaz ćemo koristiti stdout.

Evo tako jednostavne konfiguracije za prvu vožnju. Budući da je početni zadatak pokrenuti.

Dakle, imamo ovaj docker-compose.yml

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      	- elk
    ports:
      	- 5046:5046
    volumes:
      	- ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
	- ./config/pipelines:/usr/share/logstash/config/pipelines:ro

Što vidimo ovdje?

  1. Mreže i volumeni su preuzeti iz originalnog docker-compose.yml (onog gdje se pokreće cijeli stog) i mislim da ne utječu puno na cjelokupnu sliku ovdje.
  2. Kreiramo jednu uslugu (usluge) logstash, iz slike docker.elastic.co/logstash/logstash:6.3.2 i dajemo joj ime logstash_one_channel.
  3. Port 5046 unutar kontejnera prosljeđujemo na isti interni port.
  4. Mapiramo našu konfiguracijsku datoteku cijevi ./config/pipelines.yml u datoteku /usr/share/logstash/config/pipelines.yml unutar spremnika, gdje će je logstash preuzeti i učiniti samo za čitanje, za svaki slučaj.
  5. Mapiramo direktorij ./config/pipelines, gdje imamo konfiguracijske datoteke cijevi, u direktorij /usr/share/logstash/config/pipelines i također ga postavljamo samo za čitanje.

Praktična primjena ELK. Postavljanje logstasha

piping.yml datoteku

- pipeline.id: HABR
  pipeline.workers: 1
  pipeline.batch.size: 1
  path.config: "./config/pipelines/habr_pipeline.conf"

Opisuje jedan kanal s HABR identifikatorom i put do njegove konfiguracijske datoteke.

I na kraju datoteka "./config/pipelines/habr_pipeline.conf"

input {
  tcp {
    port => "5046"
   }
  }
filter {
  mutate {
    add_field => [ "habra_field", "Hello Habr" ]
    }
  }
output {
  stdout {
      
    }
  }

Za sada nećemo ulaziti u njegov opis, pokušavamo pokrenuti:

docker-compose up

Što vidimo?

Kontejner je počeo. Njegov rad možemo provjeriti:

echo '13123123123123123123123213123213' | nc localhost 5046

I vidimo odgovor u konzoli spremnika:

Praktična primjena ELK. Postavljanje logstasha

Ali u isto vrijeme, također vidimo:

logstash_jedan_kanal | [2019-04-29T11:28:59,790][GREŠKA][logstash.licensechecker.licensereader] Nije moguće dohvatiti informacije o licenci s poslužitelja licence {:message=>"Elasticsearch nedostupan: [http://elasticsearch:9200/][Manticore ::ResolutionFailure]elasticsearch", ...

logstash_jedan_kanal | [2019-04-29T11:28:59,894][INFO ][logstash.pipeline ] Cjevovod je uspješno pokrenut {:pipeline_id=>".monitoring-logstash", :thread=>"# »}

logstash_jedan_kanal | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Cjevovodi rade {:count=>2, :running_pipelines=>[:HABR, :".monitoring-logstash"], :non_running_pipelines=>[ ]}
logstash_jedan_kanal | [2019-04-29T11:29:00,015][GREŠKA][logstash.inputs.metrics ] X-Pack je instaliran na Logstash, ali ne i na Elasticsearch. Instalirajte X-Pack na Elasticsearch za korištenje značajke nadzora. Druge značajke mogu biti dostupne.
logstash_jedan_kanal | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Uspješno pokrenuta Logstash API krajnja točka {:port=>9600}
logstash_jedan_kanal | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Pokreće se provjera ispravnosti da se vidi radi li Elasticsearch veza {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_jedan_kanal | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Pokušao sam oživjeti vezu s mrtvom ES instancom, ali dobio sam pogrešku. {:url=>"elastično pretraživanje:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch nedostupan: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elastično pretraživanje"}
logstash_jedan_kanal | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Izvršavam provjeru ispravnosti da vidim radi li Elasticsearch veza {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_jedan_kanal | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Pokušao sam oživjeti vezu s mrtvom ES instancom, ali dobio sam pogrešku. {:url=>"elastično pretraživanje:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch nedostupan: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elastično pretraživanje"}

A naš balvan stalno puzi gore.

Ovdje sam označio zelenom bojom poruku da je cjevovod uspješno pokrenut, crvenom bojom poruku o pogrešci i žutom bojom poruku o pokušaju kontaktiranja elastično pretraživanje: 9200.
To se događa zbog činjenice da u logstash.conf-u uključenom u sliku postoji provjera dostupnosti elasticsearch-a. Uostalom, logstash pretpostavlja da radi kao dio Elk steka, a mi smo ga odvojili.

Možete raditi, ali nije zgodno.

Rješenje je onemogućiti ovu provjeru putem varijable okruženja XPACK_MONITORING_ENABLED.

Napravimo promjenu u docker-compose.yml i ponovno ga pokrenimo:

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      - elk
    environment:
      XPACK_MONITORING_ENABLED: "false"
    ports:
      - 5046:5046
   volumes:
      - ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
      - ./config/pipelines:/usr/share/logstash/config/pipelines:ro

Sada je sve u redu. Spremnik je spreman za eksperimente.

Možemo ponovno upisati u susjednu konzolu:

echo '13123123123123123123123213123213' | nc localhost 5046

I vidi:

logstash_one_channel | {
logstash_one_channel |         "message" => "13123123123123123123123213123213",
logstash_one_channel |      "@timestamp" => 2019-04-29T11:43:44.582Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |            "host" => "gateway",
logstash_one_channel |            "port" => 49418
logstash_one_channel | }

Rad unutar jednog kanala

Pa smo počeli. Sada zapravo možete odvojiti vrijeme za izravnu konfiguraciju logstasha. Nemojmo za sada dirati datoteku pipelines.yml, da vidimo što možemo dobiti radom s jednim kanalom.

Moram reći da je opći princip rada s konfiguracijskom datotekom kanala dobro opisan u službenom priručniku, ovdje здесь
Ako želite čitati na ruskom, onda smo koristili ovaj članak(ali sintaksa upita je tamo stara, morate to uzeti u obzir).

Krenimo redom od odjeljka Unos. Već smo vidjeli rad na tcp-u. Što još ovdje može biti zanimljivo?

Testirajte poruke pomoću otkucaja srca

Postoji tako zanimljiva mogućnost generiranja automatskih testnih poruka.
Da biste to učinili, trebate uključiti dodatak heartbean u odjeljak za unos.

input {
  heartbeat {
    message => "HeartBeat!"
   }
  } 

Uključimo ga, počnemo primati jednom u minuti

logstash_one_channel | {
logstash_one_channel |      "@timestamp" => 2019-04-29T13:52:04.567Z,
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |         "message" => "HeartBeat!",
logstash_one_channel |        "@version" => "1",
logstash_one_channel |            "host" => "a0667e5c57ec"
logstash_one_channel | }

Želimo primati češće, moramo dodati parametar intervala.
Ovako ćemo svakih 10 sekundi dobiti poruku.

input {
  heartbeat {
    message => "HeartBeat!"
    interval => 10
   }
  }

Dobivanje podataka iz datoteke

Također smo odlučili pogledati način rada datoteke. Ako dobro radi s datotekom, onda je moguće da nije potreban agent, pa, barem za lokalnu upotrebu.

Prema opisu, način rada bi trebao biti sličan tail -f, t.j. čita nove retke ili, izborno, čita cijelu datoteku.

Dakle, što želimo dobiti:

  1. Želimo primati retke koji su dodani u jednu datoteku dnevnika.
  2. Želimo primati podatke koji su zapisani u nekoliko log datoteka, a pritom moći razdvojiti što je odakle primljeno.
  3. Želimo biti sigurni da kada se logstash ponovno pokrene, neće ponovno primiti ove podatke.
  4. Želimo provjeriti da ćemo, ako je logstash onemogućen, a podaci se nastave pisati u datoteke, kada ga pokrenemo, primiti te podatke.

Da bismo proveli eksperiment, dodajmo još jedan redak u docker-compose.yml, otvarajući direktorij u koji smo stavili datoteke.

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      - elk
    environment:
      XPACK_MONITORING_ENABLED: "false"
    ports:
      - 5046:5046
   volumes:
      - ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
      - ./config/pipelines:/usr/share/logstash/config/pipelines:ro
      - ./logs:/usr/share/logstash/input

I promijenite odjeljak unosa u habr_pipeline.conf

input {
  file {
    path => "/usr/share/logstash/input/*.log"
   }
  }

Počinjemo:

docker-compose up

Za izradu i pisanje log datoteka koristit ćemo naredbu:


echo '1' >> logs/number1.log

{
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:28:53.876Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |         "message" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number1.log"
logstash_one_channel | }

Da, radi!

U isto vrijeme vidimo da smo automatski dodali polje staze. Dakle, u budućnosti ćemo moći filtrirati zapise po njemu.

Pokušajmo ponovo:

echo '2' >> logs/number1.log

{
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:28:59.906Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |         "message" => "2",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number1.log"
logstash_one_channel | }

A sada drugu datoteku:

 echo '1' >> logs/number2.log

{
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:29:26.061Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |         "message" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number2.log"
logstash_one_channel | }

Sjajno! Datoteka je preuzeta, put je točno naveden, sve je u redu.

Zaustavite logstash i ponovno ga pokrenite. Pričekajmo. Tišina. Oni. Ove zapise više ne primamo.

A sada najhrabriji eksperiment.

Stavljamo logstash i izvršavamo:

echo '3' >> logs/number2.log
echo '4' >> logs/number1.log

Ponovno pokrenite logstash i pogledajte:

logstash_one_channel | {
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |         "message" => "3",
logstash_one_channel |        "@version" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number2.log",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:48:50.589Z
logstash_one_channel | }
logstash_one_channel | {
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |         "message" => "4",
logstash_one_channel |        "@version" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number1.log",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:48:50.856Z
logstash_one_channel | }

hura! Sve pokupljeno.

No, potrebno je upozoriti na sljedeće. Ako se logstash spremnik ukloni (docker stop logstash_one_channel && docker rm logstash_one_channel), ništa se neće preuzeti. Pozicija datoteke do koje je čitana pohranjena je unutar spremnika. Ako počnete od nule, prihvatit će samo nove retke.

Čitanje postojećih datoteka

Recimo da prvi put pokrećemo logstash, ali već imamo zapise i željeli bismo ih obraditi.
Ako pokrenemo logstash s odjeljkom za unos koji smo koristili gore, nećemo dobiti ništa. Logstash će obraditi samo nove retke.

Kako biste povukli retke iz postojećih datoteka, dodajte dodatni red u odjeljak za unos:

input {
  file {
    start_position => "beginning"
    path => "/usr/share/logstash/input/*.log"
   }
  }

Štoviše, postoji nijansa, ovo utječe samo na nove datoteke koje logstash još nije vidio. Za iste datoteke koje su već bile u vidnom polju logstash-a, on je već zapamtio njihovu veličinu i sada će u njima uzimati samo nove zapise.

Zaustavimo se na ovome proučavanjem odjeljka za unos. Ima još puno opcija, ali za sada imamo dovoljno za daljnje eksperimente.

Usmjeravanje i transformacija podataka

Pokušajmo riješiti sljedeći problem, recimo da imamo poruke s jednog kanala, neke od njih su informativne, a neke poruke o pogrešci. Razlikuju se po oznaci. Neki su INFO, drugi su POGREŠKA.

Moramo ih razdvojiti na izlazu. Oni. Na jednom kanalu pišemo informativne poruke, a na drugom poruke o pogreškama.

Da biste to učinili, idite iz odjeljka za unos na filter i izlaz.

Koristeći odjeljak filtera, analizirat ćemo dolaznu poruku, dobivajući iz nje hash (parove ključ-vrijednost), s kojim već možemo raditi, tj. raščlaniti prema uvjetima. A u izlaznom odjeljku ćemo odabrati poruke i poslati svaku na svoj kanal.

Raščlanjivanje poruke s grok

Kako biste raščlanili tekstualne nizove i dobili skup polja iz njih, postoji poseban dodatak u odjeljku filtera - grok.

Ne postavljajući sebi cilj da ga ovdje detaljno opišem (za ovo se pozivam na službena dokumentacija), dat ću svoj jednostavan primjer.

Da biste to učinili, morate odlučiti o formatu ulaznih redaka. Ja ih imam ovako:

1 INFO poruka1
2 Poruka o POGREŠCI2

Oni. Prvo identifikator, pa INFO/GREŠKA, pa neka riječ bez razmaka.
Nije teško, ali dovoljno za razumijevanje principa rada.

Dakle, u odjeljku filtera, u dodatku grok, moramo definirati obrazac za raščlanjivanje naših nizova.

Izgledat će ovako:

filter {
  grok {
    match => { "message" => ["%{INT:message_id} %{LOGLEVEL:message_type} %{WORD:message_text}"] }
   }
  } 

U osnovi, to je regularni izraz. Koriste se gotovi obrasci kao što su INT, LOGLEVEL, WORD. Njihov opis, kao i ostale krojeve, možete pogledati ovdje. здесь

Sada, prolazeći kroz ovaj filter, naš niz će se pretvoriti u hash tri polja: message_id, message_type, message_text.

Oni će biti prikazani u izlaznom odjeljku.

Usmjeravanje poruka u izlazni odjeljak pomoću naredbe if

U izlaznom dijelu, kao što se sjećamo, namjeravali smo podijeliti poruke u dva toka. Neke - koje su iNFO, ispisat ćemo u konzolu, a s pogreškama u datoteku.

Kako možemo podijeliti te poruke? Uvjet problema već sugerira rješenje - nakon svega, već imamo namjensko polje message_type, koje može uzeti samo dvije vrijednosti INFO i ERROR. Na njemu ćemo napraviti izbor pomoću naredbe if.

if [message_type] == "ERROR" {
        # Здесь выводим в файл
       } else
     {
      # Здесь выводим в stdout
    }

Opis rada s poljima i operatorima nalazi se u ovom odjeljku službeni priručnik.

E sad o samom zaključku.

Konzolni izlaz, ovdje je sve jasno - stdout {}

Ali izlaz u datoteku - zapamtite da sve ovo pokrećemo iz spremnika i da bi datoteka u koju pišemo rezultat bila dostupna izvana, moramo otvoriti ovaj direktorij u docker-compose.yml.

Ukupno:

Izlazni odjeljak naše datoteke izgleda ovako:


output {
  if [message_type] == "ERROR" {
    file {
          path => "/usr/share/logstash/output/test.log"
          codec => line { format => "custom format: %{message}"}
         }
    } else
     {stdout {
             }
     }
  }

Dodajte još jedan volumen u docker-compose.yml za izlaz:

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      - elk
    environment:
      XPACK_MONITORING_ENABLED: "false"
    ports:
      - 5046:5046
   volumes:
      - ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
      - ./config/pipelines:/usr/share/logstash/config/pipelines:ro
      - ./logs:/usr/share/logstash/input
      - ./output:/usr/share/logstash/output

Krećemo, pokušavamo, vidimo podjelu na dvije struje.

Izvor: www.habr.com

Dodajte komentar