ELK Stack — сбор и анализ логов
ELK — это связка Elasticsearch + Logstash + Kibana (плюс легковесные сборщики Beats). Стек собирает логи со всех серверов и приложений в одно место, индексирует их и позволяет искать по ним за секунды.
1. Компоненты стека
| Компонент | Роль |
|---|---|
| Elasticsearch | хранение, индексация и поиск |
| Logstash | приём, преобразование и доставка данных |
| Kibana | визуализация и интерфейс |
| Filebeat | лёгкий сборщик логов с серверов (shipper) |
Схема: Filebeat → Logstash → Elasticsearch → Kibana.
2. Установка Elasticsearch
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update && sudo apt install elasticsearch
sudo systemctl enable --now elasticsearchПроверка
curl http://localhost:92003. Индексы и документы
Данные хранятся в индексах (аналог коллекций/таблиц) и документах (JSON).
# создать документ
curl -X PUT "localhost:9200/products/_doc/1" -H 'Content-Type: application/json' -d '{
"name": "Чашка",
"price": 990
}'
# получить
curl "localhost:9200/products/_doc/1"4. Logstash — пайплайн
Пайплайн Logstash состоит из трёх зон: input, filter, output.
# /etc/logstash/conf.d/logs.conf
input {
beats { port => 5044 }
}
filter {
if [log][file][path] =~ "error" {
mutate { add_field => { "severity" => "error" } }
}
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:msg}" }
}
date {
match => [ "timestamp", "ISO8601" ]
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "app-logs-%{+YYYY.MM.dd}"
}
}sudo systemctl enable --now logstashgrok — распаковка неструктурного текста в поля по шаблонам. Это самый мощный инструмент Logstash.5. Filebeat — сборщик файлов
sudo apt install filebeatКонфигурация /etc/filebeat/filebeat.yml:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/*.log
- /opt/app/logs/*.log
output.logstash:
hosts: ["logstash:5044"]sudo systemctl enable --now filebeat6. Kibana — интерфейс
sudo apt install kibana
sudo systemctl enable --now kibana
# http://localhost:5601Настройка: перейти в Stack Management → Data Views и создать view для индексов app-logs-* с временным полем по выбору.
7. Поиск в Kibana (Discover)
// простой поиск
error AND status:500
// по полю
level: WARN OR level: ERROR
// диапазон
response_time > 1000 AND service: paymentПолнотекстовый запрос Elasticsearch
GET app-logs-2026.09.04/_search
{
"query": {
"bool": {
"must": [ { "match": { "message": "exception" } } ],
"filter": [ { "term": { "level": "ERROR" } } ]
}
}
}8. Визуализация
В Kibana создаются визуализации и комбинируются в дашборды:
- Lens — построение графиков мышью без DSL;
- Timelion / TSVB — сложные временные ряды;
- Maps — география событий;
- Discover — таблица логов с фильтрами.
9. Жизненный цикл индексов (ILM)
Политики ILM автоматически переносят старые индексы в более холодное хранение:
hot => 7 дней, искать часто
warm => 30 дней
delete => после 90 дней10. Мониторинг стека
curl "localhost:9200/_cluster/health"
curl "localhost:9200/_cat/indices?v"red значит, что часть данных недоступна, yellow — реплики не разложены. Исправляй, а не игнорируй.11. Альтернативы лёгкого стека
# вместо Logstash часто ставят Filebeat напрямую в Elasticsearch:
filebeat --> elasticsearch --> kibana # проще, меньше ресурсовДля «супер-лёгкого» сценария подойдёт стек Loki + Grafana из урока Grafana.
12. Grok: разбираем логи в поля
Grok-фильтр превращает неструктурированную строку лога в типизированные поля:
filter {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:ts} %{LOGLEVEL:level} %{DATA:msg}"
}
}
}# вход: 2026-09-19T10:00:01 ERROR DB timeout
# поля: ts, level="ERROR", msg="DB timeout"| Паттерн | Что ловит | Пример |
|---|---|---|
| %{IP:client} | IP-адрес | 192.168.1.10 |
| %{NUMBER:code:int} | число | 404 |
| %{UUID:req_id} | UUID | a1b2-… |
13. Multiline и обогащение: mutate
Стек-трейсы и многострочные сообщения падают в одной записи только с фильтром multiline:
input {
beats { port => 5044 }
}
filter {
multiline {
pattern => "^\\s"
negate => false
what => "previous"
}
mutate {
add_field => { "app" => "billing" }
rename => { "message" => "raw" }
}
date {
match => [ "timestamp", "ISO8601" ]
target => "@timestamp"
}
}- multiline — склейка повторяющихся «хвостов» по паттерну;
- mutate — добавление, переименование, удаление полей;
- date — единый @timestamp для сортировки и индексов.
max_lines и таймауты защищают от зависаний воркеров.14. Alerting и оповещения
Elastic Alerting следит за правилами и шлёт уведомления при нарушении условий. Правило на частоту ошибок:
POST /_alerting/rules?filter_path=id
{
"rule_type_id": ".es-query",
"name": "Ошибка 5xx за 5 минут",
"params": {
"index": ["logs"],
"timeField": "@timestamp",
"esQuery": {
"query": { "range": { "count5xx": { "gt": 10 } } }
}
},
"actions": [
{ "group": "query matched",
"id": "my-slack-connector",
"params": { "message": "5xx превышен: {{count}} за 5 минут" } }
]
}15. Производительность и масштабирование
Стек растёт — и его нужно уметь расширять:
- индексные шаблоны: разделение логов по дням (
logs-2026.09.19); - ILM (index lifecycle management): rollover, delete через N дней;
- сжатие и количество реплик — баланс надёжности и цены;
- Filebeat вместо Logstash для простых задач экономит ресурсы;
- разделяй роли узлов: hot / warm / cold.
# размер шардов держи в диапазоне 10–50 ГБ
GET /_cat/shards?v16. Алиасы и reindex
Менять mapping и поля у готового индекса трудно — проще создать новый и перенести данные. Для «прозрачной» замены используют алиасы:
POST /_aliases
{
"actions": [
{ "remove": { "index": "logs-2026.09", "alias": "logs-live" } },
{ "add": { "index": "logs-2026.09-v2", "alias": "logs-live" } }
]
}Reindex копирует документы между индексами (можно с квалифицирующим запросом):
POST _reindex
{ "source": { "index": "logs-2026.09" },
"dest": { "index": "logs-2026.09-v2" } }- приложения пишут в алиас, поэтому переход для них бесшовный;
- reindex по большому индексу выполняй пачками пере rate-limit — не дави кластер;
- после переноса удали старый индекс и верни настройки.
17. Enrich: обогащение логов справочниками
Чтобы лог стал понятнее, добавь к нему контекст: имя сервиса из инстанса, регион, владельца. Enrich-processor делает это на входе.
PUT /_enrich/policy/services-policy
{ "match": { "indices": ["services"],
"match_field": "instance",
"enrich_fields": ["service", "team", "region"] } }PUT /_ingest/pipeline/enrich-logs
{ "processors": [
{ "enrich": { "policy_name": "services-policy",
"field": "instance", "target_field": "meta" } }
] }- справочник-индекс обновляется, после чего перезапускается политика;
- применение — через pipeline при индексации или Logstash enrich filter;
- никаких JOIN на этапе чтения: всё уже в документе — поиск мгновенный.
18. X-Pack Security: защита стека
Стек без аутентификации отдаёт логи любому, кто дотянется до порта. Включение защиты в новом стеке — с самого начала:
# elasticsearch.yml
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true- сгенерируй пароли:
bin/elasticsearch-setup-passwords auto; - Kibana и Logstash получают креды через
elasticsearch.username/password; - пользовательские роли: read-only для аналитиков, kibana_dashboard_only_users для «наблюдателей»;
- закрой порты 9200 и 9300 фаерволом — безопасность слоями.
kibana.yml -> elasticsearch.username: kibana_system19. Кейс: расследование инцидента по логам
Практический сценарий: пo/háчe нaчин в 14:02 выросла ошибка 500. Пошаговое разбирательство:
- Discover: фильтр по уровню
status >= 500за последние 2 часа; - Cut by
service.nameиhost— это API-шлюз или конкретный инстанс; - трейн самого медленного запроса: traceId из лога → собери вкладки по времени;
- посмотри correlation с деплоем и метриками (если подключен Prometheus);
- алерт-правило (раздел 14) о подобном случае уже снимет поиск.
# Kibana Discover — стартовый запрос инцидента
level: ERROR AND status: [500 TO 599] | Last 2h| Гипотеза | Проверка |
|---|---|
| деплой сломал API | сверка времени рестарта и времени ошибок |
| нагрузка | метрики CPU, лог «connection refused» |
| баговый запрос | группировка по IP и пользователю |
20. dissect vs grok: скорость парсинга
grok мощный, но на высоком потоке медленный. Для «простых» логов быстрее dissect:
filter {
dissect {
mapping => {
"message" => "%{ts} %{level} [%{thread}] %{msg}"
}
}
}- dissect жёсткий по позиции (быстрее), grok гибкий по regex (дольше);
- если поле необязательно — dissect не сработает без строгого соответствия;
- на потоке 10К событий/сек разница заметна: dissect может выиграть в 5–10 раз.
# гибрид: лёгкий dissect для типовой строки + grok для «странной»
if [log][file][path] =~ /error/ { grok { match => { "message" => "..." } } }
else { dissect { mapping => { "message" => "..." } } }21. Шаблоны индексов и data streams
Правила создания индексов хранят в шаблонах, чтобы каждый день не настраивать руками:
PUT /_index_template/logs-template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 2,
"lifecycle": { "name": "logs-policy" }
},
"mappings": {
"properties": { "@timestamp": { "type": "date" } }
}
},
"priority": 100
}- composable-шаблоны: несколько шаблонов, каждый со своими настройками;
- data stream = шаблон + алиас + ILM: пишем в одно имя, ролловер автоматический;
- изменение mapping в шаблоне не трогает уже созданные индексы — нужен реиндекс.
logs-2026.09.19-именование.22. Импорт внешних данных
Данные приходят не только из Beats. Быстрые каналы — CSV и bulk API:
curl -X POST "localhost:9200/_bulk" \
-H 'Content-Type: application/x-ndjson' --data-binary @bulk.ndjson# bulk.ndjson — пары строк: метаданные и документ
{"index":{"_index":"prices"}}
{"sku":"A1","price":1000}
{"index":{"_index":"prices"}}
{"sku":"A2","price":2000}- файл → индекс: Filebeat для логов или Logstash для справочников в разных форматах;
- импорт CSV в Kibana удобен ad-hoc, но в регулярной загрузке лучше скрипт с bulk;
- задавай
_id: повторный прогон не задвоит данные.
_id создаёт новые документы: второй прогон того же CSV удвоит данные.23. Чек-лист боевого ELK
Стек «завёлся на локалке» ≠ готов к бою. Пройди по списку:
- heap JVM: 50% RAM узла, не выше 31 ГБ — выше Java работает иначе;
- swap выключен,
vm.max_map_count >= 262144; - X-Pack включен: пароли, TLS между узлами, роли для Kibana и Filebeat;
- порты 9200/9300 закрыты, мониторинг кластера подключен;
- ILM и retention согласованы с требованиями хранения логов.
sysctl -w vm.max_map_count=262144 # и в /etc/sysctl.confЧто дальше
Метрики приложений собирай в Prometheus, графики — в Grafana, разворачивай всё через Docker. Все курсы — на главной странице.