Praktična primjena ELK-a. Postavljanje logstash-a

Uvod

Prilikom postavljanja drugog sistema suočili smo se s potrebom obrade velikog broja raznih logova. ELK je izabran kao instrument. Ovaj članak će govoriti o našem iskustvu u postavljanju ovog steka.

Ne postavljamo za cilj da opišemo sve njegove mogućnosti, ali želimo da se koncentrišemo na rešavanje praktičnih problema. To je zbog činjenice da s dovoljno velikom količinom dokumentacije i gotovih slika postoji puno zamki, barem smo ih pronašli.

Stack smo postavili kroz docker-compose. Štaviše, imali smo dobro napisan docker-compose.yml koji nam je omogućio da podignemo stek gotovo bez problema. I činilo nam se da je pobjeda već blizu, sad ćemo je malo izvrnuti po našim potrebama i to je to.

Nažalost, pokušaj da se sistem podesi da prima i obrađuje logove iz naše aplikacije nije bio uspješan odmah. Stoga smo odlučili da je vrijedno proučavati svaku komponentu posebno, a zatim se vratiti na njihove veze.

Pa počnimo sa logstash-om.

Okruženje, implementacija, pokretanje Logstash-a u kontejneru

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

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

Koristićemo ga za eksperimente.

Da bismo pokrenuli logstash, napisali smo poseban docker-compose.yml. Naravno, bilo je moguće pokrenuti sliku iz komandne linije, ali ipak smo riješili konkretan zadatak, gdje nam se pokreće sve iz docker-composea.

Ukratko o konfiguracijskim datotekama

Kao što slijedi iz opisa, logstash se može pokrenuti kao za jedan kanal, u kom slučaju treba prenijeti *.conf datoteku ili za nekoliko kanala, u kom slučaju treba prenijeti datoteku pipelines.yml, koja zauzvrat , će se odnositi na datoteke .conf za svaki kanal.
Išli smo drugim putem. Činilo nam se svestranijim i skalabilnijim. Stoga smo kreirali pipelines.yml, i napravili direktorij pipelines u koji ćemo staviti .conf fajlove za svaki kanal.

Unutar kontejnera nalazi se još jedan konfiguracijski fajl - logstash.yml. Ne diramo ga, koristimo ga kakvog jeste.

Dakle, naša struktura direktorija je:

Praktična primjena ELK-a. Postavljanje logstash-a

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 prvo pokretanje. Zato što je početni zadatak pokretanje.

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

Šta vidimo ovdje?

  1. Mreže i volumeni su preuzeti iz originalnog docker-compose.yml (onog gdje se pokreće cijeli stek) i mislim da oni ovdje ne utiču mnogo na cjelokupnu sliku.
  2. Kreiramo jednu uslugu (usluge) logstash, od docker.elastic.co/logstash/logstash:6.3.2 slike i dajemo joj ime logstash_one_channel.
  3. Port 5046 prosljeđujemo unutar kontejnera, na isti interni port.
  4. Mi mapiramo našu ./config/pipelines.yml konfiguracijsku datoteku cijevi u /usr/share/logstash/config/pipelines.yml datoteku unutar kontejnera, gdje će je logstash pokupiti i učiniti je 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 činimo samo za čitanje.

Praktična primjena ELK-a. Postavljanje logstash-a

piping.yml fajl

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

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

I na kraju fajl "./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

šta vidimo?

Kontejner je počeo. Možemo provjeriti njegov rad:

echo '13123123123123123123123213123213' | nc localhost 5046

I vidimo odgovor u konzoli kontejnera:

Praktična primjena ELK-a. Postavljanje logstash-a

Ali u isto vrijeme vidimo i:

logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Nije moguće dohvatiti informacije o licenci sa servera licenci {:message=>"Elasticsearch nedostupno: [http://elasticsearch:9200/][Manticore ::ResolutionFailure]elasticsearch", ...

logstash_one_channel | [2019-04-29T11:28:59,894][INFO ][logstash.pipeline ] Pipeline je uspješno započeo {:pipeline_id=>".monitoring-logstash", :thread=>"# »}

logstash_one_channel | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Cjevovodi rade {:count=>2, :running_pipelines=>[:HABR, :".monitoring-logstash"], :non_running_pipelines=>[ ]}
logstash_one_channel | [2019-04-29T11:29:00,015][GREŠKA][logstash.inputs.metrics ] X-Pack je instaliran na Logstash, ali ne i na Elasticsearch. Molimo instalirajte X-Pack na Elasticsearch da biste koristili funkciju praćenja. Druge funkcije mogu biti dostupne.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Uspješno pokrenuta Logstash API krajnja tačka {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Pokretanje provjere zdravlja da se vidi da li Elasticsearch veza radi {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Pokušao da se oživi veza sa mrtvom ES instancom, ali se pojavila greška. {:url=>"elastična pretraga:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/Resolution][Manticore::ure] elasticsearch"}
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Pokretanje provjere zdravlja da se vidi da li Elasticsearch veza radi {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Pokušao da se oživi veza sa mrtvom ES instancom, ali se pojavila greška. {:url=>"elastična pretraga:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/Resolution][Manticore::ure] elasticsearch"}

A naš dnevnik puzi sve vrijeme.

Ovdje sam zelenom bojom istaknuo poruku da je cjevovod uspješno započeo, crvenom poruku o grešci i žutom poruku o pokušaju kontaktiranja elastična pretraga: 9200.
To se događa zbog činjenice da u logstash.conf koji je uključen u sliku postoji provjera dostupnosti elasticsearch. Uostalom, logstash pretpostavlja da radi kao dio Elk steka, a mi smo ga odvojili.

Možete raditi, ali nije zgodno.

Rješenje je da onemogućite ovu provjeru preko XPACK_MONITORING_ENABLED varijable okruženja.

Učinimo promjenu u docker-compose.yml i pokrenimo ga ponovo:

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. Kontejner je spreman za eksperimente.

Možemo ponovo ukucati 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 | }

Radite unutar jednog kanala

Pa smo počeli. Sada zapravo možete odvojiti vrijeme da direktno konfigurirate logstash. Hajde da za sada ne diramo fajl pipelines.yml, da vidimo šta možemo da dobijemo radom sa jednim kanalom.

Moram reći da je opći princip rada s konfiguracijskim fajlom kanala dobro opisan u službenom priručniku, ovdje ovdje
Ako želite da čitate na ruskom, onda smo koristili ovaj članak(ali sintaksa upita je stara, ovo morate uzeti u obzir).

Idemo redom od odjeljka Unos. Već smo vidjeli rad na tcp-u. Šta još tu može biti zanimljivo?

Testirajte poruke pomoću otkucaja srca

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

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

Upalimo ga, počinjemo 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 da primamo češće, moramo dodati parametar intervala.
Ovako ćemo primati poruku svakih 10 sekundi.

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

Preuzimanje podataka iz datoteke

Odlučili smo da pogledamo i režim datoteka. Ako dobro radi s datotekom, onda je moguće da agent nije potreban, pa, barem za lokalnu upotrebu.

Prema opisu, način rada bi trebao biti sličan tail -f, tj. čita nove redove ili, opciono, čita cijeli fajl.

Dakle, šta želimo da dobijemo:

  1. Želimo primiti redove koji su dodani jednom log fajlu.
  2. Želimo da primamo podatke koji su upisani u nekoliko log fajlova, a da pritom možemo da odvojimo šta je primljeno od kuda.
  3. Želimo da budemo sigurni da kada se logstash ponovo pokrene, neće ponovo primiti ove podatke.
  4. Želimo provjeriti da ako je logstash onemogućen, a podaci se i dalje upisuju u datoteke, onda ćemo te podatke primiti kada ga pokrenemo.

Da bismo sproveli eksperiment, dodajmo još jednu liniju 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 za unos u habr_pipeline.conf

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

počinjemo:

docker-compose up

Za kreiranje i pisanje log fajlova koristić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!

Istovremeno, vidimo da smo automatski dodali polje putanje. Tako da ćemo u budućnosti 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 na drugi fajl:

 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 | }

Odlično! Datoteka je podignuta, putanja je točno navedena, sve je u redu.

Zaustavite logstash i ponovo pokrenite. Sačekajmo. Tišina. One. Ove zapise više ne dobijamo.

A sada najhrabriji eksperiment.

Stavljamo logstash i izvršavamo:

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

Ponovo 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 | }

Ura! Sve se pokupilo.

No, potrebno je upozoriti na sljedeće. Ako se logstash kontejner ukloni (docker stop logstash_one_channel && docker rm logstash_one_channel), ništa se neće pokupiti. Pozicija datoteke do koje je pročitana pohranjena je unutar kontejnera. Ako krenete od nule, tada će prihvatiti samo nove linije.

Čitanje postojećih fajlova

Recimo da pokrećemo logstash po prvi put, ali već imamo dnevnike i želimo ih obraditi.
Ako pokrenemo logstash sa input sekcijom koju smo koristili gore, nećemo dobiti ništa. Logstash će obraditi samo nove redove.

Da biste izvukli linije iz postojećih datoteka, dodajte dodatni red u odjeljak za unos:

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

Štaviše, postoji nijansa, ovo utič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 uzimati samo nove zapise u njima.

Zaustavimo se na ovome proučavajući odjeljak za unos. Postoji još mnogo opcija, ali za sada imamo dovoljno za dalje eksperimente.

Usmjeravanje i transformacija podataka

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

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

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

Koristeći sekciju filtera, analiziraćemo dolaznu poruku, dobijajući od nje heš (parove ključ/vrijednost), s kojim već možemo raditi, tj. analizirati prema uslovima. A u izlaznom dijelu ćemo odabrati poruke i poslati svaku na svoj kanal.

Parsiranje poruke pomoću groka

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

Ne postavljajući sebi za cilj da to ovdje dam detaljan opis (za ovo se pozivam službena dokumentacija), dat ću svoj jednostavan primjer.

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

1 INFO poruka1
2 Poruka GREŠKE2

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

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

To će izgledati 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 druge šare, možete pogledati ovdje. ovdje

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

Oni će biti prikazani u izlaznoj sekciji.

Usmjeravanje poruka u izlaznoj sekciji pomoću naredbe if

U izlaznoj sekciji, kao što se sjećamo, htjeli smo podijeliti poruke u dva toka. Neke - koje su iNFO, mi ćemo izbaciti na konzolu, a sa greškama ćemo izvesti u datoteku.

Kako možemo podijeliti ove poruke? Uslov problema već sugerira rješenje - na kraju krajeva, već imamo namjensko polje message_type, koje može imati samo dvije vrijednosti INFO i ERROR. Na njemu ćemo napraviti izbor koristeći if naredbu.

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

Opis rada sa poljima i operaterima možete pronaći u ovom odeljku službeni priručnik.

Sada o samom zaključku.

Izlaz konzole, ovdje je sve jasno - stdout {}

Ali izlaz u datoteku - zapamtite da sve ovo pokrećemo iz kontejnera i da bi fajl u koji upisujemo rezultat bio dostupan izvana, trebamo otvoriti ovaj direktorij u docker-compose.yml.

Ukupno:

Izlazni dio 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

Počinjemo, pokušavamo, vidimo podelu na dva toka.

izvor: www.habr.com

Dodajte komentar