Praktisk anvendelse af ELK. Opsætning af logstash

Indledning

Mens vi implementerede et andet system, stod vi over for behovet for at behandle et stort antal forskellige logfiler. ELK blev valgt som instrument. Denne artikel vil fortælle om vores erfaring med at opsætte denne stak.

Vi sætter ikke et mål om at beskrive alle dets muligheder, men vi ønsker at koncentrere os om at løse praktiske problemer. Det skyldes, at der med en tilstrækkelig stor mængde dokumentation og færdige billeder er mange faldgruber, vi fandt dem i hvert fald.

Vi implementerede stakken gennem docker-compose. Desuden havde vi en velskrevet docker-compose.yml, der tillod os at hæve stakken næsten uden problemer. Og det forekom for os, at sejren allerede var tæt på, nu vil vi vride den lidt for at passe til vores behov, og det er det.

Desværre lykkedes et forsøg på at tune systemet til at modtage og behandle logfiler fra vores applikation ikke umiddelbart. Derfor besluttede vi, at det er værd at studere hver komponent separat og derefter vende tilbage til deres forbindelser.

Så lad os starte med logstash.

Miljø, implementering, kørsel af Logstash i en container

Til udrulning bruger vi docker-compose, eksperimenterne beskrevet her blev udført på MacOS og Ubuntu 18.0.4.

Logstash-billedet, som vi havde i vores originale docker-compose.yml, er docker.elastic.co/logstash/logstash:6.3.2

Vi vil bruge det til eksperimenter.

For at køre logstash skrev vi en separat docker-compose.yml. Det var selvfølgelig muligt at starte billedet fra kommandolinjen, men vi løste trods alt en specifik opgave, hvor alt fra docker-compose lanceres for os.

Kort om konfigurationsfiler

Som det følger af beskrivelsen, kan logstash køres som for én kanal, i dette tilfælde skal den overføre *.conf filen eller for flere kanaler, i hvilket tilfælde den skal overføre filen pipelines.yml, som igen , vil referere til filerne .conf for hver kanal.
Vi tog den anden vej. Det forekom os mere alsidigt og skalerbart. Derfor lavede vi pipelines.yml, og lavede en pipelines-mappe, hvori vi lægger .conf-filer til hver kanal.

Inde i containeren er der en anden konfigurationsfil - logstash.yml. Vi rører det ikke, vi bruger det som det er.

Så vores mappestruktur er:

Praktisk anvendelse af ELK. Opsætning af logstash

Foreløbig antager vi, at dette er tcp på port 5046 til at modtage inputdata, og vi vil bruge stdout til output.

Her er sådan en simpel konfiguration til den første kørsel. Fordi den første opgave er at lancere.

Så vi har denne 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

Hvad ser vi her?

  1. Netværk og volumener blev taget fra den originale docker-compose.yml (den, hvor hele stakken er lanceret), og jeg tror, ​​at de ikke påvirker det overordnede billede i høj grad her.
  2. Vi opretter én tjeneste (tjenester) logstash fra docker.elastic.co/logstash/logstash:6.3.2-billedet og giver det navnet logstash_one_channel.
  3. Vi videresender port 5046 inde i containeren til den samme interne port.
  4. Vi kortlægger vores ./config/pipelines.yml-pipe-konfigurationsfil til filen /usr/share/logstash/config/pipelines.yml inde i containeren, hvor logstash vil samle den op og gøre den skrivebeskyttet, for en sikkerheds skyld.
  5. Vi kortlægger ./config/pipelines-mappen, hvor vi har pipe-konfigurationsfilerne, til mappen /usr/share/logstash/config/pipelines og gør den også skrivebeskyttet.

Praktisk anvendelse af ELK. Opsætning af logstash

piping.yml fil

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

Den beskriver en kanal med HABR-identifikationen og stien til dens konfigurationsfil.

Og til sidst filen "./config/pipelines/habr_pipeline.conf"

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

Vi vil ikke gå ind på dens beskrivelse for nu, vi prøver at køre:

docker-compose up

Hvad ser vi?

Containeren er startet. Vi kan tjekke dets arbejde:

echo '13123123123123123123123213123213' | nc localhost 5046

Og vi ser svaret i containerkonsollen:

Praktisk anvendelse af ELK. Opsætning af logstash

Men samtidig ser vi også:

logstash_one_channel | [2019-04-29T11:28:59,790][FEJL][logstash.licensechecker.licensereader] Kan ikke hente licensoplysninger fra licensserveren {:message=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore ::ResolutionFailure]elasticsearch", ...

logstash_one_channel | [2019-04-29T11:28:59,894][INFO ][logstash.pipeline ] Pipeline startede med succes {:pipeline_id=>".monitoring-logstash", :thread=>"# »}

logstash_one_channel | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Pipelines, der kører {: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 er installeret på Logstash, men ikke på Elasticsearch. Installer venligst X-Pack på Elasticsearch for at bruge overvågningsfunktionen. Andre funktioner kan være tilgængelige.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Logstash API-slutpunkt {:port=>9600} er startet med succes
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Kører sundhedstjek for at se, om en Elasticsearch-forbindelse virker {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Forsøgte at genoprette forbindelsen til en død ES-instans, men fik en fejl. {:url=>"elastiksøgning:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore]:Resolution Failure elasticsearch"}
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Kører sundhedstjek for at se, om en Elasticsearch-forbindelse virker {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Forsøgte at genoprette forbindelsen til død ES-instans, men fik en fejl. {:url=>"elastiksøgning:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore]:Resolution Failure elasticsearch"}

Og vores log kravler op hele tiden.

Her fremhævede jeg med grøn beskeden om, at pipelinen startede med succes, med rødt fejlmeddelelsen og med gult beskeden om at forsøge at kontakte elastiksøgning: 9200.
Dette sker på grund af det faktum, at der i logstash.conf inkluderet i billedet er en kontrol for tilgængeligheden af ​​elasticsearch. Logstash antager trods alt, at den fungerer som en del af Elk-stakken, og vi adskilte den.

Du kan arbejde, men det er ikke praktisk.

Løsningen er at deaktivere denne kontrol via miljøvariablen XPACK_MONITORING_ENABLED.

Lad os lave en ændring til docker-compose.yml og køre den igen:

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

Nu er alt fint. Beholderen er klar til eksperimenter.

Vi kan skrive igen i den tilstødende konsol:

echo '13123123123123123123123213123213' | nc localhost 5046

Og se:

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

Arbejd inden for én kanal

Så vi startede. Nu kan du faktisk tage dig tid til at konfigurere logstash direkte. Lad os ikke røre pipelines.yml-filen lige nu, lad os se, hvad vi kan få ved at arbejde med én kanal.

Jeg må sige, at det generelle princip for at arbejde med kanalkonfigurationsfilen er godt beskrevet i den officielle manual her her
Hvis du vil læse på russisk, så brugte vi denne artikel(men forespørgselssyntaksen er gammel der, du skal tage højde for dette).

Lad os gå sekventielt fra Input-sektionen. Vi har allerede set arbejdet med tcp. Hvad kan ellers være interessant her?

Test beskeder ved hjælp af hjerteslag

Der er sådan en interessant mulighed for at generere automatiske testmeddelelser.
For at gøre dette skal du inkludere heartbean-plugin'et i inputsektionen.

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

Vi tænder for det, vi begynder at modtage en gang i minuttet

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

Vi ønsker at modtage oftere, vi skal tilføje intervalparameteren.
Sådan vil vi modtage en besked hvert 10. sekund.

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

Henter data fra en fil

Vi besluttede også at se på filtilstanden. Hvis det fungerer fint med filen, er det muligt, at der ikke kræves nogen agent, i hvert fald til lokal brug.

Driftsmåden skulle ifølge beskrivelsen svare til hale -f, dvs. læser nye linjer eller, valgfrit, læser hele filen.

Så hvad vi gerne vil have:

  1. Vi ønsker at modtage linjer, der er tilføjet til én logfil.
  2. Vi ønsker at modtage data, der er skrevet til flere logfiler, samtidig med at vi kan adskille, hvad der er modtaget fra hvor.
  3. Vi vil sikre os, at når logstash genstartes, vil den ikke modtage disse data igen.
  4. Vi vil kontrollere, at hvis logstash er deaktiveret, og data fortsætter med at blive skrevet til filer, så vil vi modtage disse data, når vi kører det.

For at udføre eksperimentet, lad os tilføje en linje mere til docker-compose.yml og åbne den mappe, hvor vi lægger filerne.

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

Og skift inputsektionen i habr_pipeline.conf

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

Vi starter:

docker-compose up

For at oprette og skrive logfiler bruger vi kommandoen:


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

Jep, det virker!

Samtidig ser vi, at vi automatisk har tilføjet stifeltet. Så i fremtiden vil vi være i stand til at filtrere poster efter det.

Lad os prøve igen:

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

Og nu til en anden fil:

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

Store! Filen blev samlet op, stien blev angivet korrekt, alt er i orden.

Stop logstash og genstart. Lad os vente. Stilhed. De der. Vi modtager ikke disse optegnelser igen.

Og nu det mest vovede eksperiment.

Vi sætter logstash og udfører:

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

Kør logstash igen og se:

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

Hurra! Alt blev samlet op.

Men det er nødvendigt at advare om følgende. Hvis logstash-beholderen fjernes (docker stop logstash_one_channel && docker rm logstash_one_channel), vil der ikke blive hentet noget. Placeringen af ​​filen, indtil den blev læst, blev gemt inde i beholderen. Hvis du starter fra bunden, vil den kun acceptere nye linjer.

Læser eksisterende filer

Lad os sige, at vi kører logstash for første gang, men vi har allerede logs, og vi vil gerne behandle dem.
Hvis vi kører logstash med den input sektion, vi brugte ovenfor, får vi ikke noget. Kun newlines vil blive behandlet af logstash.

For at trække linjer fra eksisterende filer skal du tilføje en ekstra linje til inputsektionen:

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

Desuden er der en nuance, dette påvirker kun nye filer, som logstash endnu ikke har set. For de samme filer, der allerede var i logstashs synsfelt, har den allerede husket deres størrelse og vil nu kun tage nye poster i dem.

Lad os stoppe med dette ved at studere inputsektionen. Der er mange flere muligheder, men indtil videre har vi nok til yderligere eksperimenter.

Routing og datatransformation

Lad os prøve at løse følgende problem, lad os sige, at vi har beskeder fra én kanal, nogle af dem er informative, og nogle er fejlmeddelelser. De adskiller sig i tag. Nogle er INFO, andre er FEJL.

Vi skal skille dem ad ved udgangen. De der. Vi skriver informationsmeddelelser i én kanal og fejlmeddelelser i en anden.

For at gøre dette skal du gå fra inputsektionen til filtrering og output.

Ved hjælp af filtersektionen vil vi parse den indkommende besked, og få en hash (nøgleværdi-par) fra den, som vi allerede kan arbejde med, dvs. analysere efter betingelserne. Og i outputsektionen vil vi vælge beskeder og sende hver enkelt til sin egen kanal.

Parser en besked med grok

For at parse tekststrenge og få et sæt felter fra dem, er der et særligt plugin i filtersektionen - grok.

Uden at sætte mig som mål at give en detaljeret beskrivelse af det her (hertil henviser jeg til officiel dokumentation), vil jeg give mit simple eksempel.

For at gøre dette skal du bestemme formatet på inputlinjerne. Jeg har dem sådan her:

1 INFO besked1
2 FEJL-meddelelse2

De der. Identifikator først, så INFO/FEJL, så et ord uden mellemrum.
Ikke svært, men nok til at forstå princippet om arbejde.

Så i filtersektionen i grok-plugin'et skal vi definere et mønster til at analysere vores strenge.

Det vil se sådan ud:

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

Dybest set er det et regulært udtryk. Der bruges færdige mønstre, såsom INT, LOGLEVEL, WORD. Deres beskrivelse, såvel som andre mønstre, kan ses her. her

Når vi passerer gennem dette filter, vil vores streng blive til en hash af tre felter: message_id, message_type, message_text.

De vil blive vist i outputsektionen.

Routing af meddelelser i outputsektionen med if-kommandoen

I outputsektionen skulle vi, som vi husker, opdele beskederne i to strømme. Nogle - som er iNFO, vil vi output til konsollen, og med fejl, vil vi output til en fil.

Hvordan kan vi dele disse budskaber? Problemets tilstand foreslår allerede en løsning - vi har trods alt allerede et dedikeret message_type felt, som kun kan tage to værdier INFO og ERROR. Det er på den, vi vil træffe et valg ved at bruge if-sætningen.

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

Beskrivelse af arbejdet med felter og operatører kan findes i dette afsnit officiel manual.

Nu om selve konklusionen.

Konsoludgang, alt er klart her - stdout {}

Men outputtet til filen - husk, at vi kører alt dette fra containeren, og for at filen, som vi skriver resultatet i, er tilgængelig udefra, skal vi åbne denne mappe i docker-compose.yml.

Totalt:

Outputsektionen af ​​vores fil ser sådan ud:


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

Tilføj endnu en volumen til docker-compose.yml til output:

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

Vi starter, vi prøver, vi ser opdelingen i to strømme.

Kilde: www.habr.com

Tilføj en kommentar