
Kontynuuję swoją opowieść o tym, jak zintegrować Exchange z ELK (część pierwsza ). Przypominam, że ta kombinacja potrafi bez problemu przetwarzać bardzo dużą ilość logów. Tym razem porozmawiamy o tym, jak skonfigurować współpracę Exchange z komponentami Logstash i Kibana.
Logstash w stosie ELK służy do inteligentnego przetwarzania logów i ich przygotowywania do umieszczenia w Elastic w postaci dokumentów, na podstawie których wygodnie buduje się różne wizualizacje w Kibana.
Instalacja
Składa się z dwóch etapów:
- Instalacja i konfiguracja pakietu OpenJDK.
- Instalacja i konfiguracja pakietu Logstash.
Instalacja i konfiguracja pakietu OpenJDK
Pakiet OpenJDK należy pobrać i rozpakować do określonego katalogu. Następnie ścieżkę do tego katalogu należy dodać do zmiennych $env:Path oraz $env:JAVA_HOME systemu operacyjnego Windows:


Sprawdźmy wersję Java:
PS C:> java -version
openjdk version "13.0.1" 2019-10-15
OpenJDK Runtime Environment (build 13.0.1+9)
OpenJDK 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)
Instalacja i konfiguracja pakietu Logstash
Pobierz archiwum z dystrybucją Logstash . Archiwum należy rozpakować na korzeń dysku. Nie warto rozpakowywać go do folderu C:Program Files , ponieważ Logstash nie uruchomi się poprawnie. Następnie należy wprowadzić zmiany w pliku jvm.options , dotyczące przydzielania pamięci RAM dla procesu Java. Zalecam ustawienie połowy pamięci RAM serwera. Jeśli ma on 16 GB pamięci, to domyślne klucze będą następujące:
-Xms1g
-Xmx1g
, które należy zastąpić na:
-Xms8g
-Xmx8g
Ponadto sensowne jest zakomentowanie linii -XX:+UseConcMarkSweepGC. Więcej informacji na ten temat . Następnym krokiem jest stworzenie domyślnej konfiguracji w pliku logstash.conf:
input {
stdin{}
}
filter {
}
output {
stdout {
codec => "rubydebug"
}
}
Przy użyciu tej konfiguracji Logstash odczytuje dane z konsoli, przetwarza przez pusty filtr i wyprowadza z powrotem do konsoli. Zastosowanie tej konfiguracji pozwoli na sprawdzenie działania Logstash. Aby to zrobić, uruchomimy go w trybie interaktywnym:
PS C:...bin> .logstash.bat -f .logstash.conf
...
[2019-12-19T11:15:27,769][INFO ][logstash.javapipeline ][main] Pipeline started {"pipeline.id"=>"main"}
Wtyczka stdin czeka teraz na dane wejściowe:
[2019-12-19T11:15:27,847][INFO ][logstash.agent ] Pipelines running {:count=>1, :running_pipelines=>[:main], :non_running_pipelines=>[]}
[2019-12-19T11:15:28,113][INFO ][logstash.agent ] Z powodzeniem uruchomiono punkt końcowy API Logstash {:port=>9600}
Logstash pomyślnie uruchomił się na porcie 9600.
Ostatni krok instalacji: uruchomienie Logstash jako usługi Windows. Można to zrobić na przykład za pomocą pakietu :
PS C:...bin> .nssm.exe install logstash
Usługa "logstash" została pomyślnie zainstalowana!
Odporność na awarie
Zachowanie logów podczas przesyłania z serwera źródłowego zapewnia mechanizm Persistent Queues.
Jak to działa
Schemat rozmieszczenia kolejek w procesie przetwarzania logów: input → queue → filter + output.
Plugin input odbiera dane z źródła logów, zapisuje je w kolejce i wysyła potwierdzenie otrzymania danych do źródła.
Wiadomości z kolejki są przetwarzane przez Logstash, przechodzą przez filtr i plugin output. Po otrzymaniu od output potwierdzenia wysłania loga, Logstash usuwa przetworzony log z kolejki. Jeśli Logstash zostanie zatrzymany, wszystkie nieprzetworzone wiadomości i wiadomości, dla których nie otrzymano potwierdzenia wysyłki, pozostają w kolejce, a Logstash wznowi ich przetwarzanie przy następnym uruchomieniu.
Konfiguracja
Regulowane kluczami w pliku C:Logstashconfiglogstash.yml:
queue.type: (możliwe wartości —persistedimemory (domyślnie)).path.queue: (ścieżka do folderu z plikami kolejek, które domyślnie przechowywane są w C:Logstashqueue).queue.page_capacity: (maksymalny rozmiar strony kolejki, wartość domyślna — 64mb).queue.drain: (true/false — włącza/wyłącza zatrzymywanie przetwarzania kolejki przed wyłączeniem Logstash. Nie polecam włączać, ponieważ bezpośrednio wpłynie to na szybkość wyłączenia serwera).queue.max_events: (maksymalna liczba wydarzeń w kolejce, domyślnie — 0 (bez ograniczeń)).queue.max_bytes: (maksymalny rozmiar kolejki w bajtach, domyślnie — 1024mb (1gb)).
Jeśli ustawione są queue.max_events i queue.max_bytes, to wiadomości przestają być przyjmowane do kolejki po osiągnięciu wartości któregokolwiek z tych ustawień. Więcej o Persistent Queues opisano .
Przykład części logstash.yml, odpowiedzialnej za konfigurację kolejki:
queue.type: persisted
queue.max_bytes: 10gb
Konfiguracja
Konfiguracja Logstash zwykle składa się z trzech części, odpowiadających za różne etapy przetwarzania przychodzących logów: odbiór (sekcja input), parsowanie (sekcja filter) i wysyłanie do Elastic (sekcja output). Poniżej szczegółowo omówimy każdą z nich.
Input
Otrzymujemy przychodzący strumień surowych logów od agentów filebeat. To właśnie ten plugin wskazujemy w sekcji input:
input {
beats {
port => 5044
}
}
Po takiej konfiguracji Logstash zaczyna nasłuchiwać na porcie 5044 i po otrzymaniu logów przetwarza je zgodnie z ustawieniami sekcji filter. W razie potrzeby można kanał otrzymywania logów od filebit zaszyfrować w SSL. Więcej o ustawieniach pluginu beats opisano .
Filter
Wszystkie interesujące do przetwarzania dzienniki tekstowe generowane przez Exchange mają format csv z polami opisanymi w samych plikach dziennika. Do analizowania wpisów csv Logstash oferuje nam trzy wtyczki: , csv i grok. Pierwsza z nich jest najszybsza, Na przykład, poniższy wpis zostanie rozdzielony na dwa (z powodu występowania przecinka wewnątrz pola), przez co dziennik zostanie zanalizowany niepoprawnie:
…,"MDB:GUID1, Mailbox:GUID2, Event:526545791, MessageClass:IPM.Note, CreationTime:2020-05-15T12:01:56.457Z, ClientType:MOMT, SubmissionAssistant:MailboxTransportSubmissionEmailAssistant",…
Można go używać przy analizowaniu dzienników, na przykład, IIS. W tym przypadku sekcja filter może wyglądać następująco:
filter { if "IIS" in [tags] { dissect { mapping => { "message" => "%{date} %{time} %{s-ip} %{cs-method} %{cs-uri-stem} %{cs-uri-query} %{s-port} %{cs-username} %{c-ip} %{cs(User-Agent)} %{cs(Referer)} %{sc-status} %{sc-substatus} %{sc-win32-status} %{time-taken}" } remove_field => ["message"] add_field => { "application" => "exchange" } } } }
Konfiguracja Logstash pozwala na użycie
instrukcji warunkowych, IIS . Wewnątrz wtyczki mapujemy wartości pól z ich nazwami, usuwamy pole źródłowe,które zawierało zapis z dziennika, i możemy dodać dowolne pole, które na przykład będzie zawierało nazwę aplikacji, z której zbieramy logi. messageW przypadku logów śledzenia lepiej użyć wtyczki csv, która potrafi prawidłowo obsługiwać złożone pola:
filter { if "Tracking" in [tags] { csv { columns => ["date-time","client-ip","client-hostname","server-ip","server-hostname","source-context","connector-id","source","event-id","internal-message-id","message-id","network-message-id","recipient-address","recipient-status","total-bytes","recipient-count","related-recipient-address","reference","message-subject","sender-address","return-path","message-info","directionality","tenant-id","original-client-ip","original-server-ip","custom-data","transport-traffic-type","log-id","schema-version"] remove_field => ["message", "tenant-id", "schema-version"] add_field => { "application" => "exchange" } } }
Wewnątrz wtyczki mapujemy wartości pól z ich nazwami, usuwamy pole źródłowe
(a także pola message tenant-id schema-version i ), które zawierało zapis z dziennika, i możemy dodać dowolne pole, które na przykład będzie zawierało nazwę aplikacji, z której zbieramy logi.Na wyjściu z etapu filtrowania otrzymamy dokumenty wstępnie gotowe do wizualizacji w Kibana. Będzie nam brakować następującego:
На выходе из стадии фильтрации мы получим документы в первом приближении готовые к визуализации в Kibana. Не хватать нам будет следующего:
- Pola numeryczne będą rozpoznawane jako tekst, co uniemożliwia wykonywanie operacji na nich. A mianowicie, pola
time-takenlogu IIS, a także polarecipient-countitotal-biteslogu Tracking. - Standardowy znacznik czasowy dokumentu będzie zawierał czas przetwarzania logu, a nie czas zapisu po stronie serwera.
- Pole
recipient-addressbędzie wyglądać jako jedna linia, co uniemożliwia przeprowadzenie analizy z liczeniem odbiorców wiadomości.
Nadszedł czas, aby dodać trochę magii do procesu przetwarzania logów.
Konwersja pól numerycznych
Wtyczka dissect ma opcję convert_datatype, którą można wykorzystać do konwersji pola tekstowego na format liczbowy. Na przykład tak:
dissect {
…
convert_datatype => { "time-taken" => "int" }
…
}
Należy pamiętać, że ta metoda pasuje tylko wtedy, gdy pole na pewno będzie zawierać ciąg. Wartości Null z pól nie są obsługiwane przez opcję i powodują wyjątek.
W przypadku logów śledzenia lepiej nie używać podobnej metody konwersji, ponieważ pola recipient-count i total-bites mogą być puste. Do konwersji tych pól lepiej użyć wtyczki :
mutate {
convert => [ "total-bytes", "integer" ]
convert => [ "recipient-count", "integer" ]
}
Podział recipient_address na poszczególnych odbiorców
Tę sprawę można również rozwiązać za pomocą wtyczki mutate:
mutate {
split => ["recipient_address", ";"]
}
Zmiana znacznika czasu
W przypadku logów śledzenia zadanie to bardzo łatwo rozwiązuje wtyczka , która pomoże wpisać w pole timestamp datę i czas w odpowiednim formacie z pola date-time:
date {
match => [ "date-time", "ISO8601" ]
timezone => "Europe/Moscow"
remove_field => [ "date-time" ]
}
W przypadku logów IIS będziemy musieli połączyć dane pól date i time za pomocą wtyczki mutate, ustawić odpowiednią strefę czasową i umieścić ten znacznik czasu w timestamp za pomocą wtyczki date:
mutate {
add_field => { "data-time" => "%{date} %{time}" }
remove_field => [ "date", "time" ]
}
date {
match => [ "data-time", "YYYY-MM-dd HH:mm:ss" ]
timezone => "UTC"
remove_field => [ "data-time" ]
}
Output
Sekcja output służy do wysyłania przetworzonych logów do odbiornika logów. W przypadku bezpośredniego wysyłania do Elastic używana jest wtyczka , w której podaje się adres serwera i wzór nazwy indeksu do wysyłania utworzonego dokumentu:
output {
elasticsearch {
hosts => ["127.0.0.1:9200", "127.0.0.2:9200"]
manage_template => false
index => "Exchange-%{+YYYY.MM.dd}"
}
}
Ostateczna konfiguracja
Ostateczna konfiguracja będzie wyglądać następująco:
input {
beats {
port => 5044
}
}
filter {
if "IIS" in [tags] {
dissect {
mapping => {
"message" => "%{date} %{time} %{s-ip} %{cs-method} %{cs-uri-stem} %{cs-uri-query} %{s-port} %{cs-username} %{c-ip} %{cs(User-Agent)} %{cs(Referer)} %{sc-status} %{sc-substatus} %{sc-win32-status} %{time-taken}"
}
remove_field => ["message"]
add_field => { "application" => "exchange" }
convert_datatype => { "time-taken" => "int" }
}
mutate {
add_field => { "data-time" => "%{date} %{time}" }
remove_field => [ "date", "time" ]
}
date {
match => [ "data-time", "YYYY-MM-dd HH:mm:ss" ]
timezone => "UTC"
remove_field => [ "data-time" ]
}
}
if "Tracking" in [tags] {
csv {
columns => ["date-time","client-ip","client-hostname","server-ip","server-hostname","source-context","connector-id","source","event-id","internal-message-id","message-id","network-message-id","recipient-address","recipient-status","total-bytes","recipient-count","related-recipient-address","reference","message-subject","sender-address","return-path","message-info","directionality","tenant-id","original-client-ip","original-server-ip","custom-data","transport-traffic-type","log-id","schema-version"]
remove_field => ["message", "tenant-id", "schema-version"]
add_field => { "application" => "exchange" }
}
mutate {
convert => [ "total-bytes", "integer" ]
convert => [ "recipient-count", "integer" ]
split => ["recipient_address", ";"]
}
date {
match => [ "date-time", "ISO8601" ]
timezone => "Europe/Moscow"
remove_field => [ "date-time" ]
}
}
}
output {
elasticsearch {
hosts => ["127.0.0.1:9200", "127.0.0.2:9200"]
manage_template => false
index => "Exchange-%{+YYYY.MM.dd}"
}
}
Przydatne linki:
Źródło: habr.com
