Praktesch Uwendung vun ELK. Logstash opbauen

Aféierung

Beim Ofbau vun engem anere System ware mir konfrontéiert mat der Bedierfnes fir eng grouss Zuel vu verschiddene Logbicher ze veraarbecht. ELK gouf als Tool gewielt. Dësen Artikel wäert eis Erfarung diskutéieren beim Ariichten vun dësem Stack.

Mir setzen net en Zil fir all seng Fäegkeeten ze beschreiwen, mä mir wëllen eis speziell op d'Léisung vu praktesche Problemer konzentréieren. Dëst ass wéinst der Tatsaach, datt och wann et eng zimlech grouss Quantitéit vun Dokumentatioun a prett-feieren Biller ass, sinn et relativ vill Fallen, op d'mannst hu mir se fonnt.

Mir hunn de Stack iwwer Docker-compose ofgesat. Ausserdeem hate mir eng gutt geschriwwen docker-compose.yml, déi eis erlaabt de Stack bal ouni Problemer ze erhéijen. An et huet eis geschéngt datt d'Victoire scho no wier, elo wäerte mir et e bëssen upassen fir eise Besoinen ze passen an dat ass et.

Leider war de Versuch de System ze konfiguréieren fir Logbicher vun eiser Applikatioun ze kréien an ze veraarbechten net direkt erfollegräich. Dofir hu mir décidéiert datt et derwäert wier all Komponent separat ze studéieren, an dann zréck op hir Verbindungen.

Also hu mir ugefaang mat Logstash.

Ëmfeld, Deployment, Lafen Logstash an engem Container

Fir Deployment benotze mir Docker-compose; d'Experimenter, déi hei beschriwwe ginn, goufen op MacOS an Ubuntu 18.0.4 duerchgefouert.

Образ logstash, который был прописан у нас в исходном docker-compose.yml, это docker.elastic.co/logstash/logstash:6.3.2

Mir wäerten et fir Experimenter benotzen.

Mir hunn eng separat docker-compose.yml geschriwwen fir Logstash ze lafen. Natierlech war et méiglech d'Bild vun der Kommandozeil ze lancéieren, awer mir hunn e spezifesche Problem geléist, wou mir alles aus Docker-compose lafen.

Kuerz iwwer Configuratiounsdateien

Wéi aus der Beschreiwung follegt, kann Logstash entweder fir ee Kanal lafen, an deem Fall muss et d'*.conf Datei passéieren, oder fir verschidde Kanäl, an deem Fall muss et d'Pipelines.yml Datei passéieren, déi am Tour , wäert op d'Fichier'en .conf fir all Kanal verbannen.
Мы пошли по второму пути. Он нам показался более универсальным и масштабируемым. Поэтому, мы создали pipelines.yml, и сделали директорию pipelines, в которую будем класть файлы .conf для каждого канала.

Внутри контейнера есть ещё один конфигурационный файл — logstash.yml. Мы его не трогаем, используем как есть.

Also, eis Verzeechnesstruktur:

Praktesch Uwendung vun ELK. Logstash opbauen

Для получения входных данных пока считаем, что это tcp по порту 5046, а для вывода будем использовать stdout.

Hei ass eng einfach Konfiguratioun fir den éischte Start. Well déi éischt Aufgab ass ze starten.

Also, mir hunn dës 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

Wat gesi mer hei?

  1. Networks и volumes были взяты из исходного docker-compose.yml (тот где целиком стек запускается) и думаю, что сильно здесь на общую картинку не влияют.
  2. Мы создаём один сервис (services) logstash, из образа docker.elastic.co/logstash/logstash:6.3.2 и присваиваем ему имя logstash_one_channel.
  3. Mir weider port 5046 am Container, op déi selwecht intern port.
  4. Мы отображаем наш файл настройки каналов ./config/pipelines.yml на файл /usr/share/logstash/config/pipelines.yml внутри контейнера, откуда его подхватит logstash и делаем его read-only, просто на всякий случай.
  5. Мы отображаем директорию ./config/pipelines, где у нас лежат файлы с настройками каналов, в директорию /usr/share/logstash/config/pipelines и тоже делаем её read-only.

Praktesch Uwendung vun ELK. Logstash opbauen

Pipelines.yml Datei

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

Ee Kanal mat dem HABR Identifizéierer an de Wee zu senger Konfiguratiounsdatei ginn hei beschriwwen.

A schliisslech d'Datei "./config/pipelines/habr_pipeline.conf"

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

Loosst eis elo net op seng Beschreiwung goen, loosst eis probéieren et auszeféieren:

docker-compose up

Wat gesi mir?

De Container huet ugefaang. Mir kënnen hir Operatioun kontrolléieren:

echo '13123123123123123123123213123213' | nc localhost 5046

A mir gesinn d'Äntwert an der Containerkonsole:

Praktesch Uwendung vun ELK. Logstash opbauen

Awer gläichzäiteg gesi mir och:

logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Kann net Lizenzinformatioun vum Lizenzserver recuperéieren {: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=>"#<Thread:0x119abb86 run>»}

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 ass op Logstash installéiert awer net op Elasticsearch. Installéiert w.e.g. X-Pack op Elasticsearch fir d'Iwwerwaachungsfunktioun ze benotzen. Aner Funktiounen kënne verfügbar sinn.
logstash_one_channel | [2019-04-29T11:29:00,526][INFO ][logstash.agent ] Logstash API Endpunkt gestart {:port=>9600}
logstash_one_channel | [2019-04-29T11:29:04,478][INFO ][logstash.outputs.elasticsearch] Lafen Gesondheetscheck fir ze kucken ob eng Elasticsearch Verbindung funktionnéiert {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Attempted to resurrect connection to dead ES instance, but got an error. {:url=>«elastesch Sich:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::Resolution Failure] elasticsearch"}
logstash_one_channel | [2019-04-29T11:29:04,704][INFO ][logstash.licensechecker.licensereader] Running health check to see if an Elasticsearch connection is working {:healthcheck_url=>http://elasticsearch:9200/, :path=>"/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Versicht d'Verbindung op dout ES Instanz erëmbeliewen, awer krut e Feeler. {:url=>“elastesch Sich:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::Resolution Failure] elasticsearch"}

An eise Logbicher kräischen déi ganzen Zäit.

Здесь я выделил зелёным цветом сообщение о том, что pipeline успешно запустилась, красным — сообщение об ошибке и жёлтым — сообщение о попытке связаться с elastesch Sich: 9200.
Dëst geschitt well logstash.conf, am Bild abegraff, e Scheck fir elasticsearch Disponibilitéit enthält. No all, iwwerhëlt logstash datt et als Deel vun der Elk Stack Wierker, mä mir getrennt et.

Et ass méiglech ze schaffen, awer et ass net bequem.

D'Léisung ass dës Scheck iwwer d'XPACK_MONITORING_ENABLED Ëmfeldvariabel auszeschalten.

Loosst eis eng Ännerung op docker-compose.yml maachen a lafen et nach eng Kéier:

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

Elo ass alles gutt. De Container ass prett fir Experimenter.

Mir kënnen erëm an der nächster Konsole tippen:

echo '13123123123123123123123213123213' | nc localhost 5046

A kuckt:

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

Schafft an engem Kanal

Итак, мы запустились. Теперь собственно можно уделить время настройке непосредственно logstash. Не будем пока трогать файл pipelines.yml, посмотрим, что можно получить, работая с одним каналом.

Ech muss soen datt den allgemenge Prinzip fir mat der Kanalkonfiguratiounsdatei ze schaffen ass gutt an der offizieller Handbuch beschriwwen, hei hei
Wann Dir op Russesch wëllt liesen, hu mir dëst benotzt Artikel(awer d'Ufro-Syntax do ass al, mir mussen dat berücksichtegen).

Пойдем последовательно от секции Input. Работу по tcp мы уже видели. Что ещё здесь может быть интересного?

Test Messagen mat Häerzschlag

Et gëtt sou eng interessant Geleeënheet fir automatesch Testmeldungen ze generéieren.
Fir dëst ze maachen, musst Dir den Heartbean Plugin an der Input Sektioun aktivéieren.

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

Maacht et un, fänkt un eemol pro Minutt ze kréien

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

Wa mir méi dacks wëlle kréien, musse mir den Intervallparameter derbäisetzen.
Dëst ass wéi mir all 10 Sekonnen e Message kréien.

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

Recuperéieren Daten aus enger Datei

Ещё решили посмотреть режим file. Если нормально с файлом работает, то возможно, и агента никакого не потребуется, ну хотя бы для локального использования.

Laut der Beschreiwung soll de Betribsmodus ähnlech wéi Schwanz -f sinn, d.h. liest nei Zeilen oder, als Optioun, liest de ganze Fichier.

Also wat mir wëllen kréien:

  1. Mir wëllen Zeilen kréien, déi un eng Logdatei bäigefüügt ginn.
  2. Мы хотим получать данные, которые записываются в несколько лог файлов, при этом, иметь возможность разделить что откуда получено.
  3. Mir wëllen sécherstellen datt wann de Logstash nei gestart gëtt, et dës Donnéeën net erëm kritt.
  4. Мы хотим проверить, что если logstash отключить, а данные в файлы продолжают писаться, то, когда мы его запустим, то мы эти данные получим.

Fir den Experiment ze maachen, loosst eis eng aner Zeil op docker-compose.yml derbäisetzen, de Verzeichnis opmaachen an deem mir d'Dateien setzen.

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

A änneren d'Input Rubrik an habr_pipeline.conf

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

Loosst eis ufänken:

docker-compose up

Fir Logbicher ze kreéieren an ze schreiwen benotze mir de Kommando:


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

Jo, et funktionnéiert!

Zur selwechter Zäit gesi mir datt mir automatesch de Weefeld bäigefüügt hunn. Dëst bedeit datt mir an Zukunft records dorop kënnen filteren.

Loosst eis nach eng Kéier probéieren:

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

An elo zu engem anere Fichier:

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

Super! D'Datei gouf opgeholl, de Wee gouf korrekt spezifizéiert, alles ass gutt.

Stop Logstash a fänkt erëm un. Loosst eis waarden. Rou. Déi. Mir kréien dës records net erëm.

An elo dat getraut Experiment.

Installéiert Logstash an ausféiert:

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

Run Logstash erëm a kuckt:

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

Hour! Alles gouf opgeholl.

Mä mir mussen Iech iwwer déi folgend warnen. Wann de Logstash Container geläscht gëtt (docker stop logstash_one_channel && docker rm logstash_one_channel), da gëtt näischt opgeholl. D'Positioun vun der Datei bis zu där se gelies gouf gouf am Container gespäichert. Wann Dir et vun Null leeft, akzeptéiert et nëmmen nei Linnen.

Liesen bestehend Fichieren

Допустим мы первый раз запускаем logstash, но у нас уже есть логи и мы хотели бы их обработать.
Wa mir Logstash mat der Input Sektioun lafen, déi mir uewe benotzt hunn, kréie mir näischt. Nëmmen nei Linnen ginn duerch Logstash veraarbecht.

Для того, чтобы подтянулись строки из существующих файлов, следует добавить в input секцию дополнительную строчку:

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

Причём, есть нюанс, это действует только на новые файлы, которые logstash ещё не видел. Для тех же файлов, что уже попадали в поле зрения logstash, он уже запомнил их размер и теперь будет брать только новые записи в них.

Loosst eis hei ophalen an d'Input Sektioun studéieren. Et ginn nach vill Optiounen, awer dat ass genuch fir eis fir weider Experimenter fir de Moment.

Routing an Daten Transformatioun

Loosst eis probéieren de folgende Problem ze léisen, loosst eis soen datt mir Messagen vun engem Kanal hunn, e puer vun hinnen sinn informativ, an e puer sinn Fehlermeldungen. Si ënnerscheede sech duerch Tag. E puer sinn INFO, anerer sinn ERROR.

Mir mussen se bei der Sortie trennen. Déi. Mir schreiwen Informatiounsmeldungen an engem Kanal, a Fehlermeldungen an engem aneren.

Fir dëst ze maachen, réckelt vun der Input Sektioun op Filter an Output.

Mat der Filtersektioun wäerte mir déi erakommen Noriicht parséieren, en Hash (Schlëssel-Wäertpairen) kréien, mat deem mir scho kënne schaffen, d.h. no Konditiounen disassemble. An an der Ausgangssektioun wäerte mir Messagen auswielen an all eenzel op säin eegene Kanal schécken.

Parsing e Message mat Grok

Fir Text Strings ze analyséieren an e Set vu Felder vun hinnen ze kréien, gëtt et e spezielle Plugin an der Filtersektioun - grok.

Не ставя себе целью дать здесь его детальное описание (за этим отсылаю к offiziell Dokumentatioun), Ech ginn mäi einfacht Beispill.

Fir dëst ze maachen, musst Dir iwwer d'Format vun den Input Saiten entscheeden. Ech hunn se esou:

1 INFO Message1
2 Feelmeldung 2

Déi. Den Identifizéierer kënnt als éischt, dann INFO/Feeler, dann e puer Wuert ouni Raum.
Et ass net schwéier, awer et ass genuch fir de Prinzip vun der Operatioun ze verstoen.

Итак, в секции filter, в плагине grok мы должны определить паттерн для разбора наших строк.

Et wäert esou ausgesinn:

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

Wesentlech ass et e reguläre Ausdrock. Fäerdeg Mustere gi benotzt, wéi INT, LOGLEVEL, WORD. Hir Beschreiwung, wéi och aner Mustere kënnen hei fonnt ginn hei

Elo, duerch dëse Filter passéiert, wäert eis String an en Hash vun dräi Felder verwandelen: message_id, message_type, message_text.

Si ginn an der Ausgangssektioun ugewisen.

Routing Messagen op d'Output Sektioun mat dem Kommando if

В секции output, как мы помним, мы собирались разделить сообщения на два потока. Одни — которые iNFO, будем выводить на консоль, а с ошибками, будем выводить в файл.

Wéi trenne mir dës Messagen? Den Zoustand vum Problem proposéiert schonn eng Léisung - schliisslech hu mir schonn en dedizéierten Message_type Feld, deen nëmmen zwee Wäerter ka huelen: INFO an ERROR. Et ass op dëser Basis datt mir e Choix maache mat der if Ausso.

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

Eng Beschreiwung vun der Aarbecht mat Felder an Opérateuren kann an dëser Rubrik fonnt ginn offiziell Handbuch.

Elo iwwer déi eigentlech Conclusioun selwer.

Konsolausgang, hei ass alles kloer - stdout {}

Awer d'Ausgab op eng Datei - erënnert datt mir all dëst aus engem Container lafen a fir datt d'Datei, an där mir d'Resultat schreiwen, vu baussen zougänglech ass, musse mir dëse Verzeechnes an docker-compose.yml opmaachen.

Total:

D'Output Sektioun vun eiser Datei gesäit esou aus:


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

An docker-compose.yml addéiere mer en anere Volume fir d'Ausgab:

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

Mir starten et, probéieren et a gesinn eng Divisioun an zwee Streamen.

Source: will.com

Setzt e Commentaire