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è:
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ì ?
- 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ì.
- Creemu un serviziu (servizi) logstash, da l'imaghjini docker.elastic.co/logstash/logstash:6.3.2 è dà u nome logstash_one_channel.
- Inviamu u portu 5046 in u cuntinuu, à u stessu portu internu.
- 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.
- 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.
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:
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 =>"
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 =>"
È 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à.
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ì
Se vulete leghje in Russu, allora avemu usatu questu
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:
- Vulemu riceve linee chì sò appiccicate à un schedariu di log.
- Vulemu riceve dati chì hè scrittu à parechji schedarii di log, mentre chì pudendu separà ciò chì hè statu ricevutu da induve.
- Vulemu assicurà chì quandu u logstash hè riavviatu, ùn riceverà micca più sti dati.
- 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 à
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ì.
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
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