← назад к разделу

Запустить Elasticsearch несложно. Сложнее — сделать так, чтобы он не утонул в собственных данных через месяц. Эта статья о том, как управлять кластером в реальных условиях: контролировать рост индексов, делать резервные копии, правильно выбрать железо и знать, когда что-то идёт не так.

Проблема растущих индексов

Представьте, что вы пишете логи в Elasticsearch. Поначалу всё хорошо: поиск быстрый, места хватает. Через полгода диск заполнен, кластер тормозит, логи трёхлетней давности занимают место, но никто их уже не читает.

Решение — Index Lifecycle Management (ILM). Вы описываете политику: как долго индекс живёт на быстром железе, когда переезжает на медленное и дешёвое, когда удаляется. Elasticsearch выполняет её автоматически.

Политика описывает четыре фазы жизни индекса:

[HOT]     — активная запись и частое чтение, быстрые диски (NVMe)
   │  rollover: 50 GB или 7 дней
   ▼
[WARM]    — только чтение, SSD
   │  через 30 дней
   ▼
[COLD]    — редкое чтение, обычные HDD, меньше реплик
   │  через 90 дней
   ▼
[DELETE]  — удалить

Есть и пятая фаза — Frozen: данные хранятся как snapshot на S3, доступны для поиска, но читаются в 10-100 раз медленнее. Подходит для аудита или соответствия требованиям регулятора.

Как создать политику

PUT /_ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_size": "50gb", "max_age": "7d" }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "forcemerge": { "max_num_segments": 1 },
          "shrink": { "number_of_shards": 1 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": { "number_of_replicas": 0 }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}

ILM работает с rollover-индексами: приложение пишет в один alias (logs), а Elasticsearch сам создаёт logs-000001, logs-000002 при достижении порога. Для этого нужны шаблон и стартовый индекс:

PUT /_index_template/logs-template
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "index.lifecycle.name": "logs-policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}

PUT /logs-000001
{
  "aliases": {
    "logs": { "is_write_index": true }
  }
}

Приложение пишет в logs, всё остальное — автоматически.

Резервные копии через snapshots

В Elasticsearch нет аналога pg_dump. Вместо этого — snapshot: копия данных в удалённом хранилище (S3, GCS, Azure Blob или NFS).

Snapshot инкрементальный: первый раз копируется всё, дальше — только новые данные. Под капотом Elasticsearch копирует неизменяемые файлы сегментов Lucene, которых ещё нет в репозитории.

Шаг 1: зарегистрировать репозиторий

PUT /_snapshot/s3-backup
{
  "type": "s3",
  "settings": {
    "bucket": "my-es-backups",
    "region": "eu-west-1",
    "compress": true,
    "base_path": "es-cluster-1"
  }
}

S3-плагин входит в дистрибутив Elasticsearch с версии 7.12. На узлах нужна IAM-роль с доступом к bucket.

Шаг 2: создать snapshot

PUT /_snapshot/s3-backup/snapshot-2026-06-27?wait_for_completion=false
{
  "indices": "products,orders,logs-*",
  "include_global_state": false
}

Шаг 3: восстановить

POST /_snapshot/s3-backup/snapshot-2026-06-27/_restore
{
  "indices": "products",
  "rename_pattern": "products",
  "rename_replacement": "products-restored",
  "include_global_state": false
}

Восстановить поверх существующего индекса нельзя — поэтому используем переименование. После восстановления переключаем alias.

Автоматизация через SLM

Чтобы не создавать snapshots вручную, есть Snapshot Lifecycle Management (SLM):

PUT /_slm/policy/daily-snapshots
{
  "schedule": "0 30 1 * * ?",
  "name": "<daily-snap-{now/d}>",
  "repository": "s3-backup",
  "config": {
    "indices": ["products", "orders"],
    "include_global_state": false
  },
  "retention": {
    "expire_after": "30d",
    "min_count": 5,
    "max_count": 50
  }
}

Snapshot каждый день в 01:30, хранится 30 дней, минимум 5 штук, максимум 50.

Многоуровневое хранилище: hot / warm / cold / frozen

В крупных кластерах узлы делят на роли: горячие данные — на быстром дорогом железе, старые — на дешёвом. ILM перемещает индексы между уровнями автоматически.

УровеньОборудованиеЧто хранить
HotNVMe, много RAMАктивная запись, последние 1-7 дней
WarmSSDТолько чтение, 7-30 дней
ColdHDD, мало RAM, 0 репликРедкое чтение, 30-90 дней
FrozenSnapshot на S3, диск как кэшАудит, очень редкое чтение

Роль узла задаётся в elasticsearch.yml:

node.roles: [data_hot, data_content]
# или
node.roles: [data_warm]

Для небольших кластеров (до 10 узлов, до 10 TB) многоуровневость не нужна — усложняет без пользы. Она актуальна при объёмах от 10-20 TB или 50+ узлов.

Force merge: зачем и когда

Каждый шард Elasticsearch состоит из нескольких сегментов — файлов на диске. Новые данные пишутся в новые сегменты. Если их накопилось сто — на каждый запрос ES читает сто файлов, это медленнее.

После того как индекс перестаёт получать новые записи (прошёл rollover), его можно «сжать» в один сегмент:

POST /logs-000001/_forcemerge?max_num_segments=1

Эффект: чтение ускоряется на 10-30%, метаданные занимают меньше памяти.

Важно: force merge — тяжёлая операция, она нагружает диск и CPU, может занять часы. Не запускайте её на активно пишущем индексе. ILM делает force merge автоматически в warm-фазе в нужный момент.

Как выбрать размер кластера

Heap JVM

Устанавливают в половину от объёма RAM узла. Жёсткий лимит — 31 GB: выше этого Java переключается на другой режим адресации, и выигрыш от большого heap пропадает. Если данных больше — лучше взять два узла по 31 GB heap, чем один с 64 GB.

Идеальный узел: 64 GB RAM (32 GB heap + 32 GB под кэш файловой системы для Lucene).

Шарды на узел

Ориентир: не более 600-800 шардов на узел при 30 GB heap. Каждый шард — накладные расходы на метаданные. Типичная ошибка при первом развёртывании: создать тысячи индексов с пятью шардами каждый и получить 20 000 шардов на 10 узлов.

Проверить просто: если кластер тормозит, но данных мало — скорее всего, слишком много шардов.

Размер одного шарда

Оптимальный диапазон: 10-50 GB. Меньше — лишние накладные расходы, больше — медленный поиск и долгие операции слияния.

Для индекса 1 TB нужно 20-100 первичных шардов. С двумя репликами — 60-300 шардов итого.

Скорость записи

Один узел обрабатывает примерно 5-20 тысяч документов в секунду (зависит от размера документа и настроек). Для 100K документов в секунду нужно 5-20 узлов.

Совет по настройке: по умолчанию refresh_interval=1s, что создаёт много мелких сегментов при высокой нагрузке на запись. Если записей много и свежесть данных не критична, можно поднять до 30s — это даёт в 2-3 раза больше пропускной способности.

Мониторинг кластера

Prometheus exporter

Стандартный инструмент — elasticsearch_exporter. Запускается контейнером рядом с Elasticsearch, опрашивает _nodes/stats и отдаёт метрики в формате Prometheus.

Ключевые метрики

МетрикаЧто означаетКогда тревога
elasticsearch_cluster_health_statusСтатус кластера: green / yellow / redred — немедленно, yellow — расследовать
elasticsearch_jvm_memory_used_bytes / max_bytesИспользование heapУстойчиво > 85%
elasticsearch_jvm_gc_collection_seconds_countЧастота сборки мусораOld GC чаще 1 раза в минуту
elasticsearch_indices_indexing_index_time_secondsВремя индексацииРастёт — значит, нарастает нагрузка
elasticsearch_indices_search_query_time_secondsВремя поискаРастёт — проблемы с запросами или mapping
elasticsearch_thread_pool_rejected_countОтклонённые задачиЛюбое значение > 0
elasticsearch_filesystem_data_available_bytesСвободное место на дискеМеньше 15%

Пороги заполнения диска

Elasticsearch автоматически реагирует на заполнение диска:

  • 85% — перестаёт размещать новые шарды на этом узле.
  • 90% — начинает переносить шарды на другие узлы.
  • 95% (flood stage) — все индексы на этом узле переводятся в режим только для чтения. Запись останавливается.

Flood stage — аварийный режим, из которого нужно выходить вручную: расширить диск или удалить данные, затем снять флаг read_only_allow_delete.

Частые ошибки

Слишком много шардов. Один индекс на 100 GB лучше, чем 100 индексов по 1 GB. Чем меньше шардов — тем меньше накладных расходов.

Динамические поля без ограничений. Если писать JSON с тысячами разных ключей (attr_color, attr_size, attr_brand_...), Elasticsearch создаст отдельный тип для каждого. На миллионе уникальных полей — переполнение памяти. Решение: dynamic: false в mapping и тип flattened для произвольных атрибутов.

Крупные aggregations. terms aggregation с size: 10000 на миллиардах документов может убить узел. Вместо этого используйте composite aggregation с постраничной загрузкой.

Отключённый _source. Можно сэкономить место, убрав _source из индекса. Но тогда невозможно обновить документ или переиндексировать — только полное воссоздание из источника. Подходит только для логов и метрик, где исходные данные хранятся где-то ещё.

Коротко

  • ILM описывает политику жизни индекса: hot → warm → cold → delete. Для логов, метрик и событий — обязателен.
  • Rollover создаёт новый индекс при достижении размера или возраста; приложение всегда пишет в один alias.
  • Snapshots — единственный способ резервного копирования; они инкрементальны и хранятся в S3 / GCS / Azure.
  • SLM автоматизирует создание и удаление snapshots по расписанию.
  • Hot/warm/cold нужен от ~10 TB данных; в небольших кластерах — лишнее.
  • Heap — не больше 31 GB; идеальный узел — 64 GB RAM. Шардов — не больше 600-800 на узел.
  • Оптимальный размер шарда: 10-50 GB.
  • Flood stage на 95% диска останавливает запись — следите за свободным местом заранее.

Что почитать дальше

  • Основы Elasticsearch — как устроен кластер, шарды и реплики.
  • Query DSL и релевантность — как писать эффективные запросы.
  • Клиентский код Elasticsearch — интеграция на стороне приложения.