Praktisk bruk av ELK. Sette opp logstash

Innledning

Mens vi implementerte et annet system, ble vi møtt med behovet for å behandle et stort antall forskjellige logger. ELK ble valgt som verktøy. Denne artikkelen vil diskutere vår erfaring med å sette opp denne stabelen.

Vi setter oss ikke som mål å beskrive alle dens evner, men vi ønsker å konsentrere oss spesifikt om å løse praktiske problemer. Dette kommer av at selv om det er ganske mye dokumentasjon og ferdige bilder, er det ganske mange fallgruver, vi fant dem i hvert fall.

Vi distribuerte stabelen via docker-compose. Dessuten hadde vi en velskrevet docker-compose.yml, som tillot oss å heve stabelen nesten uten problemer. Og det virket for oss som om seieren allerede var nær, nå skal vi justere den litt for å passe våre behov, og det er det.

Dessverre ble ikke forsøket på å konfigurere systemet til å motta og behandle logger fra applikasjonen vår umiddelbart vellykket. Derfor bestemte vi oss for at det var verdt å studere hver komponent separat, og deretter gå tilbake til forbindelsene deres.

Så vi startet med logstash.

Miljø, distribusjon, kjøring av Logstash i en container

For distribusjon bruker vi docker-compose; eksperimentene beskrevet her ble utført på MacOS og Ubuntu 18.0.4.

Logstash-bildet som ble registrert i vår originale docker-compose.yml er docker.elastic.co/logstash/logstash:6.3.2

Vi vil bruke den til eksperimenter.

Vi skrev en egen docker-compose.yml for å kjøre logstash. Selvfølgelig var det mulig å starte bildet fra kommandolinjen, men vi løste et spesifikt problem, der vi kjører alt fra docker-compose.

Kort om konfigurasjonsfiler

Som det følger av beskrivelsen, kan logstash kjøres enten for én kanal, i så fall må den passere *.conf-filen, eller for flere kanaler, i så fall må den passere pipelines.yml-filen, som igjen , vil koble til filene .conf for hver kanal.
Vi tok den andre veien. Det virket for oss mer universelt og skalerbart. Derfor opprettet vi pipelines.yml, og laget en pipelines-katalog der vi legger .conf-filer for hver kanal.

Inne i beholderen er det en annen konfigurasjonsfil - logstash.yml. Vi rører den ikke, vi bruker den som den er.

Så vår katalogstruktur:

Praktisk bruk av ELK. Sette opp logstash

For å motta inngangsdata antar vi foreløpig at dette er tcp på port 5046, og for utdata vil vi bruke stdout.

Her er en enkel konfigurasjon for den første lanseringen. Fordi den første oppgaven er å lansere.

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

Hva ser vi her?

  1. Nettverk og volumer ble hentet fra den originale docker-compose.yml (den der hele stabelen er lansert) og jeg tror at de ikke påvirker helhetsbildet i stor grad her.
  2. Vi oppretter én(e) logstash-tjeneste(r) fra docker.elastic.co/logstash/logstash:6.3.2-bildet og gir det navnet logstash_one_channel.
  3. Vi videresender port 5046 inne i containeren, til samme interne port.
  4. Vi kartlegger pipekonfigurasjonsfilen vår ./config/pipelines.yml til filen /usr/share/logstash/config/pipelines.yml inne i beholderen, hvor logstash vil plukke den opp og gjøre den skrivebeskyttet, for sikkerhets skyld.
  5. Vi kartlegger ./config/pipelines-katalogen, der vi har filer med kanalinnstillinger, til /usr/share/logstash/config/pipelines-katalogen og gjør den også skrivebeskyttet.

Praktisk bruk av ELK. Sette opp logstash

Pipelines.yml-filen

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

Én kanal med HABR-identifikatoren og banen til konfigurasjonsfilen er beskrevet her.

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

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

La oss ikke gå inn på beskrivelsen for nå, la oss prøve å kjøre den:

docker-compose up

Hva ser vi?

Containeren har startet. Vi kan sjekke funksjonen:

echo '13123123123123123123123213123213' | nc localhost 5046

Og vi ser svaret i containerkonsollen:

Praktisk bruk av ELK. Sette opp logstash

Men samtidig ser vi også:

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

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

logstash_one_channel | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Rørledninger som kjø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 installert på Logstash, men ikke på Elasticsearch. Installer X-Pack på Elasticsearch for å bruke overvåkingsfunksjonen. Andre funksjoner kan være tilgjengelige.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Startet Logstash API-endepunkt {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Kjører helsesjekk for å se om en Elasticsearch-tilkobling fungerer {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Forsøkte å gjenopprette forbindelsen til død ES-forekomst, men fikk en feil. {:url=>“elasticsearch:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch"}
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Kjører helsesjekk for å se om en Elasticsearch-tilkobling fungerer {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Forsøkte å gjenopprette forbindelsen til død ES-forekomst, men fikk en feil. {:url=>“elasticsearch:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch"}

Og loggen vår kryper opp hele tiden.

Her har jeg markert med grønt meldingen om at rørledningen har startet vellykket, i rødt feilmeldingen og i gult meldingen om forsøk på å kontakte elasticsearch: 9200.
Dette skjer fordi logstash.conf, inkludert i bildet, inneholder en sjekk for tilgjengelighet for elasticsearch. Tross alt antar logstash at den fungerer som en del av elgstabelen, men vi skilte den.

Det er mulig å jobbe, men det er ikke praktisk.

Løsningen er å deaktivere denne kontrollen via miljøvariabelen XPACK_MONITORING_ENABLED.

La oss gjøre en endring til docker-compose.yml og kjøre den på nytt:

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

Nå er alt bra. Beholderen er klar for eksperimenter.

Vi kan skrive på nytt i neste konsoll:

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

Arbeider innenfor én kanal

Så vi lanserte. Nå kan du faktisk ta deg tid til å konfigurere selve logstash. La oss ikke røre pipelines.yml-filen foreløpig, la oss se hva vi kan få ved å jobbe med én kanal.

Jeg må si at det generelle prinsippet for å jobbe med kanalkonfigurasjonsfilen er godt beskrevet i den offisielle håndboken, her her
Hvis du vil lese på russisk, brukte vi denne artikkel(men spørringssyntaksen der er gammel, vi må ta hensyn til dette).

La oss gå sekvensielt fra Input-delen. Vi har allerede sett arbeid med TCP. Hva annet kan være interessant her?

Test meldinger ved hjelp av hjerteslag

Det er en interessant mulighet til å generere automatiske testmeldinger.
For å gjøre dette må du aktivere heartbean-pluginen i inngangsdelen.

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

Slå den på, begynn å motta 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 | }

Hvis vi ønsker å motta oftere, må vi legge til intervallparameteren.
Slik vil vi motta en melding hvert 10. sekund.

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

Henter data fra en fil

Vi bestemte oss også for å se på filmodusen. Hvis det fungerer bra med filen, er det kanskje ikke nødvendig med noen agent, i hvert fall for lokal bruk.

I følge beskrivelsen skal driftsmodusen være lik tail -f, dvs. leser nye linjer eller, som et alternativ, leser hele filen.

Så hva vi ønsker å få:

  1. Vi ønsker å motta linjer som legges til én loggfil.
  2. Vi ønsker å motta data som skrives til flere loggfiler, samtidig som vi kan skille det som mottas fra hvor.
  3. Vi vil forsikre oss om at når logstash startes på nytt, mottar den ikke disse dataene igjen.
  4. Vi ønsker å sjekke at hvis logstash er slått av, og data fortsetter å bli skrevet til filer, vil vi motta disse dataene når vi kjører det.

For å utføre eksperimentet, la oss legge til en annen linje i docker-compose.yml, og åpne katalogen der vi legger filene.

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 endre inndatadelen i habr_pipeline.conf

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

La oss begynne:

docker-compose up

For å lage og skrive loggfiler bruker 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 | }

Jepp, det fungerer!

Samtidig ser vi at vi automatisk har lagt til banefeltet. Dette betyr at vi i fremtiden vil kunne filtrere poster etter det.

La oss prøve igjen:

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 nå til en annen 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 | }

Flott! Filen ble plukket opp, banen ble spesifisert riktig, alt er i orden.

Stopp logstash og start på nytt. La oss vente. Stillhet. De. Vi mottar ikke disse postene igjen.

Og nå det mest dristige eksperimentet.

Installer logstash og kjør:

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

Kjør logstash igjen 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 ble plukket opp.

Men vi må advare deg om følgende. Hvis logstash-beholderen slettes (docker stop logstash_one_channel && docker rm logstash_one_channel), vil ingenting bli plukket opp. Posisjonen til filen som den ble lest til, ble lagret inne i beholderen. Hvis du kjører det fra bunnen av, vil det bare godta nye linjer.

Leser eksisterende filer

La oss si at vi lanserer logstash for første gang, men vi har allerede logger og vi vil gjerne behandle dem.
Hvis vi kjører logstash med inndatadelen vi brukte ovenfor, får vi ingenting. Kun nye linjer vil bli behandlet av logstash.

For at linjer fra eksisterende filer skal trekkes opp, bør du legge til en ekstra linje i inndatadelen:

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

Dessuten er det en nyanse: dette påvirker bare nye filer som logstash ennå ikke har sett. For de samme filene som allerede var i synsfeltet til logstash, har den allerede husket størrelsen og vil nå bare ta nye oppføringer i dem.

La oss stoppe her og studere inngangsdelen. Det er fortsatt mange alternativer, men det er nok for oss for videre eksperimenter for nå.

Ruting og datatransformasjon

La oss prøve å løse følgende problem, la oss si at vi har meldinger fra én kanal, noen av dem er informative, og noen er feilmeldinger. De er forskjellige etter tag. Noen er INFO, andre er FEIL.

Vi må skille dem ved utgangen. De. Vi skriver informasjonsmeldinger i én kanal, og feilmeldinger i en annen.

For å gjøre dette, gå fra inngangsdelen til filtrering og utgang.

Ved å bruke filterdelen vil vi analysere den innkommende meldingen, og hente en hash (nøkkelverdi-par) fra den, som vi allerede kan jobbe med, dvs. demonteres etter forholdene. Og i utdatadelen vil vi velge meldinger og sende hver til sin egen kanal.

Parser en melding med grok

For å analysere tekststrenger og få et sett med felt fra dem, er det en spesiell plugin i filterdelen - grok.

Uten å sette meg som mål å gi en detaljert beskrivelse av det her (for dette referer jeg til offisiell dokumentasjon), Jeg skal gi mitt enkle eksempel.

For å gjøre dette må du bestemme formatet på inndatastrengene. Jeg har dem slik:

1 INFO-melding1
2 FEILmelding2

De. Identifikatoren kommer først, deretter INFO/FEIL, deretter et ord uten mellomrom.
Det er ikke vanskelig, men det er nok til å forstå operasjonsprinsippet.

Så, i filterdelen av grok-pluginen, må vi definere et mønster for å analysere strengene våre.

Det vil se slik ut:

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

I hovedsak er det et regulært uttrykk. Det brukes ferdige mønstre, som INT, LOGLEVEL, WORD. Beskrivelsen deres, så vel som andre mønstre, finner du her her

Når vi nå passerer gjennom dette filteret, blir strengen vår til en hash av tre felt: meldings-id, meldingstype, meldingstekst.

De vil vises i utdatadelen.

Rute meldinger til utdatadelen ved å bruke if-kommandoen

I utdatadelen, som vi husker, skulle vi dele meldingene i to strømmer. Noen - som er iNFO, vil sendes ut til konsollen, og med feil vil vi sende ut til en fil.

Hvordan skiller vi disse meldingene? Tilstanden til problemet antyder allerede en løsning - tross alt har vi allerede et dedikert meldingstype-felt, som bare kan ha to verdier: INFO og FEIL. Det er på dette grunnlaget vi vil ta et valg ved å bruke if-setningen.

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

En beskrivelse av arbeid med felt og operatører finnes i denne delen offisiell manual.

Nå om selve konklusjonen.

Konsollutgang, alt er klart her - stdout {}

Men utdata til en fil - husk at vi kjører alt dette fra en container, og for at filen der vi skriver resultatet skal være tilgjengelig fra utsiden, må vi åpne denne katalogen i docker-compose.yml.

totalt:

Utdatadelen av filen vår ser slik ut:


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

I docker-compose.yml legger vi til et annet volum for utdata:

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 lanserer den, prøver den og ser en inndeling i to strømmer.

Kilde: www.habr.com

Legg til en kommentar