Запустить 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 перемещает индексы между уровнями автоматически.
| Уровень | Оборудование | Что хранить |
|---|---|---|
| Hot | NVMe, много RAM | Активная запись, последние 1-7 дней |
| Warm | SSD | Только чтение, 7-30 дней |
| Cold | HDD, мало RAM, 0 реплик | Редкое чтение, 30-90 дней |
| Frozen | Snapshot на 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 / red | red — немедленно, 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 — интеграция на стороне приложения.