MAATRIX / Блог / Elasticsearch на сервере: частые ошибки и решения

Elasticsearch на сервере: частые ошибки и решения

MAATRIX

Elasticsearch не прощает небрежной настройки: кластер, который спокойно работал на лаптопе разработчика, на боевом сервере то отказывается стартовать, то уходит в yellow или red, то падает под нагрузкой без внятного сообщения в логах. Разберём шесть самых частых причин отказов на сервере — от bootstrap-проверок при первом запуске до нехватки памяти и переполнения диска — с конкретными командами диагностики и рабочими решениями.

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →

Bootstrap check failed: кластер не стартует

Самая частая проблема новичков — Elasticsearch запускается и почти сразу падает с сообщением вида bootstrap check failure и списком нарушенных условий. Это защитный механизм: начиная с 5-й версии, при работе в «production-режиме» (как только узел слушает не только localhost или задан discovery.seed_hosts) Elasticsearch строго проверяет системные лимиты хоста и отказывается стартовать, если они занижены — лучше явная ошибка при старте, чем случайный отказ под нагрузкой.

Чаще всего в списке нарушений встречается max virtual memory areas vm.max_map_count [65530] is too low. Lucene, на котором построен Elasticsearch, активно использует mmap для сегментов индекса, и дефолтного лимита ядра не хватает:

sudo sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' | sudo tee -a /etc/sysctl.conf

Если Elasticsearch запущен в Docker, лимит всё равно применяется на хосте, а не внутри контейнера — команду нужно выполнять на самой VPS/сервере, иначе настройка не подействует и слетит после перезагрузки, если не закрепить её в /etc/sysctl.conf.

Второй частый пункт — превышение лимита на число потоков (max number of threads) или заниженный max file descriptors, о них отдельно ниже. Полный список нарушенных проверок всегда виден в логе перед падением процесса — читайте его целиком, там же указано конкретное значение и требуемый минимум.

Память: heap, OOM killer и circuit breaker

Elasticsearch — JVM-приложение, и большинство проблем со стабильностью на сервере сводятся к неверной настройке кучи (heap). Базовое правило: heap должен занимать не больше 50% RAM сервера, а -Xms и -Xmx в jvm.options (или ES_JAVA_OPTS) должны быть равны — если минимальный и максимальный размер кучи разные, JVM тратит время на её расширение под нагрузкой вместо стабильной работы:

-Xms4g
-Xmx4g

Оставшуюся половину памяти операционная система использует под файловый кеш — именно туда Lucene мапит сегменты индекса через mmap, и без этого запаса поиск начинает упираться в диск даже при формально достаточном heap. Поднимать heap выше примерно 30-31 ГБ обычно нет смысла: после этого порога JVM теряет оптимизацию compressed oops, и на объект тратится больше памяти — для большинства проектов на одной ноде до этого предела просто не доходят.

Если под нагрузкой процесс исчезает без единой строчки в логе Elasticsearch — это почти всегда OOM killer ядра Linux, а не сама JVM. Проверяется так:

sudo dmesg -T | grep -i "killed process"
sudo journalctl -u elasticsearch --since "1 hour ago"

Если видите Out of memory: Killed process ... (java) — значит суммарно heap плюс файловый кеш плюс другие процессы на сервере превысили физическую RAM. Решение — либо снизить heap, либо перенести Elasticsearch на сервер с большим объёмом памяти, а не гнаться за минимальной конфигурацией.

Отдельная категория — ошибки circuit breaker вида [parent] Data too large, data for [<transport_request>] would be [...] which is larger than the limit. Это не сбой, а защита: запрос (обычно тяжёлая агрегация или сортировка по полю с большим объёмом уникальных значений) попытался бы занять больше памяти, чем разрешено. Правильный ответ — не увеличивать лимит breaker вслепую, а разобраться с самим запросом: сузить агрегацию по времени/фильтру, использовать keyword вместо text там, где не нужен полнотекстовый анализ, и избегать fielddata на текстовых полях.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Диск: yellow/red и watermark

Проверка состояния кластера — первое, что стоит делать при любой странности:

curl -s "localhost:9200/_cluster/health?pretty"
curl -s "localhost:9200/_cat/indices?v&s=store.size:desc"
curl -s "localhost:9200/_cat/shards?v" | grep -v STARTED

Статус yellow на одной ноде — норма (реплики шардов физически негде размещать). А вот red означает, что часть первичных шардов недоступна — обычно это либо упавшая нода, либо диск, который уткнулся в watermark.

Elasticsearch следит за свободным местом на диске тремя порогами: cluster.routing.allocation.disk.watermark.low (по умолчанию 85% занятости — новые шарды перестают размещаться на ноде), .watermark.high (90% — шарды начинают перемещаться с ноды), .watermark.flood_stage (95% — индексы на этой ноде принудительно переводятся в режим read-only-allow-delete, запись прекращается). Если приложение внезапно получает ClusterBlockException при индексации — почти всегда это именно flood stage.

Решение по порядку: сначала освободите место (удалите старые индексы, настройте ILM-политику на ротацию), затем снимите блокировку с индексов вручную — сама она не уходит, даже если место появилось:

curl -X PUT "localhost:9200/*/_settings" -H 'Content-Type: application/json' -d '{
  "index.blocks.read_only_allow_delete": null
}'

Если диск действительно упирается в потолок регулярно, а не разово, это сигнал не подкручивать watermark, а взять сервер с большим объёмом NVMe — на VPS/выделенном сервере MAATRIX объём диска можно подобрать под растущие индексы заранее, не мигрируя данные в панике на живом кластере. Подробнее о выборе диска — в статье NVMe против SATA SSD на сервере, а за общим состоянием диска на проде удобно следить через мониторинг диска на сервере.

Discovery и формирование кластера

Ошибка master not discovered yet, this node has not previously joined a bootstrapped cluster при первом старте многонодового кластера почти всегда означает одно из двух: узлы не видят друг друга по сети (порт 9300, transport-протокол, а не 9200) или параметр cluster.initial_master_nodes не задан либо задан неверно на этапе первого поднятия кластера.

Частая ловушка — cluster.initial_master_nodes нужен только для самой первой инициализации кластера. Как только кластер сформировался хотя бы раз, этот параметр можно и нужно убрать из elasticsearch.yml: если оставить его в конфиге и перезапустить ноду позже (например, после добавления новых master-нод), Elasticsearch может попытаться забутстрапить кластер заново и получить конфликт вместо присоединения к существующему.

Проверка состава кластера и связности узлов:

curl -s "localhost:9200/_cat/nodes?v"
curl -s "localhost:9200/_cat/master?v"

Если нода не появляется в списке — проверьте, что порт 9300 открыт между серверами файрволом (не только 9200, который часто открывают для клиентских запросов, забывая про transport-порт для общения нод между собой), и что discovery.seed_hosts в elasticsearch.yml содержит верные адреса или имена хостов остальных нод кластера.

Файловые дескрипторы и лимиты systemd

Ошибка max file descriptors [4096] for elasticsearch process is too low, increase to at least [65535] — ещё одна из bootstrap-проверок, но встречается достаточно часто и отдельно, чтобы разобрать её сама по себе. Elasticsearch держит открытыми множество файлов на каждый сегмент индекса плюс сетевые сокеты, и дефолтный лимит ulimit большинства дистрибутивов (1024-4096) для этого мал.

Если сервис запущен через systemd, лимит правится не в /etc/security/limits.conf (он для интерактивных сессий и systemd его не всегда подхватывает), а через override юнита:

sudo systemctl edit elasticsearch
[Service]
LimitNOFILE=65535
LimitMEMLOCK=infinity
LimitNPROC=4096
sudo systemctl daemon-reload
sudo systemctl restart elasticsearch

Проверить, что лимит реально применился к работающему процессу:

cat /proc/$(pgrep -f elasticsearch)/limits | grep "open files"

Если Elasticsearch запущен в Docker — лимиты задаются секцией ulimits в docker-compose.yml (nofile: soft/hard, memlock: soft/hard: -1), а не на хосте, хотя vm.max_map_count из первого раздела всё равно применяется к хосту, а не к контейнеру.

Производительность: медленная индексация и поиск

Когда кластер стабильно жив, но всё работает медленно — почти всегда виновата схема шардирования или режим индексации, а не «слабый сервер». Частый антипаттерн — избыточное число шардов: много мелких индексов с несколькими шардами каждый суммарно дают тысячи шардов на кластер, и Elasticsearch тратит ресурсы на их обслуживание (метаданные, файловые дескрипторы, накладные расходы на каждый поисковый запрос) больше, чем на полезную работу. При приближении к лимиту cluster.max_shards_per_node появляется предупреждение в логах — это повод пересмотреть схему, а не поднимать лимит.

Практическое правило по размеру: старайтесь держать шард в диапазоне примерно от нескольких ГБ до нескольких десятков ГБ (это ориентир, а не жёсткая цифра — зависит от типа нагрузки), а для индексов с растущими во времени данными (логи, метрики, события) использовать ILM с автоматическим rollover вместо ручного управления числом шардов на глаз.

При массовой первоначальной загрузке данных (bulk-импорт миллионов документов) индексация ускоряется, если временно снять часть накладных расходов:

curl -X PUT "localhost:9200/my-index/_settings" -H 'Content-Type: application/json' -d '{
  "index.refresh_interval": "-1",
  "index.number_of_replicas": 0
}'

После загрузки верните значения обратно (refresh_interval на 1s, реплики на нужное число) — иначе документы не будут появляться в поиске сразу после индексации, а данные останутся без резервных копий на других нодах. Для точечных медленных запросов используйте Profile API (GET /my-index/_search с "profile": true), чтобы увидеть, какая часть запроса — сама выборка или агрегация — съедает время, вместо того чтобы гадать по общему времени ответа.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Elasticsearch не стартует и сразу падает — с чего начать?

Смотрите лог целиком, не только последнюю строку — там перечислены все нарушенные bootstrap checks. Чаще всего это vm.max_map_count, лимит файловых дескрипторов или число потоков, и все три чинятся системными настройками хоста, а не конфигом Elasticsearch.

Кластер жёлтый (yellow) — это проблема?

На одной ноде — нет, это нормальное состояние: реплики шардов просто некуда положить. Проблема — статус red, когда недоступны первичные шарды.

Почему индексация вдруг остановилась с ошибкой блокировки?

Скорее всего, сработал flood stage watermark по диску — индексы автоматически перевелись в read-only. Освободите место и явно снимите блокировку index.blocks.read_only_allow_delete, сама она не пропадает.

Сколько памяти реально нужно под Elasticsearch на проде?

Зависит от объёма данных, но отправная точка — heap в 50% RAM, оставшаяся половина под файловый кеш ОС. Для небольшого прод-кластера редко хватает меньше 8 ГБ RAM на ноду, для серьёзной нагрузки — заметно больше.

Нужно ли держать cluster.initial_master_nodes в конфиге постоянно?

Нет, только на этапе первого бутстрапа кластера. После того как кластер сформировался, уберите параметр из elasticsearch.yml, иначе при следующем перезапуске возможен конфликт инициализации.

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →