Praktiese toepassing van ELK. Stel logstash op

Inleiding

Terwyl ons 'n ander stelsel ontplooi het, het ons die behoefte gehad om 'n groot aantal verskillende logs te verwerk. ELK is as die instrument gekies. Hierdie artikel sal praat oor ons ervaring met die opstel van hierdie stapel.

Ons stel nie 'n doelwit om al sy vermoëns te beskryf nie, maar ons wil daarop konsentreer om praktiese probleme op te los. Dit is te wyte aan die feit dat daar met 'n voldoende groot hoeveelheid dokumentasie en klaargemaakte beelde baie slaggate is, ten minste het ons dit gevind.

Ons het die stapel ontplooi deur docker-compose. Boonop het ons 'n goedgeskrewe docker-compose.yml gehad wat ons toegelaat het om die stapel met byna geen probleme te verhoog nie. En dit het vir ons gelyk asof die oorwinning reeds naby was, nou sal ons dit 'n bietjie verdraai om by ons behoeftes te pas en dit is dit.

Ongelukkig was 'n poging om die stelsel in te stel om logs van ons toepassing te ontvang en te verwerk, nie dadelik suksesvol nie. Daarom het ons besluit dat dit die moeite werd is om elke komponent afsonderlik te bestudeer, en dan terug te keer na hul verbindings.

So kom ons begin met logstash.

Omgewing, ontplooiing, loop Logstash in 'n houer

Vir ontplooiing gebruik ons ​​docker-compose, die eksperimente wat hier beskryf word, is op MacOS en Ubuntu 18.0.4 uitgevoer.

Die logstash-prent wat ons in ons oorspronklike docker-compose.yml gehad het, is docker.elastic.co/logstash/logstash:6.3.2

Ons sal dit vir eksperimente gebruik.

Om logstash te laat loop, het ons 'n aparte docker-compose.yml geskryf. Natuurlik was dit moontlik om die prent vanaf die opdragreël te begin, maar ons het immers 'n spesifieke taak opgelos, waar alles van docker-compose vir ons geloods word.

Kortliks oor konfigurasielêers

Soos volg uit die beskrywing, kan logstash uitgevoer word soos vir een kanaal, in hierdie geval moet dit die *.conf lêer oordra of vir verskeie kanale, in welke geval dit die pipelines.yml lêer moet oordra, wat op sy beurt weer , sal verwys na lêers .conf vir elke kanaal.
Ons het die tweede pad gevat. Dit het vir ons meer veelsydig en skaalbaar gelyk. Daarom het ons pipelines.yml geskep en 'n pypleidinggids gemaak waarin ons .conf-lêers vir elke kanaal sal plaas.

Binne die houer is daar nog 'n konfigurasielêer - logstash.yml. Ons raak nie daaraan nie, ons gebruik dit soos dit is.

Ons gidsstruktuur is dus:

Praktiese toepassing van ELK. Stel logstash op

Vir eers neem ons aan dat dit tcp op poort 5046 is om insetdata te ontvang, en ons sal stdout vir uitvoer gebruik.

Hier is so 'n eenvoudige konfigurasie vir die eerste lopie. Omdat die aanvanklike taak is om te begin.

So ons het hierdie 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

Wat sien ons hier?

  1. Netwerke en volumes is geneem uit die oorspronklike docker-compose.yml (die een waar die hele stapel geloods word) en ek dink dat hulle nie die algehele prentjie hier baie beïnvloed nie.
  2. Ons skep een diens (dienste) logstash, vanaf die docker.elastic.co/logstash/logstash:6.3.2 beeld en gee dit die naam logstash_one_channel.
  3. Ons stuur poort 5046 binne die houer aan na dieselfde interne poort.
  4. Ons karteer ons ./config/pipelines.yml-pypkonfigurasielêer na die /usr/share/logstash/config/pipelines.yml-lêer binne die houer, waar logstash dit sal optel en dit leesalleen maak, net vir ingeval.
  5. Ons karteer die ./config/pipelines gids, waar ons die pyp konfigurasie lêers het, na die /usr/share/logstash/config/pipelines gids en maak dit ook leesalleen.

Praktiese toepassing van ELK. Stel logstash op

piping.yml lêer

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

Dit beskryf een kanaal met die HABR-identifiseerder en die pad na sy konfigurasielêer.

En laastens die lêer "./config/pipelines/habr_pipeline.conf"

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

Ons gaan nie eers in op die beskrywing daarvan nie, ons probeer hardloop:

docker-compose up

Wat sien ons?

Die houer het begin. Ons kan sy werk nagaan:

echo '13123123123123123123123213123213' | nc localhost 5046

En ons sien die reaksie in die houerkonsole:

Praktiese toepassing van ELK. Stel logstash op

Maar terselfdertyd sien ons ook:

logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Kan nie lisensie-inligting van lisensiebediener af haal nie {:message=>"Elasticsearch onbereikbaar: [http://elasticsearch:9200/][Manticore] ::ResolutionFailure]elasticsearch", ...

logstash_one_channel | [2019-04-29T11:28:59,894][INFO ][logstash.pipeline ] Pyplyn het suksesvol begin {:pipeline_id=>.monitoring-logstash", :thread=>"# »}

logstash_one_channel | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Pyplyne loop {:count=>2, :running_pipelines=>[:HABR, :.monitoring-logstash"], :non_running_pipelines=>[ ]}
logstash_one_channel | [2019-04-29T11:29:00,015][ERROR][logstash.inputs.metrics ] X-Pack is op Logstash geïnstalleer, maar nie op Elasticsearch nie. Installeer asseblief X-Pack op Elasticsearch om die moniteringsfunksie te gebruik. Ander kenmerke kan beskikbaar wees.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Logstash API-eindpunt {:port=>9600} is suksesvol begin
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Voer tans gesondheidsondersoek uit om te sien of 'n Elasticsearch-verbinding werk {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Het probeer om verbinding met dooie ES-instansie weer te laat herleef, maar het 'n fout gekry. {:url=""elasticsearch:9200/", :error_type=>LogStash::Uitsette::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch onbereikbaar: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elastiese soek"}
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Voer tans gesondheidsondersoek uit om te sien of 'n Elasticsearch-verbinding werk {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WAARSKUWING ][logstash.licensechecker.licensereader] Het probeer om verbinding met dooie ES-instansie weer te laat herleef, maar het 'n fout gekry. {:url=""elasticsearch:9200/", :error_type=>LogStash::Uitsette::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch onbereikbaar: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elastiese soek"}

En ons log kruip heeltyd op.

Hier het ek die boodskap dat die pyplyn suksesvol begin het in groen uitgelig, in rooi die foutboodskap en in geel die boodskap oor probeer kontak maak elasticsearch: 9200.
Dit gebeur as gevolg van die feit dat daar in die logstash.conf wat in die beeld ingesluit is, 'n kontrole is vir die beskikbaarheid van elasticsearch. Logstash neem immers aan dat dit as deel van die Elk-stapel werk, en ons het dit geskei.

Jy kan werk, maar dit is nie gerieflik nie.

Die oplossing is om hierdie kontrole te deaktiveer via die XPACK_MONITORING_ENABLED omgewingsveranderlike.

Kom ons maak 'n verandering aan docker-compose.yml en voer dit weer uit:

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

Nou, alles is reg. Die houer is gereed vir eksperimente.

Ons kan weer in die aangrensende konsole tik:

echo '13123123123123123123123213123213' | nc localhost 5046

En kyk:

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

Werk binne een kanaal

So het ons begin. Nou kan jy eintlik die tyd neem om logstash direk op te stel. Kom ons raak vir eers nie aan die pipelines.yml-lêer nie, kom ons kyk wat ons kan kry deur met een kanaal te werk.

Ek moet sê dat die algemene beginsel van werk met die kanaalkonfigurasielêer goed beskryf word in die amptelike handleiding, hier hier
As jy in Russies wil lees, dan het ons hierdie een gebruik artikel(maar die navraagsintaksis is oud daar, jy moet dit in ag neem).

Kom ons gaan opeenvolgend vanaf die Invoer-afdeling. Ons het reeds die werk op tcp gesien. Wat anders kan interessant wees hier?

Toets boodskappe deur hartklop te gebruik

Daar is so 'n interessante moontlikheid om outomatiese toetsboodskappe te genereer.
Om dit te doen, moet jy die hartboon-inprop in die invoerafdeling insluit.

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

Ons skakel dit aan, ons begin een keer per minuut ontvang

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

Ons wil meer gereeld ontvang, ons moet die intervalparameter byvoeg.
Dit is hoe ons elke 10 sekondes 'n boodskap sal ontvang.

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

Kry data uit 'n lêer

Ons het ook besluit om na die lêermodus te kyk. As dit goed werk met die lêer, dan is dit moontlik dat geen agent nodig is nie, wel, ten minste vir plaaslike gebruik.

Volgens die beskrywing moet die werkswyse soortgelyk wees aan stert -f, d.w.s. lees nuwe reëls of, opsioneel, lees die hele lêer.

So wat ons wil kry:

  1. Ons wil reëls ontvang wat by een loglêer aangeheg is.
  2. Ons wil data ontvang wat na verskeie loglêers geskryf is, terwyl ons dit wat ontvang is van waar kan skei.
  3. Ons wil seker maak dat wanneer logstash herbegin word, dit nie weer hierdie data sal ontvang nie.
  4. Ons wil seker maak dat as logstash gedeaktiveer is en data steeds na lêers geskryf word, ons hierdie data sal ontvang wanneer ons dit laat loop.

Om die eksperiment uit te voer, kom ons voeg nog een reël by docker-compose.yml, wat die gids oopmaak waar ons die lêers plaas.

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

En verander die invoerafdeling in habr_pipeline.conf

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

Ons begin:

docker-compose up

Om loglêers te skep en te skryf, sal ons die opdrag gebruik:


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

Jip, dit werk!

Terselfdertyd sien ons dat ons outomaties die padveld bygevoeg het. So in die toekoms sal ons rekords daardeur kan filter.

Kom ons probeer weer:

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

En nou na 'n ander lêer:

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

Puik! Die lêer is opgetel, die pad is korrek gespesifiseer, alles is in orde.

Stop logstash en herbegin. Laat ons wag. Stilte. Dié. Ons ontvang nie weer hierdie rekords nie.

En nou die mees gewaagde eksperiment.

Ons sit logstash en voer uit:

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

Begin logstash weer en sien:

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

Hoera! Alles opgetel.

Maar dit is nodig om te waarsku oor die volgende. As die logstash-houer verwyder word (docker stop logstash_one_channel && docker rm logstash_one_channel), sal niks opgetel word nie. Die posisie van die lêer waartoe dit gelees is, is binne die houer gestoor. As jy van voor af begin, sal dit net nuwe lyne aanvaar.

Lees bestaande lêers

Kom ons sê ons hardloop logstash vir die eerste keer, maar ons het reeds logs en ons wil dit graag verwerk.
As ons logstash hardloop met die invoerafdeling wat ons hierbo gebruik het, sal ons niks kry nie. Slegs nuwe lyne sal deur logstash verwerk word.

Om lyne uit bestaande lêers te trek, voeg 'n bykomende reël by die invoerafdeling:

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

Boonop is daar 'n nuanse, dit raak slegs nuwe lêers wat logstash nog nie gesien het nie. Vir dieselfde lêers wat reeds in die sigveld van logstash was, het dit reeds hul grootte onthou en sal nou net nuwe rekords daarin neem.

Kom ons stop hierby deur die insetafdeling te bestudeer. Daar is baie meer opsies, maar vir nou het ons genoeg vir verdere eksperimente.

Roetering en datatransformasie

Kom ons probeer om die volgende probleem op te los, kom ons sê ons het boodskappe van een kanaal, sommige van hulle is inligting, en sommige is foutboodskappe. Hulle verskil in etiket. Sommige is INFO, ander is FOUT.

Ons moet hulle by die uitgang skei. Dié. Ons skryf inligtingsboodskappe in een kanaal, en foutboodskappe in 'n ander.

Om dit te doen, gaan van die invoerafdeling na filter en uitvoer.

Deur die filterafdeling te gebruik, sal ons die inkomende boodskap ontleed en 'n hash (sleutel-waarde-pare) daaruit kry, waarmee ons reeds kan werk, d.w.s. ontleed volgens die voorwaardes. En in die uitsetafdeling sal ons boodskappe kies en elkeen na sy eie kanaal stuur.

Ontleed 'n boodskap met grok

Om teksstringe te ontleed en 'n stel velde daaruit te kry, is daar 'n spesiale inprop in die filterafdeling - grok.

Sonder om myself die doel te stel om hier 'n gedetailleerde beskrywing daarvan te gee (hiervoor verwys ek na amptelike dokumentasie), Ek sal my eenvoudige voorbeeld gee.

Om dit te doen, moet jy besluit oor die formaat van die invoerlyne. Ek het hulle so:

1 INLIGTING boodskap1
2 FOUT boodskap2

Dié. Identifiseerder eers, dan INFO/FOUT, dan 'n woord sonder spasies.
Nie moeilik nie, maar genoeg om die beginsel van werk te verstaan.

Dus, in die filterafdeling, in die grok-inprop, moet ons 'n patroon definieer om ons snare te ontleed.

Dit sal so lyk:

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

Basies is dit 'n gereelde uitdrukking. Klaargemaakte patrone word gebruik, soos INT, LOGLEVEL, WORD. Hul beskrywing, sowel as ander patrone, kan hier gesien word. hier

As ons nou deur hierdie filter gaan, sal ons string verander in 'n hash van drie velde: message_id, message_type, message_text.

Hulle sal in die uitsetafdeling vertoon word.

Stuur boodskappe in die uitvoerafdeling met die if-opdrag

In die uitsetafdeling, soos ons onthou, gaan ons die boodskappe in twee strome verdeel. Sommige - wat iNFO is, sal ons na die konsole uitvoer, en met foute sal ons na 'n lêer uitvoer.

Hoe kan ons hierdie boodskappe deel? Die toestand van die probleem dui reeds op 'n oplossing - ons het immers reeds 'n toegewyde message_type-veld, wat slegs twee waardes INFO en ERROR kan neem. Dit is daarop dat ons 'n keuse sal maak deur die if-stelling te gebruik.

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

Beskrywing van werk met velde en operateurs kan in hierdie afdeling gevind word amptelike handleiding.

Nou oor die gevolgtrekking self.

Konsole-uitvoer, alles is duidelik hier - stdout {}

Maar die uitvoer na die lêer - onthou dat ons dit alles vanaf die houer laat loop en sodat die lêer waarin ons die resultaat skryf van buite toeganklik is, moet ons hierdie gids oopmaak in docker-compose.yml.

Totaal:

Die uitvoerafdeling van ons lêer lyk soos volg:


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

Voeg nog een volume by docker-compose.yml vir uitvoer:

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

Ons begin, ons probeer, ons sien die verdeling in twee strome.

Bron: will.com

Voeg 'n opmerking