Sissejuhatus
Teise sĂŒsteemi kĂ€ivitamisel seisime silmitsi vajadusega töödelda suures koguses erinevaid logisid. Tööriistaks valisime ELK. Selles artiklis kĂ€sitleme oma kogemust selle stacki seadistamisel.
Me ei pĂŒĂŒa kirja panna kĂ”iki selle vĂ”imalusi, vaid soovime keskenduda praktiliste probleemide lahendamisele. Seda tingis see, et kuigi dokumentatsiooni ja valmis pilte on kĂŒllalt, esines palju takistusi, vĂ€hemalt meie kogemusel.
Me kĂ€ivitasime stacki lĂ€bi docker-compose'i. Veelgi enam, meil oli hĂ€sti kirjutatud docker-compose.yml, mis vĂ”imaldas meil praktiliselt probleemideta stacki ĂŒles tĂ”sta. Meil tundus, et vĂ”it on juba lĂ€hedal, nĂŒĂŒd veidi kohandame seda oma vajadustele ja kĂ”ik ongi.
Kahjuks ei kroonitud meie katse sĂŒsteemi seadistamisel logide vastuvĂ”tmiseks ja töötlemiseks meie rakenduselt kohe edu. SeetĂ”ttu otsustasime uurida iga komponenti eraldi, enne kui naaseme nende ĂŒhenduste juurde.
Nii et alustasime logstash'iga.
Keskkond, Logstashi kÀivitamine konteineris
KÀivitamiseks kasutame docker-compose'i, siin tehtud katsed viidi lÀbi MacOS-il ja Ubuntu 18.0.4-l.
Logstashi pilt, mis oli meie algses docker-compose.yml-is mÀÀratletud, on docker.elastic.co/logstash/logstash:6.3.2
Seda kasutame katseteks.
Logstashi kĂ€ivitamiseks kirjutasime eraldi docker-compose.yml faili. Loomulikult oleks vĂ”inud ka kĂ€surealt pilti kĂ€ivitada, kuid me lahendasime spetsiifilise ĂŒlesande, kus kĂ”ik kĂ€ivitatakse docker-compose'i kaudu.
LĂŒhidalt konfigureerimisfailidest
Kuna kirjeldus viitab sellele, et logstash'i saab kĂ€ivitada nii ĂŒhe kanalina, juhul kui sellele tuleb anda *.conf fail, kui ka mitme kanali jaoks, genereerides pipelines.yml faili, mis omakorda viitab .conf failidele iga kanali jaoks.
Valisime teise viisi. See tundus meile universaalsem ja skaleeritavam. Seega lÔime pipelines.yml ja tegsime kaustas pipelines, kuhu paigutame .conf failid iga kanali jaoks.
Konteineris on veel ĂŒks konfigureerimisfail - logstash.yml. Me ei puutu sellesse, kasutame seda sellisena, nagu see on.
Nii et meie kataloogide struktuur on:

KÀivitavate andmete jaoks arvame esialgu, et see on tcp sadamas 5046, ja vÀljundi jaoks kasutame stdout.
Siin on lihtne konfiguratsioon esmakordseks kĂ€ivitamiseks. LĂ”ppude lĂ”puks on esmane ĂŒlesanne kĂ€ivitada.
NĂŒĂŒd on meil selline 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
Mida me siin nÀeme?
- Networks ja volumes on vĂ”etud algsest docker-compose.yml (see, kus kogu stakk kĂ€ivitub), ja ma arvan, et need ei mĂ”juta suurt ĂŒldpilti.
- Loome ĂŒhe teenuse (services) logstash, kasutades pilti docker.elastic.co/logstash/logstash:6.3.2 ja anname sellele nimeks logstash_one_channel.
- Edastame konteinerisse pordi 5046, mis vastab sellele samale siseportile.
- Me kuvame oma kanali seadistusfaili ./config/pipelines.yml faili /usr/share/logstash/config/pipelines.yml konteineri sees, kust logstash selle ĂŒles leiab, ning teeme selle lugemisĂ”igusega ainult, lihtsalt igaks juhuks.
- Me kuvame katalooge ./config/pipelines, kus meie kanalite seadistuse failid asuvad, katalooge /usr/share/logstash/config/pipelines ja teeme selle samuti lugemisÔigusega.

Fail pipelines.yml
- pipeline.id: HABR
pipeline.workers: 1
pipeline.batch.size: 1
path.config: "./config/pipelines/habr_pipeline.conf"
Siin on kirjeldatud ĂŒks kanal identifikaatoriga HABR ja tee selle konfiguratsioonifailini.
Ja lÔpuks fail "./config/pipelines/habr_pipeline.conf"
input {
tcp {
port => "5046"
}
}
filter {
mutate {
add_field => [ "habra_field", "Hello Habr" ]
}
}
output {
stdout {
}
}
Ărme hetkel sĂŒvene selle kirjelduse detailidesse, proovime kĂ€ivitada:
docker-compose up
Mida me nÀeme?
Konteiner kÀivitus. Saame kontrollida selle tööd:
echo '13123123123123123123123213123213' | nc localhost 5046
Ja nÀeme konteineri konsoolis vastust:

Aga samas nÀeme ka:
logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Unable to retrieve license information from license server {:message=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch", âŠ
logstash_one_channel | [2019-04-29T11:28:59,894][INFO ][logstash.pipeline ] Pipeline started successfully {:pipeline_id=>".monitoring-logstash", :thread=>"#"}
logstash_one_channel | [2019-04-29T11:28:59,988][INFO ][logstash.agent ] Pipelines running {: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 installed on Logstash but not on Elasticsearch. Please install X-Pack on Elasticsearch to use the monitoring feature. Other features may be available.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Successfully started Logstash API endpoint {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] KĂ€ivitan tervisekontrolli, et nĂ€ha, kas Elasticsearchi ĂŒhendus töötab {:healthcheck_url=http://elasticsearch:9200/, :path="/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Ăritati parandada surnud ES nĂ€ite ĂŒhendust, kuid tekkis viga. {:url=«:9200/, :error_type=LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=«Elasticsearch ei ole kĂ€tte saadav: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch»}
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] KĂ€ivitan tervisekontrolli, et nĂ€ha, kas Elasticsearchi ĂŒhendus töötab {:healthcheck_url=http://elasticsearch:9200/, :path="/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Ăritati parandada surnud ES nĂ€ite ĂŒhendust, kuid tekkis viga. {:url=«:9200/, :error_type=LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=«Elasticsearch ei ole kĂ€tte saadav: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch»}
Ja meie logi tĂ”useb pidevalt ĂŒlespoole.
Siin olen ma rohelise vĂ€rviga esile tĂ”stnud sĂ”numi, et pipeline kĂ€ivitus edukalt, punasega â vea sĂ”numi ja kollasega â sĂ”numi ĂŒhenduse loomise katse kohta :9200.
See juhtub seetÔttu, et logstash.conf, mis kuulub pildi koosseisu, kontrollib elasticsearchi kÀtte saamist. Logstash eeldab, et töötab ELK stacki osana, aga me oleme ta eraldanud.
Töötamine on vÔimalik, kuid ebamugav.
Lahenduseks on see kontroll vĂ€lja lĂŒlitada keskkonnamuutuja XPACK_MONITORING_ENABLED kaudu.
Teeme muudatus docker-compose.yml-is ja kÀivitame uuesti:
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ĂŒĂŒd on kĂ”ik korras. Konteiner on katsetamiseks valmis.
Saame jÀlle naabruses asuvas konsoolis kirjutada:
echo '13123123123123123123123213123213' | nc localhost 5046
Ja nÀha:
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 | }
Töötamine ĂŒhe kanali raames
Nii et me alustasime. NĂŒĂŒd vĂ”ib tegelikult pĂŒhendada aega logstashi seadistamisele. Ărgem puudutagem veel pipelines.yml faili, vaatame, mida saame saavutada töötades ĂŒhe kanaliga.
Pean ĂŒtlema, et kanalikonfiguratsiooni faili tööpĂ”himĂ”te on hĂ€sti kirjeldatud ametlikus juhendis, siin
Kui soovite lugeda vene keeles, kasutasime siin seda (aga pĂ€ringute sĂŒntaks on seal vana, seda tuleb arvesse vĂ”tta).
Liigume jÀrjekindlalt Input sektsioonist edasi. TCP töötlemist oleme juba nÀinud. Mis siin veel huvitavat vÔiks olla?
Testspordialad, kasutades heartbeat'i
On selline huvitav vÔimalus genereerida automaatseid testspordialasid.
Selleks tuleb input sektsiooni lisada heartbean plugin.
input {
heartbeat {
message => "HeartBeat!"
}
}
LĂŒkkame sisse, hakkame kord minutis saama
logstash_one_channel | {
logstash_one_channel | "@timestamp" => 2019-04-29T13:52:04.567Z,
logstash_one_channel | "habra_field" => "Tere Habr",
logstash_one_channel | "message" => "HeartBeat!",
logstash_one_channel | "@version" => "1",
logstash_one_channel | "host" => "a0667e5c57ec"
logstash_one_channel | }
Tahame sagedamini, peame lisama parameetri interval.
Nii saame iga 10 sekundi tagant teate.
input {
heartbeat {
message => "HeartBeat!"
interval => 10
}
}
Andmete saamine failist
Oleme ka otsustanud vaadata faili reĆŸiimi. Kui failiga töötab normaalselt, siis vĂ”ib-olla ei ole agenti vaja, vĂ€hemalt kohalikuks kasutamiseks.
Kirjelduse kohaselt peaks tööreĆŸiim olema sarnane tail -f, st loeb uusi ridu vĂ”i, nagu valik, loeb kogu faili.
Nii et mida me tahame saada:
- Tahame saada ridu, mis lisatakse ĂŒhte logifaili.
- Tahame saada andmeid, mis kirjutatakse mitmesse logifaili, samas soovides eristada, kust miski saadud on.
- Tahame kontrollida, et kui logstash'i taaskÀivitame, siis ei saada neid andmeid uuesti.
- Tahame kontrollida, et kui logstash vĂ€lja lĂŒlitada, ja andmeid kirjutatakse failidesse edasi, siis kui me selle kĂ€ivitame, saame need andmed.
Eksperimendi lĂ€biviimiseks lisame docker-compose.yml faili veel ĂŒhe rida, avades kausta, kuhu me failid asetame.
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
Ja muudame input sektsiooni habr_pipeline.conf'is
input {
file {
path => "/usr/share/logstash/input/*.log"
}
}
KĂ€ivitame:
docker-compose up
Logifailide loomise ja kirjutamise jaoks kasutame kÀsku:
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 | }
Jah, see töötab!
Samuti nÀeme, et meil on automaatselt lisandunud vÀli path. See tÀhendab, et edaspidi saame selle alusel kirjeid filtreerida.
Proovime veel:
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 | }
NĂŒĂŒd teise faili:
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 | }
SuurepÀrane! Fail tuvastati, path mÀÀrati Ôigesti, kÔik on hÀsti.
TĂSTAME logstashi ja kĂ€ivita uuesti. Ootame. Vaikus. See tĂ€hendab, et me ei saa neid kirjeid uuesti.
Ja nĂŒĂŒd kĂ”ige julgem eksperiment.
Paneme logstashi seisma ja teeme:
echo '3' >> logs/number2.log
echo '4' >> logs/number1.log
KÀivitame logstashi uuesti ja nÀeme:
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 | }
Hurraa! KÔik tuvastati.
Aga, peame hoiatama jĂ€rgmise ĂŒle. Kui logstashi konteiner kustutatakse (docker stop logstash_one_channel && docker rm logstash_one_channel), siis ei midagi tuvastata. Konteineri sees oli salvestatud faili positsioon, kuhu seda oli loetud. Kui kĂ€ivitada ânullistâ, siis aktsepteeritakse ainult uusi ridu.
Olemasolevate failide lugemine
Oletame, et kÀivitame logstashi esmakordselt, kuid meil on juba logid ja soovime neid töötlema hakata.
Kui me kÀivitame logstashi eelmises osas kasutatud input sektsiooniga, siis me ei saa midagi. Ainult uusi rea kirjutatakse logstashi.
Selleks, et olemasolevatest failidest read ĂŒles haarata, tuleb input sektsiooni lisada tĂ€iendav rida:
input {
file {
start_position => "beginning"
path => "/usr/share/logstash/input/*.log"
}
}
Oluline on mĂ€rkida, et see kehtib ainult uute failide kohta, mida logstash pole veel nĂ€inud. Juba logstashi nĂ€htud failid on selle suuruse meelde jĂ€tnud ja nĂŒĂŒd vĂ”etakse nendest ainult uued kirjed.
Peatume sellel, et uurime input sektsiooni. Seal on veel palju variante, kuid meie edasiste eksperimentide jaoks piisab praegu sellest.
Andmete marsruutimine ja töötlemine
Proovime lahendada jĂ€rgmise ĂŒlesande: oletame, et meil on ĂŒhiselt sĂ”numid ĂŒhest kanalist, millest osa on informatiivsed ja osa on veateated. Need erinevad sildiga. Ăhed INFO, teised ERROR.
Me peame need vĂ€ljundis eraldi jagama. St. informatiivsed sĂ”numid kirjutame ĂŒhte kanalis ja veateated teise.
Selleks liigume input sektsioonist filter ja vÀljundisse.
Filter sektsiooni abil analĂŒĂŒsime sisenevat sĂ”numit, saades sellest hash (vĂ”ti-vÀÀrtus paarid), millega juba töötada, st. jagame tingimuste jĂ€rgi. Ja vĂ€ljundis valime sĂ”numid ja saadame igaĂŒhe oma kanalisse.
SĂ”numi analĂŒĂŒs groki abil
Tekstiread analĂŒĂŒsimiseks ja vĂ€li kogumi saamiseks on filter sektsioonis spetsiaalne pistik. â grok.
Ilma, et mul oleks eesmÀrk anda siin selle detailne kirjeldus (selle jaoks suunan teid ), annan ma oma lihtsa nÀite.
Selleks tuleb mÀÀratleda sisenemise ridade formaat. Minu omad on sellised:
1 INFO message1
2 ERROR message2
St. identifikaator esimesel kohal, seejĂ€rel INFO/ERROR, seejĂ€rel mĂ”ni sĂ”na ilma tĂŒhikuta.
Ei midagi keerulist, kuid tööpÔhimÔtte mÔistmiseks piisab.
Nii et filter sektsioonis grok pistikus peame mÀÀrama mustri, et analĂŒĂŒsida meie ridu.
See nÀeb vÀlja selline:
filter {
grok {
match => { "message" => ["%{INT:message_id} %{LOGLEVEL:message_type} %{WORD:message_text}"] }
}
}
Tegelikult on see regulaarne avaldis. Kasutatakse juba valmis mustreid, nagu INT, LOGLEVEL, WORD. Nende kirjeldust ja muid mustreid vÔib vaadata siit
NĂŒĂŒd, minnes lĂ€bi selle filtri, muudetakse meie rida hash'iks kolmelt vĂ€ljade: message_id, message_type, message_text.
Just need vÀljad kuvatakse vÀljundis.
SÔnumite suunamine vÀljundsektsioonis if-kÀsu abil
VĂ€ljundsektsioonis, nagu me mĂ€letame, kavatsesime jagada sĂ”numid kaheks vooks. Ăhed, mis on INFO, kuvame konsoolile, ja vead, kirjutame faili.
Kuidas saame neid sĂ”numeid eraldada? Ălesande tingimus annab meile lahenduse â meil on juba eraldi vĂ€li message_type, mis vĂ”ib vĂ”tta ainult kaks vÀÀrtust: INFO ja ERROR. Just selle alusel teeme valiku if-operaatoriga.
if [message_type] == "ERROR" {
# Siin kirjutame faili
} else
{
# Siin kirjutame stdout'i
}
Andmete ja operaatorite töö kirjeldust saab vaadata siin sektsioonis .
NĂŒĂŒd, mis puudutab vĂ€ljundit.
VĂ€ljund konsoolile, siin on kĂ”ik selge â stdout {}
Aga vĂ€ljund faili â meenutame, et me kĂ€ivitame kĂ”ik konteinerist ja et fail, kuhu me kirjutame tulemuse, oleks vĂ€ljastpoolt ligipÀÀsetav, peame avatama selle katalooge docker-compose.yml-is.
KokkuvÔtteks:
Meie faili vÀljundsektsioon nÀeb vÀlja selline:
output {
if [message_type] == "ERROR" {
file {
path => "/usr/share/logstash/output/test.log"
codec => line { format => "custom format: %{message}"}
}
} else
{stdout {
}
}
}
docker-compose.yml-le lisame veel ĂŒhe mahu, vĂ€ljundiks:
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
KÀivitame, proovime, nÀeme jagunemist kaheks vooks.
Allikas: habr.com
