Собираем метрики при помощи Graphite, StatsD и CollectD

13 июля 2015

Любые сайтики и бэкенды должны писать метрики, это бесспорно. Однако возникает вопрос, куда именно их писать. Есть неплохие SaaS решения, например, Datadog. Но Datadog на момент написания этих строк стоял 15$ в месяц за один хост. При определенном количестве хостов в какой-то момент вы можете обнаружить, что 30% стоимости всей инфраструктуры приходится на метрики. В связи с чем возникает желание перевести все на self hosted решение. О том, как поднять такой вот self hosted недо Datadog, и пойдет речь в посте.

Примечание: Поскольку Graphite и StatsD тянут за собой просто невероятно много всякого говна, ставить их лучше либо на отдельной машине, либо в чем-то вроде Vagrant или Docker.

Graphite — то, куда складываются метрики

Ранее мы с вами уже устанавливали Graphite, а также разбирались, чем Graphite отличается от Carbon, а Carbon от Whisper. Соответствующая заметка ничуть не потеряла своей актуальности. Так что, будем считать, что с установкой мы уже справились.

После установки редактируем файл /etc/carbon/storage-schemas.conf. Удаляем все и пишем:

[carbon]
pattern = ^carbon\.
retentions = 60:90d

[default]
pattern = .*
retentions = 10s:14d

Эти настройки говорят, что все метрики должны храниться за десятисекундные интервалы в течение 14 дней, кроме метрик, начинающихся со строки carbon. — эти метрики можно писать не чаще одного раза в минуту и хранятся они 90 дней. Чем больше значений вы храните, тем больше места на диске потребуется. Например, при хранении метрик с шагом в одну секунду в течение двух недель (retentions = 1s:14d), нужно ~14 Мб места под одну метрику (около 12 байт на одно значение). Учитывая, что на одну пользовательскую метрику StatsD, речь о котором пойдет ниже, может писать в Graphite десяток своих метрик, место может закончиться очень быстро. На практике десятисекундных интервалов вполне достаточно.

В общем случае retentions можно задавать как-то так:

retentions = 10s:7d,1h:30d,1d:1y

В этом примере метрики пишутся за десятисекундные интервалы времени и хранятся 7 дней. Из этих метрик агрегируются метрики за час, которые хранятся 30 дней. А из этих метрик агрегируются метрики за день, которые хранятся в течение года.

При агрегации метрик по умолчанию берется среднее. Это почти всегда не то, чего вы хотите на самом деле. Если вы пишите метрику с максимальным временем ответа, при агрегации метрик за час или за день будет взято среднее от максимумов. В результате график будет прыгать при изменении масштаба, и полученные путем такой агрегации цифры будут полностью лишены смысла.

Для решения этой проблемы положим рядом со storage-schemas.conf файл storage-aggregation.conf следующего содержания:

[min]
pattern = \.min$
xFilesFactor = 0.1
aggregationMethod = min

[max]
pattern = \.max$
xFilesFactor = 0.1
aggregationMethod = max

[count]
pattern = \.count$
xFilesFactor = 0
aggregationMethod = sum

[lower]
pattern = \.lower(_\d+)?$
xFilesFactor = 0.1
aggregationMethod = min

[upper]
pattern = \.upper(_\d+)?$
xFilesFactor = 0.1
aggregationMethod = max

[sum]
pattern = \.sum$
xFilesFactor = 0
aggregationMethod = sum

[gauges]
pattern = ^.*\.gauges\..*
xFilesFactor = 0
aggregationMethod = last

[default]
pattern = .*
xFilesFactor = 0.1
aggregationMethod = average

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

Перезапускаем Carbon, чтобы изменения вступили в силу:

sudo service carbon-cache restart

Все метрики лежат в обычных файликах в каталоге /var/lib/graphite/whisper/. Можно посмотреть, сколько места файлы занимают на диске (место выделяется сразу целиком), можно спокойно их удалять без остановки Carbon, можно искать с помощью утилиты find, и так далее.

Примечание: Для удаления метрик, которые пишет StatsD, потребуется сначала остановить StatsD, иначе метрики волшебным образом появятся сразу после удаления.

Кстати, вы вряд ли хотите, чтобы ваши метрики были общедоступны. Давайте закроем доступ к веб-интерфейсу Graphite хотя бы при помощи простого логина и пароля:

sudo apt-get install apache2-utils
sudo htpasswd -c /etc/graphite/.htpasswd afiskon
sudo chown www-data:www-data /etc/graphite/.htpasswd
sudo chmod o-rwx /etc/graphite/.htpasswd

Правим файл /etc/apache2/sites-available/graphite-web.conf, в секции VirtualHost дописываем:

<Location "/">
  AuthType Basic
  AuthName "Password required"
  AuthUserFile /etc/graphite/.htpasswd
  Require valid-user
</Location>

Говорим:

sudo service apache2 reload

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

StatsD — штука для правильной агрегации метрик

К сожалению, Graphite является всего лишь тупой хранилкой значений. Если в него придут две метрики с одинаковым именем и таймстампом, он сохранит только последнее значение. Понятно, что сегодня, когда почти все приложения являются вебскейл и работают больше, чем в одном экземпляре, это никуда не годится. Поэтому перед Graphite должно стоять приложение, агрегирующее все метрики и складывающее в Graphite только окончательные значения. Таким приложением является StatsD.

StatsD написан на Node.js :( поэтому говорим:

sudo apt-add-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs

Собираем deb-пакет:

sudo apt-get install git debhelper devscripts
git clone https://github.com/etsy/statsd.git
cd statsd
dpkg-buildpackage
cd ..

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

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

sudo dpkg -i statsd_0.7.2_all.deb

Тут начинаются какие-то долгие процессы с npm и непонятно чем еще. Терпеливо ждем.

Затем правим /etc/statsd/localConfig.js:

{
  graphitePort: 2003
, graphiteHost: "localhost"
, port: 8125
, graphite: {
    legacyNamespace: false
  }
}

Здесь мы указываем, какой порт должен слушать StatsD, где ему искать Graphite, а также отключаем legacy namespace. Благодаря этому флагу все метрики будут писаться в поддерево stats. Иначе используется сразу stats_counts, stats и statsd.

Запускаем StatsD:

sudo service statsd start

В Graphite должны начать сыпаться внутренние метрики StatsD. Протокол StatsD мы уже немного изучали в рамках заметки Делаем метрики и мониторинг для Akka при помощи Kamon, потому что Datadog использует его расширенную версию. Так, например, делается инкремент метрики-счетчика:

echo "test.counter:1|c" | nc -u -w0 127.0.0.1 8125

В Graphite жмем F5 и в дереве ищем Graphite / stats / test / counter / count. График должен быть без дырок и обновляться раз в 10 секунд. Если метрики не шлем, должны видеть на графике нули.

Обратите внимание, что если раньше вы работали с Datadog по расширенному протоколу StatsD, простой смены IP в конфиге не будет достаточно для перехода. Не все метрики будут отображаться, а лог StatsD будет забит сообщениями об ошибках и очень быстро заполнит весь диск. Переходите на клиент с обычным, не расширенным, протоколом StatsD.

По умолчанию StatsD отправляет метрики в Graphite один раз в 10 секунд. Если вы вдруг решите настроить Graphite и StatsD так, чтобы значения писались за интервалы, отличные от дэфолтных 10 секунд, убедитесь, что Graphite и StatsD синхронизированы. То есть, StatsD нужно настроить так, чтобы он писал метрики с такой же частотой, с которой их сохраняет Graphite. Если StatsD будет писать чаще, вы просто испортите метрики. Если реже, на графике будут появляться дырки. Эти дырки, впрочем, можно скрыть, нажав в графике Graph Options → Line Mode → Connected Line. Насколько я понимаю, надежно получить метрики за короткие интервалы, скажем, 1 или 2 секунды, не представляется возможным.

CollectD — демон, дающий кучу готовых метрик

StatsD удобно использовать в приложении, которое вы сами разрабатываете или которое само уже поддерживает StatsD. Но что, если я хочу писать в Graphite количество свободной памяти на всех машинах в системе? Или как, например, прикажите прикручивать метрики к Nginx или PostgreSQL? К счастью, есть CollectD, который решает все эти проблемы.

Установка:

sudo apt-get install collectd collectd-utils

Правим /etc/collectd/collectd.conf:

FQDNLookup true

Убедитесь, что у вас в системе правильно настоены имена хостов!

LoadPlugin cpu
LoadPlugin load
LoadPlugin processes
LoadPlugin df
LoadPlugin disk
LoadPlugin swap
LoadPlugin memory
LoadPlugin network
LoadPlugin rrdtool
LoadPlugin write_graphite

Комментируем все плагины, кроме этих. Из того, что еще есть интересного — apache, nginx, mysql, postgresql, bind, java, memcached, ntpd, openvpn, varnish, uptime. Будьте осторожны! Чем больше плагинов, тем больше метрик. Если, например, вы включите плагин tcpconns, он будет писать метрики по каждому соединению! Плагин fscache выглядит соблазнительно, но также пишет довольно много метрик.

Для всех плагинов в конфиге есть примеры параметров. Конфигурацию для syslog надо закомментить, чтобы при перезапуске collectd не ругался вот так:

Found a configuration for the `syslog' plugin, but the plugin isn't
loaded or didn't register a configuration callback.

Настраиваем write_graphite:

<Plugin write_graphite>
    <Node "graphite_node">
        Host "localhost"
        Port "2003"
        Protocol "tcp"
        LogSendErrors true
        Prefix "collectd."
        StoreRates true
        AlwaysAppendDS false
        EscapeCharacter "_"
    </Node>
</Plugin>

Перезапускаем CollectD:

sudo service collectd restart

Смотрим на метрики и радуемся.

К сожалению, мне не удалось найти плагинов для Cassandra и Couchbase. Говорят, Cassandra можно очень круто мониторить java-плагином по JMX, но я лично пока что это не осилил. В общем и целом, системных метрик на хостах с БД, плюс метрик самого приложения на первое время должно хватать.

Заключение

Несколько важных моментов:

  • CollectD ставится на все машины, а StatsD вы скорее всего захотите сделать централизованным, по крайне мере, для одного приложения и окружения;
  • Если вы создали метрику test, а затем test.v2, это не прокатит, потому что в интерфейсе Graphite, в дереве слева, test должна из «значения» стать «каталогом». Так что, продумывайте заранее имена метрик или будьте готовы потом руками их удалять;
  • Учтите, что место на диске под каждую метрику выделяется один раз и после этого не меняется. Если вы решите, например, что хотите хранить метрики подольше, придется их удалять;
  • В реальной, боевой системе наружу должен торчать только 80-ый порт для Graphite, и то не факт. Все остальное должно быть доступно только во внутренней сети. Как ранее уже отмечалось, это все выходит за рамки заметки;
  • Шардирование и отказоустойчивость метрик также выходят за рамки данного поста. Но там вроде нет ничего сложного, см комментарии в файле /etc/carbon/carbon.conf. В принципе, в любом нормальном приложении проблемы на стороне метрик не должны затрагивать пользователей. Поэтому, если Graphite или StatsD упадет, вы просто потеряете кусочки графиков, ничего страшного. Поэтому не факт, что метрикам нужна отказоусточивость. А шардировать можно по проектам или окружениям. Так что, во многих случаях описанной схемы хватит;
  • Как по мне, в Graphite вполне себе нормальные дашборды, но кому-то больше нравятся разные альтернативные веб-интерфейсы, например, Grafana, Giraffe или Graphene;

А куда вы пишите метрики и через что предпочитаете их смотреть?

Дополнение: Вас также могут заинтересовать статьи Простой пример записи метрик в StatsD на Scala и Устанавливаем связку из Prometheus и Grafana.

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.