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 кажется избыточным. Выбор всё равно зависит от задачи - но теперь есть что показать заказчику вместо теоретических рассуждений.
- Grafana 7.2: library panels и трансформации без ETL · 19 октября 2020
- Kubernetes 1.19 GA: обновляем production с extensions/v1beta1 на Ingress v1 · 3 сентября 2020