Настройка Elasticsearch

Zabbix может хранить данные истории в Elasticsearch в качестве альтернативы реляционной базе данных.

В этом руководстве описана настройка поддерживаемых версий Elasticsearch. Сервер Zabbix не запускается, если ваша версия Elasticsearch старше минимальной поддерживаемой версии. Если она новее максимальной поддерживаемой версии, сервер Zabbix запускается только в том случае, если параметр AllowUnsupportedDBVersions установлен в значение 1.

Elasticsearch может хранить следующие типы значений:

Тип значения элемента данных Таблица базы данных Тип Elasticsearch
Числовой (беззнаковый) history_uint uint
Числовой (с плавающей запятой) history dbl
Символьный history_str str
Журнал history_log log
Текстовый history_text text
Двоичный history_bin не поддерживается Zabbix
JSON history_json json

Важные примечания

  • Для Elasticsearch требуется libcurl. Подробнее см. в разделе требования.
  • Сборщик мусора не удаляет данные из Elasticsearch. Чтобы управлять сроком хранения данных, см. раздел управление жизненным циклом индексов.
  • Zabbix не вычисляет и не хранит тренды в Elasticsearch. Чтобы сохранить более старые данные, рассмотрите возможность увеличения периода хранения истории.
  • При использовании Elasticsearch диапазонные запросы, извлекающие значения из базы данных, ограничены временной меткой периода хранения данных.
  • Elasticsearch не поддерживается для прокси Zabbix.
  • Начиная с Elasticsearch 8.0 функции безопасности включены по умолчанию: Elasticsearch требует аутентификацию и использует HTTPS для своего HTTP API, а сертификаты подписываются автоматически сгенерированным центром сертификации. В примерах на этой странице используется http://localhost:9200 без аутентификации. Если безопасность включена, используйте URL https:// и передавайте в запросах сертификат центра сертификации и учетные данные (например, sudo curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic https://localhost:9200; файл сертификата центра сертификации может быть прочитан только пользователем root и группой elasticsearch). Чтобы подключить сервер Zabbix и веб-интерфейс Zabbix к такому кластеру, см. раздел Подключение к защищенному кластеру.

Если Elasticsearch еще не установлен, перед продолжением ознакомьтесь с официальным руководством по установке.

Настройка Elasticsearch

Чтобы хранить исторические данные в Elasticsearch, необходимо:

  • Создать индекс для каждого типа значений, который вы хотите хранить — именно здесь Elasticsearch хранит данные, аналогично таблице в реляционной базе данных.
  • Определить mapping для каждого индекса — он задаёт структуру данных, аналогично схеме таблицы.
  • Настроить ingest pipeline для обработки значений перед сохранением (требуется для значений JSON и индексов на основе даты).

Elasticsearch может хранить данные в одном индексе для каждого типа значений или в нескольких индексах на основе даты. Оба подхода описаны ниже.

Хранение истории в одном индексе

В этом подходе все данные истории для заданного типа значения записываются в один индекс (например, uint или text).

Чтобы создать индекс для типа значения Числовой (без знака), отправьте следующий запрос (с /uint в URL) в свой экземпляр 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 ответит подтверждением создания индекса:

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

Аналогичные запросы необходимо отправить для каждого типа значения, который вы хотите хранить в Elasticsearch, то есть для каждого типа, указанного в параметре value_types конфигурации сервера Zabbix.

Сопоставления для всех типов значений доступны в репозитории исходного кода Zabbix.

Например, чтобы создать индекс для типа значения Текст:

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"
         }
      }
   }
}'
Тип значения JSON

В отличие от других типов значений, значения JSON требуют дополнительной обработки перед сохранением. Индекс для типа значения JSON хранит каждое значение в одном из двух полей:

  • value_parsed — объекты JSON, хранящиеся как поле flattened, чтобы их можно было искать по ключу и значению;
  • value_raw — все остальные значения JSON (массивы, строки, числа, логические значения и null), хранящиеся в виде строки.

Конвейер ingest анализирует каждое значение как JSON и сохраняет его в соответствующем поле. Значения, не являющиеся допустимым JSON, не отправляются в Elasticsearch, поскольку элементы данных, которые их получают, становятся неподдерживаемыми.

Сначала создайте конвейер 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 ответит подтверждением создания конвейера ingest:

{"acknowledged": true}

Затем создайте индекс для типа значения JSON, отправив следующий запрос к вашему экземпляру Elasticsearch (обратите внимание на /json в URL). Параметр default_pipeline заставляет Elasticsearch обрабатывать каждое значение, записываемое в этот индекс, с помощью конвейера ingest json. Этот параметр необходим, поскольку сервер Zabbix не указывает конвейер ingest при сохранении истории в одном индексе.

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 не может индексировать значения keyword и flattened длиной более 32766 байт. При использовании "ignore_above": 8191 строки длиной более 8191 символа не индексируются (и не могут использоваться для поиска), но по-прежнему сохраняются и возвращаются узлу сети Zabbix полностью. Без этого ограничения Elasticsearch отклоняет такие значения, и они теряются.

Хранение истории в индексах на основе даты

Вместо записи всех данных истории в один индекс (например, uint) Elasticsearch может распределять эти данные по нескольким индексам на основе даты (например, uint-2026-01-01, uint-2026-01-02). Это упрощает управление объемом данных и сроками хранения с течением времени.

Чтобы включить это, необходимо:

  • Создать шаблон индекса для каждого типа значения, который вы хотите хранить — это сообщает Elasticsearch, какое сопоставление применять при автоматическом создании нового индекса на основе даты.
  • Создать конвейер ingest для каждого типа значения — он обрабатывает каждое входящее значение и направляет его в правильный индекс на основе даты.
Шаблоны индексов

Чтобы создать шаблон для индекса text, отправьте запрос со следующими параметрами:

  • Используйте _index_template/text_template в URL вашего экземпляра Elasticsearch.
  • Используйте "text-*" в поле "index_patterns", чтобы сопоставить имена индексов на основе дат.
  • В поле "template" укажите настройки индекса и сопоставление для типа значения text (см. сопоставления в репозитории исходного кода 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"
            }
         }
      }
   }
}'

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

Например, шаблон для индекса json (подробные сведения о полях value_parsed и value_raw см. в разделе Тип значения 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 }
         }
      }
   }
}'

Используйте составные шаблоны индексов (_index_template), как показано выше. Устаревшие шаблоны индексов (_template) не рекомендуются к использованию, и Elasticsearch отклоняет устаревшие шаблоны, шаблоны индексов которых пересекаются со встроенными шаблонами Elasticsearch (например, устаревший шаблон с шаблоном log* для индекса log).

Конвейеры ingest

Чтобы создать конвейер ingest для индекса text:

  • Используйте _ingest/pipeline/text-pipeline в URL-адресе вашего экземпляра Elasticsearch.
  • Добавьте процессор date_index_name, чтобы направлять каждое значение в правильный индекс на основе даты, соответствующей его метке времени.
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"
         }
      }
   ]
}'

Создайте конвейер для каждого типа значения, которое вы хотите хранить, изменив имя конвейера, описание и значение "index_name_prefix" соответствующим образом. Имя конвейера должно состоять из имени индекса и суффикса -pipeline (например, uint-pipeline), поскольку сервер Zabbix использует это имя при отправке значений в Elasticsearch.

Для индекса json конвейер также должен анализировать значение JSON перед его направлением в правильный индекс:

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"
         }
      }
   ]
}'

Настройка сервера Zabbix

В файле конфигурации сервера Zabbix (zabbix_server.conf) задайте параметр HistoryProvider.

Например, чтобы хранить значения типов Character, Log, Text и JSON в Elasticsearch, а значения типа Numeric — в базе данных:

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

Если используются индексы на основе даты, добавьте к параметру date_index=1:

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

Если включены функции безопасности Elasticsearch, укажите URL https:// с учетными данными (см. раздел Подключение к защищенному кластеру).

После внесения изменений перезапустите сервер Zabbix:

systemctl restart zabbix-server

Настройка веб-интерфейса Zabbix

В файле конфигурации веб-интерфейса Zabbix (zabbix.conf.php) задайте переменную $HISTORY_PROVIDERS в соответствии с конфигурацией сервера:

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

Если функции безопасности Elasticsearch включены, укажите тот же URL https:// с учетными данными, что и в конфигурации сервера (см. раздел Подключение к защищенному кластеру).

Подключение к защищенному кластеру

Если функции безопасности Elasticsearch включены (по умолчанию начиная с Elasticsearch 8.0), сервер Zabbix и веб-интерфейс Zabbix должны доверять сертификату CA HTTP-уровня Elasticsearch и проходить аутентификацию с использованием имени пользователя и пароля.

  1. Создайте роль и пользователя для Zabbix. Следующая роль позволяет Zabbix получать версию Elasticsearch (привилегия кластера monitor), создавать индексы, записывать и читать данные истории, а также удалять данные истории при очистке истории элемента данных:
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. Добавьте сертификат CA Elasticsearch в список доверенных сертификатов операционной системы на узлах сети сервера Zabbix и веб-интерфейса Zabbix. Если Zabbix работает на другом узле сети, отличном от Elasticsearch, сначала скопируйте сертификат CA (/etc/elasticsearch/certs/http_ca.crt) на этот узел сети. Например, в Debian/Ubuntu:
sudo cp /etc/elasticsearch/certs/http_ca.crt /usr/local/share/ca-certificates/elasticsearch-http-ca.crt
sudo update-ca-certificates

Имя узла сети в URL должно совпадать с именем в HTTP-сертификате Elasticsearch. Автоматически созданный сертификат действителен для localhost, имени узла сети и IP-адресов узла сети Elasticsearch.

  1. Укажите учетные данные в URL как в файле конфигурации сервера Zabbix, так и в файле конфигурации веб-интерфейса. Если пароль содержит специальные символы, закодируйте их в формате percent-encoding (например, @ как %40, : как %3A, а , как %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. Перезапустите сервер Zabbix и PHP-FPM (или веб-сервер, если PHP работает как модуль веб-сервера).

Устранение неполадок

Следующие действия могут помочь устранить проблемы с настройкой Elasticsearch:

  1. Проверьте журналы Elasticsearch или сервера Zabbix на наличие ошибок. Например, следующие сообщения в журнале сервера Zabbix указывают на проблемы с подключением к защищенному кластеру (см. раздел Подключение к защищенному кластеру):
  • cannot query Elasticsearch: SSL certificate problem: self-signed certificate in certificate chain — сертификат CA Elasticsearch не является доверенным;
  • cannot query Elasticsearch, HTTP error message: The requested URL returned error: 401 — учетные данные отсутствуют или указаны неправильно;
  • cannot query Elasticsearch, HTTP error message: The requested URL returned error: 403 — у пользователя нет необходимых привилегий.

В веб-интерфейсе Zabbix отображаются аналогичные ошибки (например, Elasticsearch error: missing authentication credentials for REST request).

  1. Чтобы выявить медленные запросы, задайте параметр конфигурации сервера Zabbix LogSlowQueries. Если чтение истории из Elasticsearch занимает больше указанного времени, сервер Zabbix записывает в журнал сообщение slow query: <N> sec, "<query>". Во время ожидания ответа Elasticsearch сервер Zabbix также записывает сообщение waiting for Elasticsearch response <N>sec каждый раз по истечении указанного времени или каждые 10 секунд, если для LogSlowQueries задано значение 0.

  2. Убедитесь, что Elasticsearch разрешает доступ с сервера Zabbix и из веб-интерфейса Zabbix.

  3. Убедитесь, что параметр auto_create_index включен (по умолчанию он включен):

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

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

Чтобы включить его, отправьте следующий запрос:

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. Проверьте правильность mappings, шаблонов и ingest pipelines, отправив запросы GET к соответствующим 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*"

Полученные ответы можно сравнить с ожидаемыми ответами в документации по API Elasticsearch.

  1. Проверьте, не находятся ли какие-либо шарды в состоянии сбоя; перезапуск Elasticsearch может решить эту проблему. В одновузловом кластере индексы с репликами ("number_of_replicas": 1) имеют желтый статус, поскольку реплики нельзя разместить на том же узле, что и первичные шарды; это не влияет на Zabbix. Чтобы избежать этого, задайте для "number_of_replicas" значение 0.

  2. Выполните запрос к Elasticsearch, чтобы проверить, сохранены ли собранные Zabbix данные, например:

curl "http://localhost:9200/json*/_search" \
 -H 'Content-Type: application/json' \
 -d '{
   "query": {
     "term": {
       "itemid": 42269
     }
   }
 }'
  1. Если необходимо сбросить настройку Elasticsearch и начать заново, удалите индексы, шаблоны индексов и ingest pipelines, созданные для Zabbix (при этом удаляются все данные истории, хранящиеся в Elasticsearch). По умолчанию Elasticsearch не разрешает удалять индексы с помощью выражений с подстановочными знаками, поэтому для удаления индексов на основе дат временно измените параметр action.destructive_requires_name. Шаблоны индексов и ingest pipelines необходимо удалять по одному; запросы к несуществующим шаблонам или pipelines возвращают ошибку, которую можно игнорировать.
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"