Отправляем .NET Core метрики в Graphite через StatsD

.NET Core StatsD

Уже прошло больше года с тех пор, как я подключил кусок JavaScript к collectd плагину и начал собирать данные мониторинга с нашей CI, чтобы потом хранить их в Graphite. И знаете что? Оно до сих пор работает. Даже JavaScript’овая часть. Но настали времена, когда мне нужно будет собирать метрики с .NET Core сервисов, и, чувствую, связкой JavaScript + collectd я уже не отделаюсь.

По идее, проблем быть не должно. В Graphite ведь можно отправлять данные хоть по TCP в виде текста, так что сервисы вполне могут заняться этим самостоятельно. Но можно сделать даже ещё проще.

На сцену выходит statsd

Если пристально посмотреть на свежайший Docker образ для Graphite, то в нём можно заметить тулзу под названием StatsD. Это такой маленький буфер-демон между графитом и приложением, который принимает метрики последнего по UDP,  агрегирует их немного, и раз в сколько-то секунд складывает результаты в Graphite. Так как речь идёт о UDP, то метрики можно сбрасывать ну очень быстро и совсем не дожидаться ответа получателя. Так как StatsD работает ещё и агрегатором, то метрики можно сбрасывать в огромных количествах, не боясь, что Graphite сляжет.

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

StatsD в связке с .NET Core

Во взаимоотношениях StatsD и .NET Core нет ничего особенного, но так как в последнее время мне приходится иметь дело с Core, то эта пара меня интересует больше всего.

На NuGet можно найти кучу дотнэтовских пакетов для StatsD, но они то безумно старые, то поддерживают только .NET Framework. Наконец, я нашёл один, который вроде и обновляется иногда, и работает с нужными .NET Core — StatsdClient (это не реклама, но если у автора есть ненужное пиво — приму в дар).

Самый лучший способ посмотреть, как что-то работает — сделать из этого «что-то» — «нечто». Так что приступим.

Большой пример

.NET Core программулина

У меня есть простенькая .NET Core программка, которая запускает два потока. Первый — для бессмысленной работы (генерация массивов строк), а второй — для её мониторинга: как часто мы вызываем сборщик мусора, сколько всего раз это произошло, сколько всего памяти используем и как долго делалась единица работы. Пока Graphite существует только в теории, все собранные метрики мы будем отправлять в консоль.

Эти данные, конечно, стоило бы собирать откуда-нибудь снаружи, хотя бы через тот же Event Tracing. Но что уж теперь поделаешь. Может, в следующий раз.

Итак, запускаем программку, смотрим, что она выводит:

Программка выводит много красивых и наверняка полезных чисел. Но без графика я их не воспринимаю, так что установим-ка мы Graphite, который сможет их построить.

Устанавливаем Graphite

С тех времён, как я последний раз устанавливал Graphite, многое изменилось. Теперь вместо установок и настроек индивидуальных сервисов можно взять официальный Docker образ, и расправиться с задачей в одну команду:

Для наших целей мне не нужен контейнер с именем и кучей открытых портов. Достаточно лишь 80-го порта (Graphite UI) и 8125-го для UDP (StatsD), так что эту команду можно порядочно упростить:

Теперь откываем localhost, и вот он, Graphite, во всём своём пустынном великолепии:

Пустой Graphite

Теперь нужно забить его данными для графика.

Собираем метрики через StatsdClient

Добавив StatsdClient пакет в проект я получил в награду класс Metrics, которым просто позаменял Console.WriteLine‘ы и StopWatch. В результате код даже немного похорошел:

Изменения, думаю, вполне понятны. Во-первых, мы показали StatsdClient , где Graphite, собственно, находится. Во-вторых, так как имена для графитовых метрик обычно похожи на: myserver.myapp.mysensor.mySensorComponent, что потом очень удобно превращать в дерево папок в Graphite UI. то свойством Prefix можно задать первую часть имени, а при сборе конкретных метрик — вторую.

Ну и наконец, методы Time, GaugeAbsoluteValue и Counter, собственно, и отправляют метрики в StatsD. Вот на них стоит посмотреть поближе.

GaugeAbsoluteValue

В этот метод скармливают метрики, которые, в принципе, уже полезны как есть. Вроде температуры, места на диске, количества памяти, и т. п. Какое значение считали с сенсора, то потом на графике и нарисовали. В строке 21 мы собираем метрику totalMemory, и вот она как раз под эту категорию и попадает:

 Метрика-шкала

Counter (он же счётчик)

Счётчики немного прикольнее, потому что они не показывают значение метрики как есть, а показывают скорость, с которой оно меняется. Как производная в матанализе. Когда речь идёт о количестве открытых соединений, или сборок мусора, или ещё чего-то, меняющегося со временем, мне интереснее не сколько всего раз сборщик мусора сработал за день, а в какие моменты его было больше, а в какие — меньше.

В строке 23 мы как раз и замеряем, сколько раз сборщик мусора прошёлся по нулевому поколению (gen0) за последние 100 миллисекунд. Console.WriteLine выводил числа в пределах 5-17, так что я думаю, что за секунду происходило в среднем где-то 100-120 сборок. Смотрим на график и… ну почти угадал.

Graphite и счётчик

Time

Название «Время» для этого типа метрик несколько сбивает с толку, потому что там может быть не только время. Например, количество байт, переданных в пакете, или количество долларов в корзине пользователя тоже бы вполне подошли. Все метрики этого типа StatsD терпеливо собирает в течение всего периода буферизации (10 секунд по умолчанию), а затем отправляет в Graphite статистику:

  1. сколько всего метрик собрано за эти 10 секунд,
  2. какое было минимальное значение,
  3. какое максимальное,
  4. какое средние,
  5. и т. п.

В нашем случае мы замеряли продолжительность функции DoPointlessJob, и в Graphite теперь можно посмотреть статистику всего этого безобразия. Ну или хотя бы максимальное, минимальное и средние значения:

Временная метрика

Красота.

И ещё один тип

Если ещё один StatsD тип метрик — множество (Set). StatsD собирает значения этого типа в течение 10 секунд (или какой там период буферизации мы выбрали), и затем отправляет в Graphite количество уникальных значений, пришедших ему на вход. Например, можно отправлять ему айпишки пользователей, открывающих страницы сайта, а потом узнать, сколько же всего уникальных пользователей сейчас на сайте.

Очень короткое заключение

Самое большое достоинство StatsD в том, что он ну просто невероятно простой. И полезный. Всё-таки агрегация данных, абстрагированность от бакэнда, UDP — всё это делает сборку метрик и приложение в целом чуточку быстрей и понятней. Конечно, можно было бы взять TcpClient и какой-нибудь NetworkStream и писать метрики в Graphite напрямую, но.. зачем?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *