introduction
Dum systema nostrum recentissimum disponebamus, necessitate incidimus ut magnum numerum diversorum diariorum tractaremus. ELK ut instrumentum elegimus. Hic articulus experientiam nostram in hac structura configuranda disseret.
Non omnes eius facultates describere propositum nostrum est, sed potius problemata practica solvere. Hoc fit quia, quamvis ampla documentatione et imaginibus iam paratis praesto sint, multae insidiae exstant — saltem eas nos invenimus.
Acervum programmatis "Docker-compose" adhibuimus. Praeterea, fasciculum "Docker-compose.yml" bene scriptum habuimus, quo acervum sine ullo negotio disponere potuimus. Et victoriam prope esse putavimus; paucae tantum modificationes ad necessitates nostras accommodandae erant, et id est.
Infeliciter, primum conatum nostrum systema subtiliter adaptandi ad accipienda et tractanda acta ex applicatione nostra irritum fuit. Quapropter, statuimus singula elementa seorsum examinare, deinde ad nexus eorum redire.
Itaque, cum logstash incipiamus.
Ambitus, dispositio, et cursus Logstash in receptaculo
Ad usum docker-compose utimur, experimenta hic descripta in MacOS et ... peracta sunt. Ubuntu 18.0.4.
Imago logstash quae in nostro originali docker-compose.yml specificata est est docker.elastic.co/logstash/logstash:6.3.2.
Ad experimenta eo utemur.
Ad Logstash incipiendum, separatum `docker-compose.yml` scripsimus. Sane, imaginem ex linea mandati currere potuissemus, sed problema specificum solvebamus ubi omnia ex `Docker-compose` currunt.
Breviter de fasciculis configurationis
Ut descriptio indicat, `logstash` pro uno canali curri potest, quo in casu fasciculus *.conf ei tradendus est, vel pro pluribus canalibus, quo in casu fasciculus `pipelines.yml` ei tradendus est, qui vicissim fasciculos `.conf` pro singulis canalibus referet.
Ad secundam rationem sequimur. Universalior et scalabilior videbatur. Ergo, fasciculum "pipelines.yml" et directorium "pipelines" creavimus, ubi fasciculos ".conf" pro singulis canalibus collocabimus.
Aliud fasciculum configurationis intra receptaculum est—logstash.yml. Eum intactum relinquemus et eo ut est utemur.
Ergo, structura directoriorum nostrorum:

Interim, ad accipienda data ingressa, supponemus TCP esse in portu 5046, et ad egressum stdout utemur.
Ecce configuratio simplex pro primo initium. Postremo, primum opus est initium facere.
Itaque, hoc habemus: `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
Quid hic videmus?
- Retia et volumina ex originali docker-compose.yml (eo ubi tota acervus emittitur) sumpta sunt, nec puto magnum momentum in imaginem totam habere.
- Unum servitium "logstash" ex imagine "docker.elastic.co/logstash/logstash:6.3.2" creamus et "logstash_one_channel" nominamus.
- Portum 5046 intra receptaculum ad eundem portum internum transmittimus.
- Fasciculum configurationis canalis nostri ./config/pipelines.yml cum fasciculo /usr/share/logstash/config/pipelines.yml intra receptaculum assignamus, unde logstash eum accipiet et, si forte eveniat, solam legendi locum faciet.
- Directorium "./config/pipelines", ubi fasciculos configurationis canalium reponimus, ad directorium "/usr/share/logstash/config/pipelines" assignamus et eum etiam solae lectioni destinamus.

Fasciculus pipelines.yml
- pipeline.id: HABR
pipeline.workers: 1
pipeline.batch.size: 1
path.config: "./config/pipelines/habr_pipeline.conf"
Hic unus canalis cum identificatore HABR et via ad eius fasciculum configurationis describitur.
Et denique, fasciculus "./config/pipelines/habr_pipeline.conf"
input {
tcp {
port => "5046"
}
}
filter {
mutate {
add_field => [ "habra_field", "Hello Habr" ]
}
}
output {
stdout {
}
}
Ne descriptionem eius nunc ingrediamur, conemur eam currere:
docker-compose up
Quid videmus?
Receptaculum inceptum est. Operationem eius experiri possumus:
echo '13123123123123123123123213123213' | nc localhost 5046
Et responsionem in consola continentis videmus:

Sed simul etiam videmus:
logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Informationem de licentia ex servo licentiarum recuperare non potest {:message=>“Elasticsearch Inaccessibile: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch”, ...
logstash_one_channel | [2019-04-29T11:28:59,894][INFO][logstash.pipeline] Canalis feliciter inceptus est {:pipeline_id=>".monitoring-logstash", :thread=>"# »}
logstash_one_channel | [2019-04-29T11:28:59,988][INFO][logstash.agent] Canales currentes {: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 in Logstash sed non in Elasticsearch installatum est. Quaeso X-Pack in Elasticsearch instala ut functione monitoria utaris. Aliae functiones fortasse praesto sunt.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][ logstash.agent ] Feliciter incepit Logstash API endpoint {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Examen sanitatis currens ut videas num nexus Elasticsearch recte fungatur {:healthcheck_url=>http://elasticsearch:9200/, :path=>"/"}
logstash_one_channel | [2019-04-29T11:29:04,487][MONITUM][logstash.outputs.elasticsearch] Conatus sum nexum ad instantiam ES mortuam resuscitare, sed errorem accepi. {:url=>“`Elasticsearch Inaccessibile: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch``
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Examen sanitatis currens ut videas num nexus Elasticsearch recte fungatur {:healthcheck_url=>http://elasticsearch:9200/, :path=>"/"}
logstash_one_channel | [2019-04-29T11:29:04,710][MONITUM][logstash.licensechecker.licensereader] Conatus sum nexum ad instantiam ES mortuam resuscitare, sed errorem accepi. {:url=>“`Elasticsearch Inaccessibile: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elasticsearch``
Et diarium nostrum serpere pergit.
Hic viridi colore notavi nuntium de conatu contactus prospere incepto, rubro colore nuntium erroris, flavo colore nuntium de conatu contactus. : 9200.
Hoc fit quia fasciculus logstash.conf in imagine inclusus probationem disponibilitatis elasticsearch continet. Postremo, logstash assumit se currere ut pars stack Elk, quem separavimus.
Laborare potest, sed non commodum.
Solutio est hanc probationem per variabilem ambientis XPACK_MONITORING_ENABLED inactivare.
Mutationem in `docker-compose.yml` faciamus et iterum curramus:
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
Nunc omnia bene sunt. Vasculum ad experimenta paratum est.
Iterum in proxima consola scribere possumus:
echo '13123123123123123123123213123213' | nc localhost 5046
Et vide:
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 | }
Operans intra unum canalem
Itaque, iam operamur. Nunc tempus aliquod Logstash ipsum configurando impendere possumus. Fasciculum pipelines.yml nunc solum relinquemus; videamus quid uno ductu (pipeline) efficere possimus.
Dicendum est principium generale operandi cum fasciculo configurationis canalis bene descriptum esse in manuali officiali, hic.
Si Russice legere vis, hoc usi sumus. (sed syntaxis interrogationis ibi vetus est, hoc in rationem ducere oportet.)
A sectione "Input" ordine progrediamur. Iam vidimus quomodo TCP operatur. Quid aliud hic fortasse interest?
Nuntia probare utens pulsu cordis
Est facultas curiosa ad nuntia probationis automatica generanda.
Ad hoc faciendum, plugin "heartbean" in sectione input includere debes.
input {
heartbeat {
message => "HeartBeat!"
}
}
Accendimus et semel in minuto accipere incipimus.
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 | }
Si saepius accipere volumus, parametrum "interval" addere debemus.
Hoc modo nuntium singulis decem secundis accipiemus.
input {
heartbeat {
message => "HeartBeat!"
interval => 10
}
}
Accipiendo notitias ex fasciculo
Modum fasciculorum quoque experiri decrevimus. Si bene cum fasciculis operatur, fortasse nullus agens requiretur, saltem ad usum localem.
Secundum descriptionem, modus operandi similis esse debet `tail -f`, id est, novas lineas legit vel, si optio est, totum fasciculum legit.
Quod igitur adipisci volumus:
- Lineas uni fasciculo diarii adiungendas accipere volumus.
- Data in pluribus fasciculis diarii inscripta accipere volumus, dum facultate separandi quae accepta et ubi accepta sunt.
- Volumus verificare, cum logstash denuo incipitur, has datas non iterum accepturum esse.
- Volumus explorare, si Logstash inactivum est, sed data in fasciculos scribi pergunt, tum cum illud incipimus, haec data accepturos esse.
Ad experimentum peragendum, unam lineam amplius ad docker-compose.yml addamus, directorium ubi fasciculos collocavimus aperientes.
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
Et sectionem input in habr_pipeline.conf mutemus.
input {
file {
path => "/usr/share/logstash/input/*.log"
}
}
Incipiamus:
docker-compose up
Ad fasciculos diarii creandos et scribendos, hoc mandato utemur:
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 | }
Etiam, operatur!
Videmus etiam campum semitae automatice additum esse. Hoc significat nos in futuro posse inscriptiones secundum eam filtrare.
Iterum experiamur:
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 | }
Et nunc in alio fasciculo:
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 | }
Optime! Fasciculus sumptus est, via recte definita, omnia bene sunt.
Sistamus "logstash" et denuo incipiamus. Expectemus. Silentium. Hoc est, has inscriptiones iterum non accipimus.
Et nunc experimentum audacissimum.
`logstash` instala et haec curre:
echo '3' >> logs/number2.log
echo '4' >> logs/number1.log
Iterum `logstash` exsequimur et videmus:
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 | }
Euge! Omnia sublata sunt.
Tamen te de his rebus monere debeo. Si receptaculum Logstash deletum est (docker stop logstash_one_channel && docker rm logstash_one_channel), nihil extrahetur. Locus fasciculi ubi lectum est intra receptaculum servatus est. Si ab initio incipis, novas tantum lineas accipiet.
Lectio fasciculorum existentium
Dicamus nos primum logstash currere, sed iam acta habemus et ea tractare velimus.
Si `logstash` eadem sectione input quam supra usurpavimus exsequimur, nihil accipiemus. Solae novae lineae a `logstash` tractabuntur.
Ad lineas ex fasciculis exstantibus extrahendas, lineam additam sectioni input addere debes:
input {
file {
start_position => "beginning"
path => "/usr/share/logstash/input/*.log"
}
}
Est monitio: hoc solum ad nova documenta pertinet quae Logstash nondum vidit. Pro documentis quos Logstash iam vidit, magnitudinem eorum iam meminit et nunc solum nova documenta in eis includet.
Hic subsistamus et sectionem input studeamus. Multae plures optiones ibi sunt, sed nunc, haec sufficiunt ad ulteriores experimenta.
Iter et transformatio datorum
Conemur solvere problema sequens. Fingamus nos habere nuntios ex uno canali mittentes, quorum quidam informativi, quidam erroris. Hi nuntii suis notis distinguuntur. Alii sunt INFO, alii ERROR.
Ea in exitu separare debemus. Hoc est, nuntios informationis in unum canalem, nuntios erroris in alterum scribimus.
Ad hoc faciendum, a sectione input ad filtrum et output progredimur.
Sectione "filter" utimur, nuntium advenientem analizabimus, eius "hash" (paria clavis-valoris) extrahentes ut eum tractare possimus, id est, secundum condiciones nostras analizare. Et in sectione "output", nuntios eligemus et unumquemque ad suum proprium canalem mittemus.
Nuntium cum grok interpretari
Ad seriem camporum interpretandam et ex eis seriem textuum obtinendam, sectio filtri plugin speciale habet — grok.
Non est mihi propositum hic eius descriptionem accuratam dare (ad hoc consulto) ), exemplum meum simplex dabo.
Ad hoc faciendum, de forma litterarum inputatarum decernere debes. Meae tales sunt:
Nuntius 1 INFORMATIONIS
Nuntius ERRORI II
Id est, primum identificator venit, deinde INFO/ERROR, tum quoddam verbum sine spatiis.
Non est complicatum, sed satis est ad intellegendum principium operationis.
Ergo, in sectione filtri, in supplemento grok, exemplar ad interpretandas litteras nostras definire debemus.
Sic apparebit:
filter {
grok {
match => { "message" => ["%{INT:message_id} %{LOGLEVEL:message_type} %{WORD:message_text}"] }
}
}
Essentialiter, est expressio regularis. Utitur formis iam factis, ut INT, LOGLEVEL, et WORD. Descriptiones earum, necnon aliae formae, hic inveniri possunt.
Nunc, per hoc filtrum transeunte, nostra series litterarum in seriem trium agrorum convertetur: message_id, message_type, message_text.
Haec sunt quae in sectione exitus ostendentur.
Nuntios in sectione emissionis dirigendo utens imperio "if"
In sectione "output", ut meminimus, nuntios in duos rivos dividere constitueramus. Qui "iNFO" continent, in consolam mittentur, dum qui errores continent, in fasciculum mittentur.
Quomodo haec nuntia separamus? Enuntiatio problematis iam solutionem suggerit—iam campum dedicatum `message_type` habemus, qui tantum duos valores accipere potest: INFO et ERROR. Hoc campo utemur ad electionem faciendam utentes enuntiatione `if`.
if [message_type] == "ERROR" {
# Здесь выводим в файл
} else
{
# Здесь выводим в stdout
}
Descriptio operandi cum campis et operatoribus in hac sectione inveniri potest. .
Nunc, de ipsa conclusione.
Exitus consolae, omnia hic clara sunt - stdout {}
Quod ad output in fasciculum attinet, memento nos haec omnia ex receptaculo currere, et ut fasciculus in quem output scribimus extrinsecus accessibilis sit, hunc directorium in docker-compose.yml aperire debemus.
summa
Pars exitus fasciculi nostri hoc modo apparet:
output {
if [message_type] == "ERROR" {
file {
path => "/usr/share/logstash/output/test.log"
codec => line { format => "custom format: %{message}"}
}
} else
{stdout {
}
}
}
Adde aliud volumen ad docker-compose.yml pro exitu:
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
Incipimus, experiamur, et divisionem in duos rivos videmus.
Source: www.habr.com
