Applicazione pratica di ELK. Configurazione di logstash

Introduzione

Mentre implementava un altru sistema, avemu affruntatu a necessità di processà un gran numaru di diversi logs. ELK hè statu sceltu cum'è strumentu. Questu articulu parlerà di a nostra sperienza in sta stack.

Ùn avemu micca stabilitu un scopu per descriverà tutte e so capacità, ma vulemu cuncentrazione nantu à risolve i prublemi pratichi. Questu hè duvuta à u fattu chì cù una quantità abbastanza grande di ducumentazione è di l'imaghjini pronti, ci sò assai trappule, almenu avemu trovu.

Avemu implementatu a pila attraversu docker-compose. Inoltre, avemu avutu un docker-compose.yml ben scrittu chì ci hà permessu di elevà a pila senza quasi prublemi. È ci paria chì a vittoria era dighjà vicinu, avà l'avemu a torce un pocu per risponde à i nostri bisogni è basta.

Sfurtunatamente, un tentativu di sintonizà u sistema per riceve è processà i logs da a nostra applicazione ùn hè micca successu subitu. Dunque, avemu decisu chì vale a pena studià ogni cumpunente per separatamente, è poi vultà à e so cunnessione.

Allora cuminciamu cù logstash.

Ambiente, implementazione, esecuzione di Logstash in un containeru

Per implementazione, usemu docker-compose, l'esperimenti descritti quì sò stati realizati in MacOS è Ubuntu 18.0.4.

L'imaghjini di logstash chì avemu avutu in u nostru docker-compose.yml originale hè docker.elastic.co/logstash/logstash:6.3.2

Avemu aduprà per esperimenti.

Per eseguisce logstash, avemu scrittu un docker-compose.yml separatu. Di sicuru, era pussibule lancià l'imaghjini da a linea di cummanda, ma dopu tuttu, avemu risoltu un compitu specificu, induve tuttu da docker-compose hè lanciatu per noi.

In breve nantu à i schedarii di cunfigurazione

Cumu seguita da a descrizzione, logstash pò esse eseguitu cum'è per un canale, in questu casu, hà bisognu di trasfirià u schedariu *.conf o per parechji canali, in quale casu hà bisognu di trasfiriri u schedariu pipelines.yml, chì, à u turnu , si riferirà à i schedari .conf per ogni canali.
Avemu pigliatu a seconda strada. Ci pareva più versatile è scalabile. Per quessa, avemu creatu pipelines.yml, è hà fattu un repertoriu di pipelines in quale metteremu i schedari .conf per ogni canali.

Dentru u cuntinuu ci hè un altru schedariu di cunfigurazione - logstash.yml. Ùn avemu micca toccu, l'utilicemu cum'è.

Allora a nostra struttura di cartulare hè:

Applicazione pratica di ELK. Configurazione di logstash

Per u mumentu, assumemu chì questu hè u tcp nantu à u portu 5046 per riceve dati di input, è useremu stdout per output.

Eccu una cunfigurazione cusì simplice per a prima corsa. Perchè u compitu iniziale hè di lancià.

Allora avemu questu 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

Chì vedemu quì ?

  1. Reti è volumi sò stati pigliati da l'uriginale docker-compose.yml (quellu induve l'intera pila hè lanciata) è pensu chì ùn anu micca assai affettanu a stampa generale quì.
  2. Creemu un serviziu (servizi) logstash, da l'imaghjini docker.elastic.co/logstash/logstash:6.3.2 è dà u nome logstash_one_channel.
  3. Inviamu u portu 5046 in u cuntinuu, à u stessu portu internu.
  4. Mapemu u nostru ./config/pipelines.yml file di cunfigurazione di pipe à u schedariu /usr/share/logstash/config/pipelines.yml in u cuntinuu, induve logstash u raccoglierà è u rende solu lettura, per ogni casu.
  5. Mapemu u repertoriu ./config/pipelines, induve avemu i schedarii di cunfigurazione di pipa, à u repertoriu /usr/share/logstash/config/pipelines è ancu fà a sola lettura.

Applicazione pratica di ELK. Configurazione di logstash

piping.yml file

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

Descrive un canale cù l'identificatore HABR è a strada di u so schedariu di cunfigurazione.

È infine u schedariu "./config/pipelines/habr_pipeline.conf"

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

Ùn andemu micca in a so descrizzione per ora, pruvemu di curriri:

docker-compose up

Chì vedemu ?

U cuntinuu hà cuminciatu. Pudemu verificà u so travagliu:

echo '13123123123123123123123213123213' | nc localhost 5046

È vedemu a risposta in a cunsola di u containeru:

Applicazione pratica di ELK. Configurazione di logstash

Ma à u stessu tempu, vedemu ancu:

logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Impossible di ricuperà l'infurmazioni di licenza da u servitore di licenza {:message=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore ::ResolutionFailure]elasticsearch",...

logstash_one_channel | [2019-04-29T11:28:59,894][INFO ][logstash.pipeline ] Pipeline hà iniziatu cù successu {:pipeline_id=>".monitoring-logstash", :thread => "# »}

logstash_one_channel | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Pipelines in esecuzione {: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 hè stallatu in Logstash ma micca in Elasticsearch. Per piacè installate X-Pack in Elasticsearch per utilizà a funzione di monitoraghju. Altre caratteristiche ponu esse dispunibili.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Avviatu cù successu l'endpoint API Logstash {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Esecuzione di cuntrollu di salute per vede se una cunnessione Elasticsearch funziona {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Tentatu di risuscitarà a cunnessione à l'istanza ES morta, ma hà avutu un errore. {: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] Esecuzione di cuntrollu di salute per vede se una cunnessione Elasticsearch funziona {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Tentatu di risuscitarà a cunnessione à l'istanza ES morta, ma hà avutu un errore. {:url =>"elasticsearch:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch"}

È u nostru log si arrampica sempre.

Quì aghju evidenziatu in verde u missaghju chì u pipeline hà iniziatu cù successu, in rossu u missaghju d'errore è in giallu u missaghju per pruvà à cuntattà. elasticsearch: 9200.
Questu succede per u fattu chì in u logstash.conf inclusu in l'imaghjini, ci hè un verificatu per a dispunibilità di elasticsearch. Dopu tuttu, logstash assume chì u travagliu cum'è parte di u stack Elk, è avemu separatu.

Pudete travaglià, ma ùn hè micca cunvene.

A suluzione hè di disattivà sta verificazione via a variabile d'ambiente XPACK_MONITORING_ENABLED.

Facemu un cambiamentu à docker-compose.yml è eseguite di novu:

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

Avà, tuttu hè bè. U cuntinuu hè prontu per l'esperimenti.

Pudemu scrive novu in a cunsola adiacente:

echo '13123123123123123123123213123213' | nc localhost 5046

È vede:

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

U travagliu in un canale

Allora, avemu principiatu. Avà pudete piglià u tempu per cunfigurà logstash direttamente. Ùn tocchemu micca u schedariu pipelines.yml per avà, vedemu ciò chì pudemu avè travagliatu cù un canale.

Devu dì chì u principiu generale di travaglià cù u schedariu di cunfigurazione di u canali hè ben descrittu in u manuale ufficiale, quì ccà
Se vulete leghje in Russu, allora avemu usatu questu articulu(ma a sintassi di a dumanda hè vechja quì, avete bisognu à piglià questu in contu).

Andemu in sequenza da a sezione Input. Avemu digià vistu u travagliu nantu à tcp. Chì altru pò esse interessante quì?

Testa i missaghji cù u battitu di u core

Ci hè una pussibilità cusì interessante di generà missaghji di prova automatica.
Per fà questu, avete bisognu di include u plugin heartbean in a seccione di input.

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

L'accendemu, cuminciamu à riceve una volta per minutu

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

Vulemu riceve più spessu, avemu bisognu di aghjunghje u paràmetru di l'intervallu.
Questu hè cumu ricevemu un missaghju ogni 10 seconde.

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

Ottene dati da un schedariu

Avemu ancu decisu di vede u modu di schedariu. Se funziona bè cù u schedariu, allora hè pussibule chì nisun agentu hè necessariu, bè, almenu per l'usu lucale.

Sicondu a descrizzione, u modu di funziunamentu deve esse simili à a coda -f, i.e. leghje i newlines o, opzionalmente, leghje u schedariu sanu.

Allora ciò chì vulemu ottene:

  1. Vulemu riceve linee chì sò appiccicate à un schedariu di log.
  2. Vulemu riceve dati chì hè scrittu à parechji schedarii di log, mentre chì pudendu separà ciò chì hè statu ricevutu da induve.
  3. Vulemu assicurà chì quandu u logstash hè riavviatu, ùn riceverà micca più sti dati.
  4. Vulemu verificà chì se logstash hè disattivatu, è i dati cuntinueghjanu à esse scritti à i schedari, allora quandu u corremu, ricevemu sta dati.

Per fà l'esperimentu, aghjustemu una linea più à docker-compose.yml, aprendu u repertoriu induve mettemu i schedari.

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

È cambia a sezione di input in habr_pipeline.conf

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

Cuminciamu:

docker-compose up

Per creà è scrive i schedarii di log, useremu u cumandimu:


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

Iè, funziona!

À u listessu tempu, vedemu chì avemu aghjustatu automaticamente u campu di a strada. Allora in u futuru, puderemu filtrà i registri per ellu.

Pruvemu di novu:

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

È avà à un altru schedariu:

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

Perfettu! U schedariu hè statu pigliatu, u percorsu hè statu specificatu currettamente, tuttu hè bè.

Stop logstash è riavvia. Aspittemu. Silenziu. Quelli. Ùn ricevemu micca più sti records.

È avà l'esperimentu più audace.

Pudemu logstash è eseguite:

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

Eseguite logstash di novu è vede:

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

Eura! Tuttu hà pigliatu.

Ma, hè necessariu avvistà nantu à i seguenti. Se u cuntinuu di u logstash hè sguassatu (docker stop logstash_one_channel && docker rm logstash_one_channel), nunda ùn serà pigliatu. A pusizione di u schedariu finu à u quale hè statu lettu hè stata guardata in u cuntinuu. Se parte da zero, allora accetterà solu linee novi.

Leghje i schedari esistenti

Diciamu chì eseguimu logstash per a prima volta, ma avemu digià logs è vulemu processarli.
Se eseguimu logstash cù a sezione di input chì avemu usatu sopra, ùn averemu micca nunda. Solu i novi linee seranu processati da logstash.

Per piglià e linee da i fugliali esistenti, aghjunghje una linea addiziale à a sezione di input:

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

Inoltre, ci hè una sfumatura, questu solu affetta i schedari novi chì logstash ùn hà micca vistu. Per i stessi schedari chì eranu digià in u campu di vista di logstash, hà digià ricurdatu di a so dimensione è avà pigliarà solu novi registri in elli.

Fighjemu nantu à questu studiendu a sezione di input. Ci hè parechje più opzioni, ma per avà, avemu abbastanza per più esperimenti.

Routing è trasfurmazioni di dati

Pruvemu di risolve u prublema chì seguita, dicemu chì avemu messagi da un canale, alcuni di elli sò informativi, è certi sò missaghji d'errore. Differiscenu in tag. Certi sò INFO, altri sò ERRORE.

Avemu bisognu di separà à a surtita. Quelli. Scrivemu missaghji informativi in ​​un canale, è missaghji d'errore in un altru.

Per fà questu, andate da a rùbbrica di input à filtru è output.

Utilizendu a seccione di filtru, avemu da analizà u missaghju entrante, ottenendu un hash (coppiu chjave-valore) da ellu, cù quale pudemu digià travaglià, i.e. analizà secondu e cundizioni. È in a rùbbrica di pruduzzioni, selezziunà i missaghji è mandà ognunu à u so canali.

Analizà un missaghju cù grok

Per analizà e stringhe di testu è uttene un settore di campi da elli, ci hè un plugin speciale in a sezione di filtru - grok.

Senza mettemi u scopu di dà una descrizzione dettagliata quì (per questu mi riferite à documentazione ufficiale), daraghju u mo esempiu simplice.

Per fà questu, avete bisognu di decide nantu à u formatu di e linee di input. Li aghju cusì:

1 messagiu INFO 1
2 messagiu ERRORE 2

Quelli. Identificatore prima, dopu INFO/ERRORE, dopu qualchì parolla senza spazii.
Ùn hè micca difficiule, ma abbastanza per capisce u principiu di u funziunamentu.

Allora, in a seccione di filtru, in u plugin grok, avemu bisognu di definisce un mudellu per analizà e nostre corde.

Serà cusì:

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

In fondu, hè una espressione regulare. I mudelli pronti sò usati, cum'è INT, LOGLEVEL, WORD. A so descrizzione, è ancu altri mudelli, ponu esse vistu quì. ccà

Avà, passendu per questu filtru, a nostra stringa diventerà un hash di trè campi: message_id, message_type, message_text.

Seranu visualizati in a sezione di output.

Routing missaghji in a rùbbrica di pruduzzioni cù u cumanda se

In a rùbbrica di pruduzzione, cum'è ricurdate, avemu da sparte i missaghji in dui flussi. Qualchidunu - chì sò iNFO, salderemu à a cunsola, è cù l'errori, avemu da prucede à un schedariu.

Cumu pudemu sparte sti missaghji? A cundizione di u prublema suggerisce digià una suluzione - dopu tuttu, avemu digià un campu dedicatu message_type, chì pò piglià solu dui valori INFO è ERROR. Hè nantu à questu chì faremu una scelta utilizendu a dichjarazione if.

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

A descrizzione di u travagliu cù i campi è l'operatori ponu esse truvati in questa sezione manuale ufficiale.

Avà, nantu à a cunclusione stessu.

Output di cunsola, tuttu hè chjaru quì - stdout {}

Ma l'output à u schedariu - ricordate chì eseguimu tuttu questu da u cuntinuu è per chì u schedariu in quale scrivemu u risultatu sia accessibile da l'esternu, avemu bisognu di apre stu cartulare in docker-compose.yml.

Total:

A sezione di output di u nostru schedariu s'assumiglia cusì:


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

Aghjunghjite un volumu più à docker-compose.yml per l'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

Cuminciamu, pruvemu, vedemu a divisione in dui flussi.

Source: www.habr.com

Add a comment