Elasticsearch хранит документы в инвертированном индексе и умеет одновременно искать по тексту и считать агрегаты. Для логов это удобно: одна система отвечает и на вопрос «что случилось в 03:12», и на вопрос «сколько ошибок было у каждого сервиса за сутки». Ниже — установка, шаблон индекса, приём документов, запросы, ILM и диагностика.
Когда нужен свой кластер
Небольшой объём логов можно хранить в файлах и разбирать утилитами командной строки. Elasticsearch оправдан, когда нужен полнотекстовый поиск, агрегаты по полям, общий интерфейс для команды и хранение за несколько недель. Он требователен к памяти и диску, поэтому на одном сервере с базой данных его размещают с осторожностью.
- поиск по сообщению, имени хоста, уровню и идентификатору запроса;
- агрегаты по времени, сервисам и кодам ответа;
- единые правила хранения и удаления старых индексов.
Для кластера из одного узла подходит режим single-node. Он упрощает запуск, но не даёт отказоустойчивости: при потере диска данные придётся восстанавливать из снимка.
Установка и базовая защита
Пакет из репозитория Elastic создаёт службу, каталоги /etc/elasticsearch и /var/lib/elasticsearch. Безопасность включена по умолчанию: TLS, пароль elastic и сертификат для клиентов. Не отключайте её ради удобства.
sudo systemctl status elasticsearch
sudo journalctl -u elasticsearch -n 80 --no-pager
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic https://localhost:9200
Для одноузлового стенда в elasticsearch.yml задают discovery.type: single-node, а размер кучи — в отдельном файле jvm.options.d/heap.options. Правило простое: не больше половины памяти сервера, а для больших объёмов учитывайте порог сжатых указателей.
Шаблон индекса и сопоставления
Типы полей важнее самого анализатора. Время должно быть date, адрес — ip, уровень и имя сервиса — keyword, а сообщение — text с отдельным подполем keyword для точных агрегатов. Если положить всё как text, группировка станет дорогой.
PUT _index_template/logs-app
{
"index_patterns": ["logs-app-*"],
"data_stream": {},
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"refresh_interval": "5s",
"index.lifecycle.name": "logs-app-policy"
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"message": { "type": "text" },
"service": { "type": "keyword" },
"log.level": { "type": "keyword" },
"host.name": { "type": "keyword" },
"host.ip": { "type": "ip" }
}
}
}
}
Шаблон с data_stream создаёт поток logs-app-default и сам управляет скрытыми индексами. Запись идёт через _bulk или агент доставки.
Приём документов
В продакшене логи отправляет Filebeat, Fluent Bit или Logstash. Они читают файлы, добавляют метаданные и буферизуют отправку при недоступности кластера. Для проверки формата удобно отправить несколько документов вручную.
POST logs-app-default/_bulk
{"create":{}}
{"@timestamp":"2026-11-06T10:15:00Z","message":"timeout while calling billing","service":"api","log":{"level":"error"},"host":{"name":"web-01","ip":"10.0.0.5"}}
{"create":{}}
{"@timestamp":"2026-11-06T10:16:00Z","message":"request completed","service":"api","log":{"level":"info"},"host":{"name":"web-01","ip":"10.0.0.5"}}
Слишком крупный _bulk создаёт нагрузку и дольше повторяется при ошибке. Начните с нескольких мегабайт и настройте backoff в агенте.
Поиск и агрегаты
Запрос ниже ищет ошибки за сутки и одновременно строит две группировки: по часам и по сервисам. size: 0 отключает возврат самих документов, потому что нужны только счётчики. Фильтры в bool.filter не влияют на оценку релевантности и кэшируются эффективнее.
GET logs-app-*/_search
{
"size": 0,
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-24h" } } },
{ "term": { "log.level": "error" } }
]
}
},
"aggs": {
"per_hour": { "date_histogram": { "field": "@timestamp", "fixed_interval": "1h" } },
"top_services": { "terms": { "field": "service", "size": 10 } }
}
}
Для текста используют match, для точного значения — term. Шаблон *error* плохо масштабируется: лучше добавить отдельное поле.
Жизненный цикл данных
Политика ILM переводит старые данные в менее дорогие фазы и удаляет их по сроку. Для одного узла фаза warm может быть условной, но rollover и delete нужны обязательно, иначе диск закончится внезапно.
PUT _ilm/policy/logs-app-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": { "max_primary_shard_size": "30gb", "max_age": "7d" },
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "7d",
"actions": { "forcemerge": { "max_num_segments": 1 }, "set_priority": { "priority": 50 } }
},
"delete": { "min_age": "30d", "actions": { "delete": {} } }
}
}
}
Срок хранения выбирайте по политике организации. Если в логах есть персональные данные, ограничьте доступ и срок хранения. Политику создают до первого индекса.
Снимки и восстановление
Снимки делают в отдельный каталог, подключённый через path.repo. Хранить их на том же диске, что и индексы, бессмысленно: при отказе диска пропадёт и копия. Репозиторий регистрируют один раз, затем создают снимок по расписанию.
PUT _snapshot/backup
{ "type": "fs", "settings": { "location": "/mnt/backup/elasticsearch", "compress": true } }
PUT _snapshot/backup/snap-2026-11-06?wait_for_completion=true
GET _snapshot/backup/_all
Восстановление проверяют на отдельном кластере. Перед крупным обновлением читайте матрицу совместимости и делайте копию заранее.
Диагностика
Здоровье кластера, нераспределённые шарды и заполнение диска — три главных индикатора. Жёлтый статус означает, что реплики не размещены, красный — что недоступны первичные шарды. Причина чаще всего в watermark, нехватке места или ошибке выделения памяти.
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic "https://localhost:9200/_cluster/health?pretty"
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic "https://localhost:9200/_cat/shards?v&s=state"
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic "https://localhost:9200/_cat/indices?v&s=store.size:desc"
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic "https://localhost:9200/_nodes/stats/fs,jvm?pretty"
При отказах пула потоков проверяют число одновременных запросов и размер кучи. Помогает снижение числа шардов и ограничение тяжёлых агрегатов.
Чек-лист эксплуатации
- Порт 9200 не опубликован в интернет, доступ только через защищённую сеть.
- Включены TLS, пароли и отдельные роли для приложений.
- Настроены шаблон, ILM и ротация индексов.
- Снимки лежат на другом носителе и проверены восстановлением.
- Контролируются диск, heap, отказы пулов и статус кластера.
- Срок хранения логов соответствует политике организации.
Итог
Elasticsearch превращает разрозненные логи в управляемый источник данных, но требует дисциплины в сопоставлениях, хранении и безопасности. Начните с одного узла, одного шаблона и одной политики ILM, проверьте приём и поиск на реальных событиях, затем добавляйте агенты и панели. Такой порядок не даёт кластеру разрастись раньше, чем появится понимание нагрузки.