Как мы перешли с Elastic на Grafana stack и сократили расходы в несколько раз

Kate

Administrator
Команда форума
Привет! Хочу поделиться историей миграции сервисов логирования и трейсинга с компонентов Elastic Stack на Grafana Stack и тем, что из этого вышло. До миграции у нас в М2 использовались достаточно классические схемы:

  • Logstash → Elasticsearch → Kibana для логов;
  • Jaeger → Elasticsearch → Kibana (Jaeger UI) для трейсов.
Это вполне рабочий вариант, который устраивал нас первые полтора года жизни проекта. Но время шло, микросервисы плодились как грибы после дождя, рос и объем клиентских запросов. Расширение ресурсов систем логирования и трейсинга становилось все более частой задачей. Объемов хранилищ и вычислительных мощностей требовалось все больше. Вдобавок лицезии X-Pack еще сильнее толкали ценник вверх. Когда замаячили проблемы с приобретением лицензий и доступом к самим продуктам компании Elastic, стало понятно, что дальше так жить нельзя.

В процессе поиска лучшего решения были перепробованы разные варианты компонентов, написаны Kubernetes-операторы и собрано два ведра шишек. В конечном итоге схемы приобрели следующий вид:

  • Vector → Loki → Grafana;
  • Jaeger → Tempo → Grafana.
Так удалось объединить три важнейших аспекта мониторинга: метрики, логи и трейсы — в одном рабочем пространстве Grafana и получить от этого ряд плюшек. Основные из них:

  • сокращение объема дискового пространства при тех же объемах данных;
  • сокращение объема вычислительных ресурсов для работы системы;
  • отсутствие необходимости покупки лицензии;
  • свободный доступ к продукту;
  • достаточно простая настройка механизма автомасштабирования системы.
Надеюсь, эта статья будет полезна как тем, кто только выбирает систему логирования/трейсинга, так и тем, кто имеет схожие сложности.


Предпосылки миграции. Logging​

Шел 2021 год, и, как упоминалось выше, мы использовали достаточно стандартную схему централизованного логирования.

67f996f1f58de5196bb694aced4aaab4.jpg

В сутки набегало порядка 1 ТБ логов. Кластер состоял примерно из 10 Elasticsearch data-нод, была приобретена лицензия X-Pack (главным образом, для доменной авторизации и алертинга). Приложения в основном были развернуты в кластере Kubernetes. Для их отправки использовался Fluent Bit, далее Kafka в качестве буфера и пул Logstash под каждый namespace. В процессе эксплуатации системы мы сталкивались с различными проблемами: некоторые решались достаточно просто, для части подходил только workaround, а какие-то решить и вовсе не удавалось. Совокупность второй и третьей групп проблем побудила нас к поиску другого решения. Прежде перечислю наиболее значимые из этих проблем.

1. Потери логов при сборе​

Как ни странно, первым компонентом, с которым начались проблемы, стал Fluent Bit. Время от времени он просто переставал отправлять логи отдельных подов. Анализ дебаг-логов, тюнинг буферов и обновление версии к желаемому эффекту не приводили. В качестве замены был взят Vector, который, как позже выяснилось, тоже имел подобные проблемы. Но это было исправлено в версии 0.21.0.

2. Костыли с DLQ​

Следующей неприятностью стало вынужденное использование костылей при включении DLQ на Logstash. Дело в том, что Logstash не умеет сам ротировать логи, попадающие в эту очередь, и в качестве почти официального workaround предлагалось просто рестартовать инстанс после достижения порогового объема. Негативно на работу системы в целом это не влияло, так как в качестве input использовалась Kafka и сервис завершался в graceful-режиме. Но видеть постоянно растущее число рестартов подов было так себе, да и за рестартами иногда маскировались другие проблемы.

3. Боли с написанием алертов​

Не слишком удобное описание правил алертинга. Можно, конечно, накликать через веб-интерфейс Kibana, но удобней все же через описание через код, как, например, в Prometheus. Синтаксис достаточно неочевидный и вырвиглазный, вот пример:

{

"params":{

"aggType":"count",

"termSize":5,

"thresholdComparator":"<",

"timeWindowSize":15,

"timeWindowUnit":"m",

"groupBy":"all",

"threshold":[50],

"timeField":p",

"index":["app-common*"]

},

"consumer":"alerts",

"schedule":{

"interval":"5m"

},

"tags":[],

"name":"app-common",

"enabled":true,

"throttle":"1h",

"rule_type_id":".index-threshold",

"notify_when":"onActionGroupChange",

"actions":[

{

"group":"threshold met",

"id":"378045c0-2101-11ec-83cd-97f03e582f14",

"params":{

"level":"warning",

"message":"There is low log rate for 15 minutes in {{rule.name}}:\n\n- Value: {{context.value}}\n- Conditions Met: {{context.conditions}} over {{params.timeWindowSize}}{{params.timeWindowUnit}}\n- Timestamp: {{context.date}}"

}

}

]

}

4. Потребление ресурсов​

Но основные проблемы были связаны с увеличением стоимости потребляемых ресурсов и, как следствие, стоимости системы. Когда цена перевалила за полмиллиона в месяц, мы стали все чаще задумываться над поиском альтернатив. Самыми прожорливыми компонентами оказались Logstash и Elasticsearch, ведь JVM, как известно, неравнодушна к количеству памяти.

5. Доступность продукта​

Последней каплей стало ограничение доступа к продуктам Elastic и будущая невозможность покупки лицензии. Конечно, есть различные зеркала, но сколько они будут доступны и как часто там будут появлятся новые версии, неизвестно. Можно придумать и схему закупки лицензий через какой-нибудь параллельный импорт, но цена возрастет.

Tracing​

Для централизованного сбора трейсов использовался Jaeger, который через Kafka отправлял данные в отдельный кластер «Эластика». Трейсов меньше не становилось, и приходилось масштабировать систему под сотни гигабайт трейсов ежедневно. Схема выглядела следующим образом:

d5dc99dc020b341719df804dd5bba4fc.jpg

Общим неудобством было также использование разных веб-интерфейсов под различные аспекты мониторинга:

  • Grafana — для метрик;
  • Kibana — для логов;
  • Jaeger UI — для трейсов.
Конечно, Grafana позволяет подключить в качестве источника данных и Elasticsearch, и Jaeger, но остается необходимость манипулировать различными синтаксисами запросов к данным.

С таких предпосылок начался наш поиск новых решений для логирования и трейсинга. Не то чтобы мы проводили какой-то сравнительный анализ различных систем. Сейчас на рынке из серьезных продуктов представлены в основном те, что требуют лицензирования, и с этим в любой момент могла возникнуть проблема. Поэтому мы решили взять opensource-проект Grafana Loki, который уже был успешно внедрен в ряде компаний, а затем очередь дошла и до Tempo.

Миграция в Loki​

Итак, почему был выбран именно Loki:

  • это opensource-продукт, позволяющий реализовать все необходимые нам фичи;
  • компоненты Loki потребляют ощутимо меньше ресурсов при тех же нагрузках;
  • все компоненты могут быть запущены в кластере Kubernetes, можно использовать HPA или Keda для их автоматического масштабирования;
  • данные занимают в несколько раз меньше места, так как хранятся в сжатом виде;
  • построение запросов очень схоже с языком PromQL;
  • описание алертов аналогично алертам в Prometheus;
  • ну и конечно, система отлично интегрирована с Grafana для визуализации сообщений и построения графиков.
В качестве способа развертывания был выбран Distributed helm-chart, а в качестве хранилища чанков — Object Storage. Logstash был заменен на более легковесный Vector. На небольших объемах (несколько сотен сообщений в секунду) система вполне работоспособна из коробки. Логи успевают сохраняться практически в режиме realtime, поиск свежих данных работает почти также быстро, как и в Kibana. Получившаяся схема выглядит следующим образом:

64430f9dc9cb76884af551560cb21e46.png

Тюнинг настроек​

С увеличением нагрузки начинает страдать цепочка записи. То ingester, то distributor начинают дропать логи, возвращать таймауты и т. д. А при запросах данных, отсутствующих в кэше ingester-ов, ожидание ответа начинает приближаться к минуте или вовсе падать по таймауту. На всякий случай приведу схему того, как выглядит инсталляция Loki через Distributed chart.

2ee5ba15efc3b0f876eb2b56da16c3e9.png

В случае проблем с записью стоит обратить внимание на параметры:

limits_config.ingestrion_burst _size_mb
limits_config.rate_mb
Они отвечают за пропускную способность, и при приближении трафика к пороговым значениям сообщения будут отбиваться.

Для увеличения скорости поиска стоит использовать кэширование в Memcached или Redis. Также можно поиграть с параметрами:

limits.config.split_queries_by_interal
frontend_worker.parallelisim
В Loki может использоваться кэширование четырех типов данных:

  • чанки;
  • индексы;
  • ответы на предыдущие запросы;
  • кэширование данных для нужд дедупликации.
Стоит использовать как минимум первые три.

После тюнинга настроек компонентов, масштабирования и включения кэша задержки записи логов исчезли, а поиск стал отрабатывать за приемлемое время (в пределах 10 секунд).

Что касается правил алертинга, пишутся они аналогично правилам Prometheus.

- alert: low_log_rate_common

expr: sum(count_over_time({namespace="common"}[15m])) < 50

for: 5m

labels:

severity: warning

annotations:

summary: Count is less than 50 from {{ $labels.namespace }}. Current VALUE = {{ $value }}
Для доступа к сообщениям используется язык запросов LogQL, который схож по синтаксису с PromQL. В части визуализации все выглядит примерно как в Kibana.

f7b16c3a743c74878091b82ba9a1a603.png

Очень удобной фичей является возможность настроить ссылки из лог-сообщений к соответствующим трейсам. В этом случае при нажатии на ссылку в правой половине рабочей области открывается трейс.

5aba0f5608bf8c14f6107e1dcd3c0450.png

Миграция в Tempo​

Tempo — более молодой продукт (появился в 2020-м), имеющий очень схожую с Loki архитектуру (в целом, как и Mimir). В качестве способа развертывания был выбран Distributed helm-chart.

Дистрибьюторы имеют возможность подключаться к Kafka напрямую, но в таком виде не удалось добиться скорости вычитывания, соразмерной скорости записи. С использованием Grafana agent удалось эту проблему решить.

4c240f1b5b6193de5d12bfefc758eb51.jpg

Тюнинг настроек​

В процессе конфигурирования следует уделить внимание следующим моментам:

  1. Если используется Jaeger, то спаны от Grafana agent в Distributor будут отправляться по GRPC. В кластере Kubernetes равномерное распределение по дистрибьюторам потребует настройки балансировщика, например Envoy (как вариант, через Istio).
  2. В случае проблем со скоростью записи стоит увеличить параметры:
    1. overrides.ingestion_burst_size_bytes
    2. overrides.ingestion_rate_limit_bytes
    3. overrides.max_bytes_per_trace
  3. Также можно увеличить таймаут ожидания ответа от ingester — ingester_client.remote_timeout. Они не всегда успевают ответить за 5 секунд.
Помимо самой визуализации трейса, можно еще получать связный граф, где будут отображены все компоненты, участвующие в обработке запроса.

95bb5288a326dd0b7ee3abbaa41e85fe.png

Заключение​

От первичной инсталляции Loki в dev-среде до внедрения на production прошло около двух месяцев. Еще столько же потребовалось для Tempo.

В итоге получилось:

  • уменьшить стоимость систем примерно примерно в 7 раз;
  • уйти от возможных проблем с лицензией;
  • совместить визуализацию метрик, логов и трейсов в единой системе;
  • организовать алертинг аналогичным Prometheus образом;
  • настроить автомасштабирование системы в зависимости от объема поступающих данных.
Справедливости ради, стоит отметить и то, чего добиться пока не удалось:

  • скорости отдачи логов, аналогичной Elastic, если данные не находятся в кэше. Частично это решается добавлением наиболее часто используемых полей в индекс;
  • получения агрегированных значений за большой промежуток времени (больше суток), например, подсчет числа лог-сообщений функцией count_over_time;
  • производительности в сохранении спанов как в схеме с Elasticsearch. Пока удалось решить горизонтальным масштабированием и увеличением числа партиций в Kafka.
В целом, можно сказать, что опыт перехода на Grafana Stack оказался удачным, но процесс совершенствования схем логирования и трейсинга не заканчивается.


 
Сверху