Однажды мне понадобилось внедрить метрики в сервисы своей команды. С самого начала я не понимал, что именно хочу получить: одно дело — прикрутить библиотеку и нарисовать графики, другое дело — показывать осмысленные данные.
Мне нужен был гайд, который сочетает эти две вещи: сначала «почему так принято», а затем — «как правильно делать». В результате такой гайд мне пришлось написать самому. Его цель — объяснить разработчикам с любым бэкграундом, что такое метрики, как правильно о них думать и осмысленно использовать. Сначала гайд жил во внутренней документации Точки, но я решил сделать его публичным — возможно, кому-то этот опыт будет полезен. Разбираться будем с Prometheus и Grafana. Если у вас другой стек — не страшно. Мы затронем и фундаментальные темы: например, перцентили, производные и кардинальность.
Гайд будет выходить как цикл статей. Сначала посмотрим на архитектуру: как собираются метрики и где хранятся. Дальше разберемся с типами метрик — они не так просты, как кажется. Потом придется немного отвлечься на математику (но только с инженерной точки зрения!). И, наконец, научимся писать запросы, но не просто так: сразу посмотрим на разные грабли и неочевидные моменты.
Какие статьи выйдут в цикле:
Потерянное введение
Прежде чем подключать библиотеки, поднимать сервера и писать запросы, давайте восстановим справедливость и найдем введение, которого мне не хватило при изучении метрик. В этой статье мы определимся, какую вообще решаем задачу, и почему решение такое непривычное.
Здравый смысл
Вот захотели вы собирать разные метрики, «чтобы было видно все, что происходит в сервисах». Теперь задайте себе и команде важные вопросы:
А как это вообще сделать?
А что именно хочется увидеть в результате?
Как решают эту задачу другие люди?
Чтобы не придумывать свой велосипед, можно изучить общепринятые подходы, найти готовый стек. Но там будет, что называется, своя атмосфера. И нет коллеги или гайда, которые бы объяснили, почему оно вообще так странно устроено.
Технически задача выглядит как-то так: в компании уже есть, например, Grafana (если нет, ее несложно поднять). Есть сотня способов собрать и передать туда данные, но сразу возникают вопросы: что мы хотим там отобразить? А почему нельзя это просто в SQL сложить? Зачем нужен этот ваш Prometheus, наконец?
Для сложных технологий хорошо иметь не только инструкцию, но и правильный mindset, тогда все инструменты будут даваться легче.
Вот со всем этим и будем разбираться.
Вообще метрики — это неожиданно сложно: мы пытаемся разными инженерно-математическими фокусами сжать неудобный, большой массив данных до чего-то наглядного. В отличие от логов, которые просто пишутся «как есть» и наверняка уже используются в ваших сервисах. Кстати, а почему бы не парсить логи и на основании этого уже строить какие-то графики? Так, конечно, можно, и это нормально — до некоторого предела.
Метрики vs логи
Логи — это про точность: мы собираем информацию на каждое событие. Если их агрегировать, можно увидеть общую картину и даже вытащить детали вплоть до единичного запроса. Но отправка логов на каждый чих плохо масштабируется.
Трафик логов зависит от трафика приложения: больше запросов, больше действий – и логов пропорционально больше. Часто они не структурированы, их сложно индексировать и парсить — для графиков нужно вытаскивать числа из текста. Ну и традиционно в логи пишется много лишнего, а значит обработка и агрегация будут страдать и отставать от реального времени.
Метрики – это сразу общая картина о состоянии приложения. Мы собираем не все детали, а только готовую выжимку: например, количество запросов к сервису. Картину мы получим, но в детали провалиться нельзя. Зато такой подход хорошо масштабируется и быстро работает. Можно моментально реагировать и рассылать алерты. Если нужны детали – думаем и добавляем по чуть-чуть.
Например, считаем количество запросов к разным endpoint-ам. Трафик метрик зависит от их количества и периодичности сбора/отправки их в хранилище. И, наконец, в метрики мы не пишем ничего лишнего, а, наоборот, стараемся писать как можно меньше. В следующих частях станет понятно, почему.
Пишем все и потом думаем (ценой трафика и сложности обработки) vs думаем и пишем только, что надо (ценой потери деталей).
Если у вас не очень много сервисов, и вы не планируете масштабирование, то можно не усложнять и остановиться на парсинге логов. В популярном стеке ELK есть встроенные средства визуализации.
Push vs Pull
Сбор метрик можно организовать двумя способами. У каждого есть свои плюсы и минусы. Чем глубже копать и чем больше масштабы, тем сложнее становится выбор — вплоть до холиваров.
Push – принцип такой же, как с логами или базой данных: произошло событие – пишем данные в хранилище. События можно отправлять пачками. Способ прост в реализации с точки зрения клиента.
Pull – наоборот: приложения хранят в памяти компактные данные вроде обработано запросов: 25
. Кто-то периодически приходит и собирает их, например по HTTP. С точки зрения клиента pull сложнее реализовать, но проще отлаживать: у каждого приложения появляется endpoint, куда можно зайти браузером и увидеть, что происходит конкретно в этом приложении. Не нужно писать хитрые запросы, фильтровать данные по репликам и вообще иметь доступ в общее хранилище метрик. Кроме того, эта модель стимулирует разработчиков писать в метрики только то, что необходимо, а не все подряд.
Чтобы метрики не занимали лишнюю память приложения и быстро отдавались, мы вынуждены их агрегировать. Никто не хочет хранить в памяти подробности о каждом запросе, потому что эти данные очень быстро займут всю память. Приходится ограничиваться минимумом: например, вести счетчики запросов и ошибок. Это в итоге уменьшает нагрузку на инфраструктуру: ей нужно забрать и сохранить уже максимально сжатые данные, о которых уже подумали разработчики приложения.
Конечно, push тоже можно приготовить аналогичным образом, но там гораздо проще отправлять все подряд, а потом разбираться.
Мы выбрали pull: Prometheus на тот момент был построен в основном вокруг этой модели, и нам было проще выбрать решение с централизованным управлением.
TSDB
Метрики нужно куда-то складывать и потом делать выборки. Эту задачу решают специализированные БД: Time Series Database.
Особенности TSDB – обработка временны́х рядов, то есть однотипных измерений во времени. БД этого типа оптимизируют хранение какого-то числа, которое записано через равные интервалы времени. Проще понять на примере: чтобы собирать ежедневную температуру воздуха, нам надо хранить что-то вроде [(day1, t1), (day2, t2), ...]
и больше ничего.
Главное сейчас – понять вот эту особенность: TSDB нужны, чтобы сохранять время и одно число, привязанное к этому времени. Потом снова время и число, и так далее. Пока что обойдемся без конкретики и деталей реализации.
Специфика:
реляционку умеют по минимуму: если SQL, то ограниченный, а то и вовсе его нет;
типы хранимых данных урезаны;
оптимизация на постоянную непрерывную запись.
Раз на SQL это все не похоже, то не обязательно поддерживать сложный язык запросов. Можно сделать свой, упоротый метрико-ориентированный простой язык.
Когда-то мы использовали для метрик InfluxDB — у него как раз SQL-подобные запросы. Но на наших объемах он просто взорвался, и с high availability было плохо, поэтому от него отказались.
Для работы с TSDB существуют свои специализированные форматы данных, и они проще, чем SQL. В следующей части мы разберемся, как готовить TSDB Prometheus: он устроен так, что формат метрик и язык запросов — это практически одно и то же, поэтому страдать придется всего один раз!
Визуализация
Когда у нас появится БД для хранения метрик и способ доставки данных в нее, мы сможем писать запросы, но... Просто так смотреть на таблицы с числами неинтересно. Поэтому запросы к БД обычно делает Grafana: она парсит таблицы из ответов и рисует удобные графики. В ней уже можно настроить масштаб, покрасить и добавить прочие украшения. Но писать запросы все равно придется самостоятельно.
Дальше мы будем разбираться именно с запросами Prometheus, без нюансов, которые добавляет Grafana. Поэтому для начала она вообще не понадобится — у Prometheus есть простой web-интерфейс, который визуализирует результаты запросов. Его вполне хватит для обучения, тестов и отладки.
Алерты
Самое полезное, что можно выжать из метрик и графиков – это алерты: нам не нужен живой человек, который постоянно следит за свободным местом на диске или за количеством ответов HTTP 500
. Можно настроить автоматику, которая реагирует на выход графиков за допустимые границы и рассылает уведомления. Но, чтобы до этого добраться, придется сначала научиться собирать и хранить метрики, потом их запрашивать и отображать на графиках, и только потом уже расставлять алерты.
С алертами мы разбираться не будем, потому что концептуально тема уже не настолько сложная для понимания, как метрики, а архитектура решения будет очень сильно зависеть от специфики: кому-то нужна рассылка в мессенджере, кому-то телефонный бот.
Теперь должно быть более-менее понятно, из чего будет состоять решение для сбора метрик, и почему оно такое непривычное. Дальше будем разбирать формат данных и язык запросов Prometheus.