ADG Оставить заявку
Блог DevOps 4 мин чтения

Grafana Loki 2.0: уходим от ELK в пользу лёгкого агрегатора логов для Kubernetes

Loki 2.0 с LogQL 2.0 и boltdb-shipper: переходим с ELK на более компактное решение для логов Kubernetes. Promtail с auto-discovery собирает логи всех подов без ручной конфигурации.

Контекст момента

Grafana Loki 2.0: LogQL 2.0, мультитенантность, поддержка хранения меток в индексе boltdb-shipper

Несколько недель назад Grafana Labs выпустила Loki 2.0, и мы наконец занялись тем, о чём думали давно: заменить ELK на что-то менее ресурсоёмкое для одного из клиентских кластеров Kubernetes. Elasticsearch в нашем случае жил там в первую очередь ради логов, а не поиска по произвольным полям - и это всегда выглядело как немного избыточно.

Почему вообще смотрели в сторону от ELK

ELK-стек работает. Это не про то что он плохой - он взрослый, надёжный, с огромной экосистемой. Но для задачи «хранить и искать логи Kubernetes-подов» он несёт с собой определённую стоимость: Elasticsearch любит память, требует JVM-тюнинга, индексирует все поля по умолчанию и при этом ест ресурсы даже если ты 95% этих возможностей не используешь. На кластере с несколькими сотнями подов - это заметно.

Loki с самого начала строился на другой идее: хранить не проиндексированный полнотекстовый контент, а только метки (лейблы), и давать поиск по тексту через потоковую фильтрацию. Индекс маленький, данные сжатые, хранилище - объектный storage или просто файловая система. За это платишь скоростью полнотекстового поиска - grep по чанкам не то же самое, что инвертированный индекс Lucene. Но для просмотра логов конкретного пода или пространства имён - достаточно.

Что изменилось в 2.0

Предыдущие версии Loki были в каком-то смысле proof of concept. В 2.0 несколько вещей дошли до состояния, когда их можно ставить без оговорок.

LogQL 2.0 - это главное изменение на уровне языка запросов. В первых версиях LogQL умел фильтровать строки и парсить по паттернам. Теперь появились метрические запросы из log-потоков: можно посчитать rate ошибок, quantile по распарсенным полям, агрегацию по лейблам - всё это без отдельного Prometheus. Типичный запрос для rate ошибок:

sum(rate({namespace="production", app="api"} |= "ERROR" [5m])) by (pod)

Или если в логах есть structured JSON:

{namespace="production"} | json | latency_ms > 500 | line_format "{{.pod}} {{.latency_ms}}ms"

line_format - это тоже новинка 2.0: можно переформатировать строку лога на лету в запросе, выводя только нужные поля.

boltdb-shipper в 2.0 вышел из beta и стал рекомендуемым режимом хранения индекса. Идея в том, что индекс (метки) хранится локально в BoltDB, а затем периодически выгружается в объектное хранилище. Это снимает необходимость держать отдельный Cassandra или DynamoDB для индекса - раньше это был один из главных операционных порогов при деплое Loki в production.

Мультитенантность доработана: каждый тенант получает изолированные потоки логов через заголовок X-Scope-OrgID. Для нас это означает, что несколько клиентских кластеров можно собирать в одну инсталляцию Loki, не смешивая данные.

Promtail и auto-discovery

Promtail - агент сбора логов для Loki. Его настройка в Kubernetes выглядит значительно проще, чем настройка Filebeat или Fluentd для похожей задачи.

Конфиг для сбора логов всех подов - это по существу одна секция с несколькими строками:

scrape_configs:
  - job_name: kubernetes-pods
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        target_label: app
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod
      - source_labels: [__meta_kubernetes_pod_container_name]
        target_label: container

Promtail читает Kubernetes API, находит все поды, цепляет логи из /var/log/pods/ и добавляет метки из аннотаций и лейблов пода. Без ручного перечисления приложений, без сопровождения списка источников - поднял новый деплоймент, он автоматически попал в Loki. Это разница по сравнению с Filebeat, где для каждого нового приложения нужно было хотя бы проверить конфиг.

Что получилось на практике

Деплой через официальный Helm chart занял около часа вместе с разбирательством. Loki поставили в режиме single binary (для небольшого кластера это вполне уместно), boltdb-shipper настроили на S3-совместимое хранилище. Grafana уже была в кластере - добавили datasource типа Loki, и логи появились в Explore без дополнительных дашбордов.

По ресурсам разница заметна: Elasticsearch с тремя нодами под логи занимал значительно больше памяти и CPU, чем Loki. Это не сюрприз - архитектуры принципиально разные. Плата за это - LogQL менее выразителен чем Kibana Query Language для сложных структурированных запросов. Если нужен анализ по произвольным вложенным полям JSON с агрегацией - Elastic всё ещё выигрывает. Если нужно «найти все логи этого пода за последние два часа и посмотреть ERROR-ы» - Loki справляется нормально.

Один нюанс, который немного удивил: AlertManager-интеграция через Loki alerting rules появилась в 2.0, но пока выглядит сыровато по документации. Алертинг на основе логов настроили отдельно через Grafana alert engine по LogQL-метрикам - это стабильнее.

На managed-сопровождении предлагаем Loki как опцию для Kubernetes-кластеров, где ELK кажется избыточным. Выбор всё равно зависит от задачи - но теперь есть что показать заказчику вместо теоретических рассуждений.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.