Sissejuhatus
Uue süsteemi seadistamisel seisime silmitsi vajadusega töödelda suurt hulka erinevaid logisid. Tööriistana valisime ELK-i. Käesolevas artiklis räägime oma kogemustest selle tehnoloogia seadistamisel.
Me ei püüa kirjeldada kõiki selle võimalusi, vaid tahame keskenduda praktiliste probleemide lahendamisele. See on tingitud sellest, et kuigi dokumentatsiooni ja valmisipildid on piisavalt, võib alatu takistusi olla palju, vähemalt meie kogemuses ilmnesid need.
Seadsime stacki üles docker-compose'i kaudu. Veelgi enam, meil oli hästi kirjutatud docker-compose.yml, mis võimaldas meil peaaegu probleemideta stacki üles tõsta. Meile tundus, et võit on käeulatuses, nüüd kohandame seda veidi oma vajadustele ja kõik on korras.
Kahjuks ei õnnestunud meil kohe logide vastuvõtmise ja töötlemise seadistamine meie rakenduselt. Seetõttu otsustasime, et tasub uurida iga komponenti eraldi ja seejärel naasta nende seoste juurde.
Nii et alustasime logstashist.
Keskkond, Logstashi seadistamine ja paigaldamine konteineris
Kasutame rakenduste seadistamiseks docker-compose'i, need eksperimentide kirjeldused viidi läbi MacOS-is ja Ubuntu 18.0.4-l.
Meie docker-compose.yml failis määratletud logstash pilt on docker.elastic.co/logstash/logstash:6.3.2.
Seda kasutame eksperimenteerimiseks.
Logstashi käitamiseks kirjutasime eraldi docker-compose.yml faili. Loomulikult oleks võinud pilti käsurealt käivitada, kuid me lahendasime konkreetset ülesannet, kus kõik käivitatakse docker-compose'i kaudu.
Lühike ülevaade konfiguratsioonifailidest.
Kuna kirjelduse kohaselt saab logstashi käivitada kas ühe kanali jaoks, selleks tuleb edastada *.conf fail, või mitme kanali jaoks, sel juhul tuleb edastada pipelines.yml fail, mis omakorda viitab .conf failidele iga kanali jaoks.
Valisime teise tee. See tundus meile universaalsem ja skaleeritavam. Seetõttu lõime pipelines.yml faili ja kujundasime pipelines katalooge, kuhu paigutame .conf failid iga kanali jaoks.
Konteineris on veel üks konfiguratsioonifail — logstash.yml. Me ei puuduta seda, kasutame seda sellisena nagu see on.
Nii et meie kataloogide struktuur on järgmine:

Sisendi andmete saamiseks oletame, et see on TCP sadamal 5046 ning väljundi jaoks kasutame stdout.
Siin on nii lihtne konfiguratsioon esmakäivitamiseks. Lõppude lõpuks on algne eesmärk — käivitada.
Nii et meil on 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?
- Võrgud ja mahutid on võetud algsest docker-compose.yml-st (see, kus kogu virn käivitub), ja ma arvan, et nad ei mõjuta oluliselt üldpilti.
- Loome ühe teenuse (services) logstash, kasutades pilti docker.elastic.co/logstash/logstash:6.3.2 ning määrame sellele nimeks logstash_one_channel.
- Me edastame konteinerisse sadama 5046, samal sisemisel sadamal.
- Me kaardistame oma kanali seadistusfaili ./config/pipelines.yml failile /usr/share/logstash/config/pipelines.yml konteineri sees, kust logstash selle haarab ja teeme selle ainult lugemiseks, igaks juhuks.
- Kuvame katalooge ./config/pipelines, kus asuvad meie kanalite seadistamise failid, katalooge /usr/share/logstash/config/pipelines ja muudame selle samuti ainult lugemiseks.

Fail pipelines.yml
- pipeline.id: HABR
pipeline.workers: 1
pipeline.batch.size: 1
path.config: "./config/pipelines/habr_pipeline.conf"
Siin on kirjeldatud ühte kanalit ID-ga HABR ja tee selle konfigureerimisfailini.
Ja lõpuks fail «./config/pipelines/habr_pipeline.conf"
input {
tcp {
port => "5046"
}
}
filter {
mutate {
add_field => [ "habra_field", "Hello Habr" ]
}
}
output {
stdout {
}
}
Ärme süvene selle kirjelduse detaile, proovime seda käivitada:
docker-compose up
Mida me näeme?
Konteiner käivitati. Saame kontrollida selle tööd:
echo '13123123123123123123123213123213' | nc localhost 5046
Ja näeme konteineri konsoolis vastust:

Kuid samal ajal 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 on Logstash is installed, but not on Elasticsearch. Please install X-Pack on Elasticsearch to enable the monitoring feature. Other features might still be available.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Logstash API endpoint successfully started {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Running health check to verify if the Elasticsearch connection is functioning {:healthcheck_url=>http://elasticsearch:9200/, :path=>"/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Attempted to revive the connection to a dead ES instance, but encountered an error. {:url=>«: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] Running health check to verify if the Elasticsearch connection is functioning {:healthcheck_url=>http://elasticsearch:9200/, :path=>"/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Attempted to revive the connection to a dead ES instance, but encountered an error. {:url=>«:9200/», :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>«Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch»}
Ja meie logi tõuseb pidevalt.
Siin olen tõstnud roheline värviga esile sõnumi, et pipeline käivitus edukalt, punasega — veateate ja kollasega — sõnumi katsetest ühendust võtta. :9200.
See, see, et on logstash.conf, mis on kaasatud pildi, olemas elasticsearch'i kättesaadavuse kontroll. Logstash eeldab, et see töötab Elk stackis, kuid me oleme selle eraldanud.
Töötamine on võimalik, aga ebamugav.
Lahenduseks on selle kontrolli keelamine XPACK_MONITORING_ENABLED keskkonnamuutuja kaudu.
Teeme muudatuse docker-compose.yml 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 uuesti naaberterminalis 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öö ühe kanali piires
Nii, oleme käivitanud. Nüüd saame aega pühendada logstashi seadistamisele. Ärgem puudutame veel faili pipelines.yml, vaatame, mida saame teha ühe kanaliga.
Pean ütlema, et kanali konfigureerimise üldine põhimõte on hästi kirjeldatud ametlikus juhendis, siin on link:
Kui soovite lugeda vene keeles, siis kasutasime seda: (aga päringute süntaks seal on vana, seda tuleb silmas pidada).
Liigume järjestikku Input sektsiooni. TCP töö on juba nähtud. Mis veel siin võiks huvitav olla?
Test sõnumid, kasutades heartbeat'i
On olemas huvitav võimalus genereerida automaatseid test sõnumeid.
Selleks tuleb input sektsiooni lisada heartbeat plugin.
input {
heartbeat {
message => "HeartBeat!"
}
}
Aktiveerime, hakkame saama sõnumeid iga minuti tagant.
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 saada, peame lisama parameetri interval.
Nii saame sõnumi iga 10 sekundi tagant.
sisend {
südame löögisagedus {
sõnum => "Südame löögisagedus!"
intervall => 10
}
}
Andmete saamine failist
Otsustasime ka vaadata file režiimi. Kui failiga töötab normaalselt, siis võib-olla ei ole agenti üldse vaja, vähemalt kohaliku kasutuse jaoks.
Kirjelduse järgi peaks töörežiim olema analoogne tail -f, st loeb uusi ridu või, nagu valik, loeb kogu faili.
Nii et mida me tahame saada:
- Me tahame saada ridu, mis lisatakse ühte logifaili.
- Me tahame saada andmeid, mis kirjutatakse mitmesse logifaili, samas on meil võimalus eristada, kust mis on saadud.
- Me tahame kontrollida, et logstash'i taaskäivitamisel ei saa ta neid andmeid uuesti.
- Me tahame kontrollida, et kui logstash lahti ühendada, aga andmeid faile jätkuvalt kirjutatakse, siis kui me selle käivitame, saame need andmed.
Eksperimendi läbiviimiseks lisame docker-compose.yml faili veel ühe rea, avades kausta, kuhu me failid paneme.
versioon: '3'
võrgud:
elk:
mahud:
elasticsearch:
draiver: kohalik
teenused:
logstash:
konteineri_nimi: logstash_one_channel
pilt: docker.elastic.co/logstash/logstash:6.3.2
võrgud:
- elk
keskkond:
XPACK_MONITORING_ENABLED: "false"
sadamad:
- 5046:5046
mahud:
- ./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 sektsiooni input failis habr_pipeline.conf
input {
file {
path => "/usr/share/logstash/input/*.log"
}
}
Käivitame:
docker-compose up
Logifailide loomise ja salvestamise 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 | }
Nojah, töötab!
Sellega näeme, et meil on automaatselt lisatud väli path. See tähendab, et tulevikus 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 | }
Ja nüüd teises failis:
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 on edukalt laaditud, path on õige, kõik on hästi.
Peatame logstashi ja käivitame selle uuesti. Ootame. Vaikus. Ehk siis, neid kirjeid me uuesti ei saa.
Ja nüüd kõige julgem katse.
Kandke logstash ja täitke:
echo '3' >> logs/number2.log
echo '4' >> logs/number1.log
Käivitage logstash 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 | }
Hästi! Kõik on laaditud.
Kuid tuleb tähelepanu juhtida järgmisele. Kui konteiner logstash eemaldatakse (docker stop logstash_one_channel && docker rm logstash_one_channel), siis ei jää midagi kinni. Konteineris säilitati faili positsioon, millele oli lugemine peatatud. Kui käivitame "nullist", siis aktsepteerib see ainult uusi ridu.
Juba olemasolevate failide lugemine
Oletame, et käivitame logstashi esmakordselt, kuid meil on juba logid ja tahame neid töödelda.
Kui käivitame logstashi sama input-sektsiooniga, mida kasutasime varem, siis ei saa me midagi. Ainult uusi ridu töödeldakse logstashi poolt.
Et olemasolevatest failidest ridasid võtta, tuleb input-sektsiooni lisada järgmine 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 varasemalt logstashi tähelepanu saanud failide puhul on see juba nende suuruse meelde jätnud ja nüüd võtab sealt ainult uusi kirjeid.
Lõpetame selle input-sektsiooni uurimisega. Seal on veel palju variante, kuid meie edasiseks katsetamiseks piisab praegu sellest.
Andmete suunamine ja töötlemine
Proovime lahendada järgmise ülesande, oletame, et meil on teavet, mis tuleb ühelt kanalilt, osa neist on informatiivsed ja osa veateated. Need erinevad sildi poolest. Ühed on INFO, teised ERROR.
Me peame need väljundis eraldama. St. Informatiivsed teated kirjutame ühte kanalisse ja veateated teise.
Selleks liigume input sektsioonist filteri ja väljundisse.
Filtri sektsiooni abil analüüsime sisendteate, saades selle tulemusena hash (võti-väärtus paarid), millega on juba võimalik töötada, st. analüüsida tingimuste alusel. Väljundi sektsioonis valime teated ja saadame igaühe oma kanalisse.
Teate analüüsimine grok abil
Kuna me tahame analüüsida tekstiridasid ja saada neist välja hulga välju, on filtri sektsioonis spetsiaalne pistikprogramm — grok.
Ilma et seaksime eesmärgiks selle üksikasjaliku kirjelduse andmise (selle jaoks suunangi teid), toome oma lihtsa näite.
Selleks tuleb kindlaks teha sisendriistade format. Minu omad näevad välja sellised:
1 INFO message1
2 ERROR message2
St. Identsifitseerija on esimesel kohal, seejärel INFO/ERROR, seejärel mõni sõna ilma tükkideta.
Ei ole keeruline, kuid põhimõtte mõistmiseks piisab sellest.
Seega, grok plugin'is filter jaotises peame määrama mustri meie stringide analüüsimiseks.
See näeb välja järgmine:
filter {
grok {
match => { "message" => ["%{INT:message_id} %{LOGLEVEL:message_type} %{WORD:message_text}"] }
}
}
Põhimõtteliselt on see regulaarne väljend. Kasutatakse juba olemasolevaid mustreid, nagu INT, LOGLEVEL, WORD. Nende kirjeldust ning teisi mustreid saab vaadata siit
Nüüd, liikudes läbi selle filtri, muutub meie string hash'iks, mis koosneb kolmest väljast: message_id, message_type, message_text.
Need väljad kuvatakse output jaotises.
Sõnumite marsruutimine output jaotises if käsu abil
Output jaotises, nagu me mäletame, plaanisime jagada sõnumid kaheks vooguks. Ühed – INFO, kuvatakse konsoolile, ja veateated salvestatakse faili.
Kuidas need sõnumid jagada? Ülesande tingimus juba vihjab lahendusele – meil on juba eraldatud väli message_type, mis võib omada ainult kahte väärtust INFO ja ERROR. Just selle põhjal teeme valiku if operaatori abil.
if [message_type] == "ERROR" {
# Siin väljastame faili
} else
{
# Siin väljastame stdout'i
}
Väljade ja operaatoritega töötamise kirjeldust saab vaadata siit .
Nüüd, räägime täpselt väljundist.
Väljund konsooli, siin on kõik selge — stdout {}
Aga väljund faili — meenutame, et käivitame seda kõik konteinerist ja et fail, kuhu me kirjutame tulemuse, oleks väljastpoolt kättesaadav, peame avama selle katalooge docker-compose.yml-is.
Kokku:
Meie faili väljundi osa 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-i 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 kahte voogu.
Allikas: habr.com
