Konfiguracja Elasticsearch

Zabbix może przechowywać dane historyczne w Elasticsearch jako alternatywę dla relacyjnej bazy danych.

Niniejszy przewodnik obejmuje konfigurację obsługiwanych wersji Elasticsearch. serwer Zabbix nie uruchomi się, jeśli używana wersja Elasticsearch jest starsza niż minimalna obsługiwana wersja. Jeśli jest nowsza niż maksymalna obsługiwana wersja, serwer Zabbix uruchomi się tylko wtedy, gdy parametr AllowUnsupportedDBVersions jest ustawiony na 1.

Elasticsearch może przechowywać następujące typy wartości:

Typ wartości pozycji Tabela bazy danych Typ Elasticsearch
Liczbowa (bez znaku) history_uint uint
Liczbowa (zmiennoprzecinkowa) history dbl
Znakowa history_str str
Dziennik history_log log
Tekstowa history_text text
Binarna history_bin nieobsługiwany przez Zabbix
JSON history_json json

Ważne uwagi

  • Elasticsearch wymaga libcurl. Szczegółowe informacje można znaleźć w sekcji wymagania.
  • Housekeeper nie usuwa danych z Elasticsearch. Aby określić, jak długo dane mają być przechowywane, zobacz Index Lifecycle Management.
  • Zabbix nie oblicza ani nie przechowuje trendów w Elasticsearch. Rozważ wydłużenie okresu przechowywania historii, aby zachować starsze dane.
  • Gdy używany jest Elasticsearch, zapytania zakresowe pobierające wartości z bazy danych są ograniczone znacznikiem czasu okresu przechowywania danych.
  • Elasticsearch nie jest obsługiwany przez proxy Zabbix.
  • Począwszy od Elasticsearch 8.0, funkcje bezpieczeństwa są domyślnie włączone: Elasticsearch wymaga uwierzytelniania i używa protokołu HTTPS dla swojego interfejsu API HTTP, z certyfikatami podpisanymi przez automatycznie wygenerowany urząd certyfikacji. Przykłady na tej stronie używają adresu http://localhost:9200 bez uwierzytelniania. Jeśli zabezpieczenia są włączone, użyj adresu URL https:// oraz przekaż certyfikat CA i dane uwierzytelniające w żądaniach (na przykład sudo curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic https://localhost:9200; plik certyfikatu CA może być odczytywany tylko przez użytkownika root i grupę elasticsearch). Aby połączyć serwer Zabbix i frontend Zabbix z takim klastrem, zobacz sekcję Łączenie z zabezpieczonym klastrem.

Jeśli Elasticsearch nie jest jeszcze zainstalowany, przed kontynuowaniem zapoznaj się z oficjalnym przewodnikiem instalacji.

Konfigurowanie Elasticsearch

Aby przechowywać dane historii w Elasticsearch, należy:

  • Utworzyć indeks dla każdego typu wartości, który chcesz przechowywać — to tutaj Elasticsearch zapisuje dane, podobnie jak tabela w relacyjnej bazie danych.
  • Zdefiniować mapowanie dla każdego indeksu — określa ono strukturę danych, podobnie jak schemat tabeli.
  • Skonfigurować pipeline przetwarzania, aby przetwarzać wartości przed zapisaniem (wymagane dla wartości JSON i indeksów opartych na dacie).

Elasticsearch może przechowywać dane w jednym indeksie dla każdego typu wartości lub w wielu indeksach opartych na dacie. Oba podejścia opisano poniżej.

Przechowywanie historii w jednym indeksie

W tym podejściu wszystkie dane historii dla danego typu wartości są zapisywane w jednym indeksie (np. uint lub text).

Aby utworzyć indeks dla typu wartości Liczbowe (bez znaku), wyślij następujące żądanie (z /uint w adresie URL) do instancji Elasticsearch:

curl -X PUT \
 http://localhost:9200/uint \
 -H 'content-type:application/json' \
 -d '{
   "settings": {
      "index": {
         "number_of_replicas": 1,
         "number_of_shards": 5
      }
   },
   "mappings": {
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "format": "epoch_second", "type": "date" },
         "value": { "type": "long" }
      }
   }
}'

Elasticsearch odpowie potwierdzeniem utworzenia indeksu:

{"acknowledged": true, "shards_acknowledged": true, "index": "uint"}

Podobne żądania należy wysłać dla każdego typu wartości, który chcesz przechowywać w Elasticsearch, czyli dla każdego typu wymienionego w opcji value_types w konfiguracji serwera Zabbix.

Mapowania dla wszystkich typów wartości są dostępne w repozytorium źródłowym Zabbix.

Na przykład, aby utworzyć indeks dla typu wartości Tekst:

curl -X PUT \
 http://localhost:9200/text \
 -H 'content-type:application/json' \
 -d '{
   "settings": {
      "index": {
         "number_of_replicas": 1,
         "number_of_shards": 5
      }
   },
   "mappings": {
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "format": "epoch_second", "type": "date" },
         "value": {
            "fields": {
               "analyzed": { "index": true, "type": "text", "analyzer": "standard" }
            },
            "index": false,
            "type": "text"
         }
      }
   }
}'
Typ wartości JSON

W przeciwieństwie do innych typów wartości, wartości JSON wymagają dodatkowego przetwarzania przed zapisaniem. Indeks dla typu wartości JSON przechowuje każdą wartość w jednym z dwóch pól:

  • value_parsed — obiekty JSON przechowywane jako pole flattened, dzięki czemu można je przeszukiwać według klucza i wartości;
  • value_raw — wszystkie pozostałe wartości JSON (tablice, ciągi znaków, liczby, wartości logiczne i null) przechowywane jako ciąg znaków.

Potok ingest analizuje każdą wartość jako JSON i zapisuje ją w odpowiednim polu. Wartości, które nie są prawidłowym kodem JSON, nie są wysyłane do Elasticsearch, ponieważ pozycje, które je otrzymują, stają się nieobsługiwane.

Najpierw utwórz potok ingest:

curl -X PUT \
 http://localhost:9200/_ingest/pipeline/json \
 -H 'content-type:application/json' \
 -d '{
   "processors": [
      {
         "json": {
            "field": "value",
            "target_field": "value_parsed",
            "ignore_failure": true
         }
      },
      {
         "script": {
            "source": "if (ctx.value_parsed == null || !(ctx.value_parsed instanceof Map)) { ctx.value_raw = ctx.value; ctx.remove(\"value_parsed\"); }"
         }
      }
   ]
}'

Elasticsearch odpowie potwierdzeniem utworzenia potoku ingest:

{"acknowledged": true}

Następnie utwórz indeks dla typu wartości JSON, wysyłając poniższe żądanie (z /json w adresie URL) do swojej instancji Elasticsearch. Ustawienie default_pipeline powoduje, że Elasticsearch przetwarza każdą wartość zapisywaną w tym indeksie za pomocą potoku ingest json. To ustawienie jest wymagane, ponieważ serwer Zabbix nie określa potoku ingest podczas przechowywania historii w jednym indeksie.

curl -X PUT \
 http://localhost:9200/json \
 -H 'content-type:application/json' \
 -d '{
   "settings": {
      "index": {
         "number_of_replicas": 1,
         "number_of_shards": 5,
         "default_pipeline": "json"
      }
   },
   "mappings": {
      "dynamic": false,
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "type": "date", "format": "epoch_second" },
         "ns": { "type": "long" },
         "value_parsed": { "type": "flattened", "ignore_above": 8191 },
         "value_raw": { "type": "keyword", "ignore_above": 8191 }
      }
   }
}'

Elasticsearch nie może indeksować wartości keyword i flattened dłuższych niż 32766 bajtów. Przy użyciu "ignore_above": 8191 ciągi znaków dłuższe niż 8191 znaków nie są indeksowane (i nie można ich przeszukiwać), ale nadal są przechowywane i zwracane do Zabbix w całości. Bez tego limitu Elasticsearch odrzuca takie wartości, a one zostają utracone.

Przechowywanie historii w indeksach opartych na dacie

Zamiast zapisywać wszystkie dane historyczne do jednego indeksu (np. uint), Elasticsearch może rozdzielać te dane między wiele indeksów opartych na dacie (np. uint-2026-01-01, uint-2026-01-02). Ułatwia to zarządzanie wolumenem danych oraz ich retencją w czasie.

Aby to włączyć, należy:

  • Utworzyć szablon indeksu dla każdego typu wartości, który chcesz przechowywać — informuje to Elasticsearch, jakie mapowanie zastosować podczas automatycznego tworzenia nowego indeksu opartego na dacie.
  • Utworzyć pipeline ingest dla każdego typu wartości — przetwarza on każdą przychodzącą wartość i kieruje ją do właściwego indeksu opartego na dacie.
Szablony indeksów

Aby utworzyć szablon dla indeksu text, wyślij żądanie zawierające następujące elementy:

  • Użyj _index_template/text_template w adresie URL instancji Elasticsearch.
  • Użyj "text-*" w polu "index_patterns", aby dopasować nazwy indeksów opartych na dacie.
  • W polu "template" określ ustawienia indeksu oraz mapowanie dla typu wartości text (zobacz mapowania w repozytorium źródłowym Zabbix).
curl -X PUT \
 http://localhost:9200/_index_template/text_template \
 -H 'content-type:application/json' \
 -d '{
   "index_patterns": [ "text-*" ],
   "template": {
      "settings": {
         "index": {
            "number_of_replicas": 1,
            "number_of_shards": 5
         }
      },
      "mappings": {
         "properties": {
            "itemid": { "type": "long" },
            "clock": { "format": "epoch_second", "type": "date" },
            "value": {
               "fields": {
                  "analyzed": { "index": true, "type": "text", "analyzer": "standard" }
               },
               "index": false,
               "type": "text"
            }
         }
      }
   }
}'

Utwórz szablon dla każdego typu wartości, który chcesz przechowywać, zmieniając odpowiednio nazwę szablonu, wzorzec indeksu i mapowanie.

Na przykład szablon dla indeksu json (szczegółowe informacje o polach value_parsed i value_raw znajdziesz w sekcji Typ wartości JSON):

curl -X PUT \
 http://localhost:9200/_index_template/json_template \
 -H 'content-type:application/json' \
 -d '{
   "index_patterns": [ "json-*" ],
   "template": {
      "settings": {
         "number_of_replicas": 1
      },
      "mappings": {
         "dynamic": false,
         "properties": {
            "itemid": { "type": "long" },
            "clock": { "type": "date", "format": "epoch_second" },
            "ns": { "type": "long" },
            "value_parsed": { "type": "flattened", "ignore_above": 8191 },
            "value_raw": { "type": "keyword", "ignore_above": 8191 }
         }
      }
   }
}'

Używaj złożonych szablonów indeksów (_index_template), jak pokazano powyżej. Starsze szablony indeksów (_template) są przestarzałe, a Elasticsearch odrzuca starsze szablony, których wzorce indeksów pokrywają się z wbudowanymi szablonami (na przykład starszy szablon ze wzorcem log* dla indeksu log).

Potoki ingest

Aby utworzyć potok ingest dla indeksu text:

  • Użyj _ingest/pipeline/text-pipeline w adresie URL swojej instancji Elasticsearch.
  • Uwzględnij procesor date_index_name, aby kierować każdą wartość do właściwego indeksu opartego na dacie, na podstawie jej znacznika czasu.
curl -X PUT \
 http://localhost:9200/_ingest/pipeline/text-pipeline \
 -H 'content-type:application/json' \
 -d '{
   "description": "daily text index naming",
   "processors": [
      {
         "date_index_name": {
            "field": "clock",
            "date_formats": ["UNIX"],
            "index_name_prefix": "text-",
            "date_rounding": "d"
         }
      }
   ]
}'

Utwórz potok dla każdego typu wartości, który chcesz przechowywać, odpowiednio zmieniając nazwę potoku, opis oraz wartość "index_name_prefix". Nazwa potoku musi składać się z nazwy indeksu i przyrostka -pipeline (na przykład uint-pipeline), ponieważ serwer Zabbix używa tej nazwy podczas wysyłania wartości do Elasticsearch.

W przypadku indeksu json potok musi również przeanalizować wartość JSON przed skierowaniem jej do właściwego indeksu:

curl -X PUT \
 http://localhost:9200/_ingest/pipeline/json-pipeline \
 -H 'content-type:application/json' \
 -d '{
   "description": "daily json index naming",
   "processors": [
      {
         "json": {
            "field": "value",
            "target_field": "value_parsed",
            "ignore_failure": true
         }
      },
      {
         "script": {
            "source": "if (ctx.value_parsed == null || !(ctx.value_parsed instanceof Map)) { ctx.value_raw = ctx.value; ctx.remove(\"value_parsed\"); }"
         }
      },
      {
         "date_index_name": {
            "field": "clock",
            "date_formats": [ "UNIX" ],
            "index_name_prefix": "json-",
            "date_rounding": "d"
         }
      }
   ]
}'

Konfigurowanie serwera Zabbix

W pliku konfiguracyjnym serwera Zabbix (zabbix_server.conf) ustaw parametr HistoryProvider.

Na przykład, aby przechowywać wartości typu Character, Log, Text i JSON w Elasticsearch (z zachowaniem wartości typu Numeric w bazie danych):

HistoryProvider=elasticsearch;value_types="str,log,text,json",url=http://localhost:9200

Jeśli używasz indeksów opartych na dacie, dodaj date_index=1 do parametru:

HistoryProvider=elasticsearch;value_types="str,log,text,json",url=http://localhost:9200,date_index=1

Jeśli funkcje zabezpieczeń Elasticsearch są włączone, określ adres URL https:// wraz z danymi uwierzytelniającymi (zobacz Łączenie z zabezpieczonym klastrem).

Po wprowadzeniu zmian uruchom ponownie serwer Zabbix:

systemctl restart zabbix-server

Konfigurowanie frontend Zabbix

W pliku konfiguracyjnym frontend Zabbix (zabbix.conf.php) ustaw zmienną $HISTORY_PROVIDERS zgodnie z konfiguracją serwera:

$HISTORY_PROVIDERS[] = [
  'types' => ['str','log','text','json'],
  'provider' => 'elasticsearch',
  'url' => 'http://localhost:9200'
];

Jeśli funkcje zabezpieczeń Elasticsearch są włączone, określ ten sam adres URL https:// wraz z danymi uwierzytelniającymi, co w konfiguracji serwera (zobacz Łączenie z zabezpieczonym klastrem).

Łączenie z zabezpieczonym klastrem

Jeśli funkcje zabezpieczeń Elasticsearch są włączone (domyślnie od Elasticsearch 8.0), serwer Zabbix i frontend Zabbix muszą ufać certyfikatowi CA warstwy HTTP Elasticsearch oraz uwierzytelniać się za pomocą nazwy użytkownika i hasła.

  1. Utwórz rolę i użytkownika dla Zabbix. Poniższa rola umożliwia Zabbix pobieranie wersji Elasticsearch (uprawnienie klastra monitor), tworzenie indeksów, zapisywanie i odczytywanie danych historii oraz usuwanie danych historii po wyczyszczeniu historii pozycji:
sudo curl -X PUT \
 --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
 https://localhost:9200/_security/role/zabbix_history \
 -H 'content-type:application/json' \
 -d '{
   "cluster": [ "monitor" ],
   "indices": [
      {
         "names": [ "uint", "uint-*", "dbl", "dbl-*", "str", "str-*", "log", "log-*", "text", "text-*", "json", "json-*" ],
         "privileges": [ "create_index", "index", "read", "delete" ]
      }
   ]
}'
sudo curl -X PUT \
 --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
 https://localhost:9200/_security/user/zabbix \
 -H 'content-type:application/json' \
 -d '{
   "password": "<password>",
   "roles": [ "zabbix_history" ]
}'
  1. Dodaj certyfikat CA Elasticsearch do zaufanych certyfikatów systemu operacyjnego na hostach serwera Zabbix i frontendu Zabbix. Jeśli Zabbix działa na innym hoście niż Elasticsearch, najpierw skopiuj certyfikat CA (/etc/elasticsearch/certs/http_ca.crt) na ten host. Na przykład w systemach Debian/Ubuntu:
sudo cp /etc/elasticsearch/certs/http_ca.crt /usr/local/share/ca-certificates/elasticsearch-http-ca.crt
sudo update-ca-certificates

Nazwa hosta w adresie URL musi odpowiadać certyfikatowi HTTP Elasticsearch. Automatycznie wygenerowany certyfikat jest ważny dla localhost, nazwy hosta oraz adresów IP hosta Elasticsearch.

  1. Określ dane uwierzytelniające w adresie URL, zarówno w pliku konfiguracyjnym serwera Zabbix, jak i w pliku konfiguracyjnym frontendu. Jeśli hasło zawiera znaki specjalne, zakoduj je procentowo (na przykład @ jako %40, : jako %3A, a , jako %2C).
HistoryProvider=elasticsearch;value_types="str,log,text,json",url=https://zabbix:<password>@localhost:9200
$HISTORY_PROVIDERS[] = [
  'types' => ['str','log','text','json'],
  'provider' => 'elasticsearch',
  'url' => 'https://zabbix:<password>@localhost:9200'
];
  1. Uruchom ponownie serwer Zabbix i PHP-FPM (lub serwer WWW, jeśli PHP działa jako moduł serwera WWW).

Rozwiązywanie problemów

Poniższe kroki mogą pomóc w rozwiązywaniu problemów z konfiguracją Elasticsearch:

  1. Sprawdź dzienniki Elasticsearch lub serwera Zabbix pod kątem błędów. Na przykład poniższe komunikaty w dzienniku serwera Zabbix wskazują problemy z połączeniem z zabezpieczonym klastrem (zobacz Łączenie z zabezpieczonym klastrem):
  • cannot query Elasticsearch: SSL certificate problem: self-signed certificate in certificate chain—certyfikat CA Elasticsearch nie jest zaufany;
  • cannot query Elasticsearch, HTTP error message: The requested URL returned error: 401—brakuje danych uwierzytelniających lub są one nieprawidłowe;
  • cannot query Elasticsearch, HTTP error message: The requested URL returned error: 403—użytkownik nie ma wymaganych uprawnień.

frontend Zabbix wyświetla podobne błędy (na przykład Elasticsearch error: missing authentication credentials for REST request).

  1. Aby zidentyfikować powolne zapytania, ustaw parametr konfiguracji serwera Zabbix LogSlowQueries. Jeśli odczyt historii z Elasticsearch trwa dłużej niż określony czas, serwer Zabbix zapisuje w dzienniku slow query: <N> sec, "<query>". Podczas oczekiwania na odpowiedź Elasticsearch serwer Zabbix również zapisuje w dzienniku waiting for Elasticsearch response <N>sec za każdym razem, gdy upłynie określony czas, lub co 10 sekund, jeśli LogSlowQueries ma wartość 0.

  2. Sprawdź, czy Elasticsearch zezwala na dostęp z serwera Zabbix i frontendu Zabbix.

  3. Sprawdź, czy opcja auto_create_index jest włączona (domyślnie jest włączona):

curl -X GET \
 "http://localhost:9200/_cluster/settings?include_defaults=true&filter_path=**.auto_create_index"

# {"persistent": {"action": {"auto_create_index": "false"} } }

Aby ją włączyć, wyślij następujące żądanie:

curl -X PUT \
 http://localhost:9200/_cluster/settings \
 -H 'content-type:application/json' \
 -d '{
   "persistent": {
      "action.auto_create_index": "true"
   }
}'

# {"acknowledged": true, "persistent": {"action": {"auto_create_index": "true"} }, "transient": {} }
  1. Sprawdź, czy mapowania, szablony i potoki ingest są prawidłowe, wysyłając żądania GET do odpowiednich adresów URL:
curl -X GET "http://localhost:9200/json*"
curl -X GET "http://localhost:9200/_index_template/json*"
curl -X GET "http://localhost:9200/_ingest/pipeline/json*"

Otrzymane odpowiedzi możesz porównać z oczekiwanymi odpowiedziami w dokumentacji API Elasticsearch.

  1. Sprawdź, czy którykolwiek fragment danych nie znajduje się w stanie awarii; ponowne uruchomienie Elasticsearch może rozwiązać ten problem. W klastrze składającym się z jednego węzła indeksy z replikami ("number_of_replicas": 1) mają status yellow, ponieważ replik nie można przydzielić do tego samego węzła co podstawowe fragmenty danych; nie ma to wpływu na Zabbix. Aby tego uniknąć, ustaw "number_of_replicas" na 0.

  2. Wykonaj zapytanie do Elasticsearch, aby sprawdzić, czy dane zebrane przez Zabbix są przechowywane, na przykład:

curl "http://localhost:9200/json*/_search" \
 -H 'Content-Type: application/json' \
 -d '{
   "query": {
     "term": {
       "itemid": 42269
     }
   }
 }'
  1. Jeśli musisz zresetować konfigurację Elasticsearch i rozpocząć od nowa, usuń indeksy, szablony indeksów i potoki ingest utworzone dla Zabbix (spowoduje to usunięcie wszystkich danych historii przechowywanych w Elasticsearch). Domyślnie Elasticsearch nie zezwala na usuwanie indeksów za pomocą wyrażeń wieloznacznych, dlatego aby usunąć indeksy oparte na dacie, tymczasowo zmień ustawienie action.destructive_requires_name. Szablony indeksów i potoki ingest należy usuwać pojedynczo; żądania dotyczące nieistniejących szablonów lub potoków zwracają błąd, który można zignorować.
curl -X DELETE "http://localhost:9200/uint,dbl,str,log,text,json?ignore_unavailable=true"

curl -X PUT \
 http://localhost:9200/_cluster/settings \
 -H 'content-type:application/json' \
 -d '{"persistent": {"action.destructive_requires_name": false}}'
curl -X DELETE "http://localhost:9200/uint-*,dbl-*,str-*,log-*,text-*,json-*"
curl -X PUT \
 http://localhost:9200/_cluster/settings \
 -H 'content-type:application/json' \
 -d '{"persistent": {"action.destructive_requires_name": null}}'

for type in uint dbl str log text json; do
  curl -X DELETE "http://localhost:9200/_index_template/${type}_template"
  curl -X DELETE "http://localhost:9200/_ingest/pipeline/${type}-pipeline"
done
curl -X DELETE "http://localhost:9200/_ingest/pipeline/json"