Зачастую возникает необходимость отслеживать различные параметры работы приложения/сервиса. Например, интерес представляет количество запросов в секунду, среднее время ответа сервера, количество ответов сервера с различным HTTP-статусом (технические метрики), количество регистраций пользователей в час, количество платежных транзакций в минуту (бизнес-метрики) и пр. Без системы сбора метрик разработка и сопровождение продукта происходит практически вслепую.
Данная статья является руководством по настройке системы сбора и анализа метрик приложения на базе Graphite и vimeo/graph-explorer.
Система сбора метрик не является монолитом. При ее развертывании приходится иметь дело со значительным числом компонентов, каждый из которых каким-то образом взаимодействует с остальными, имеет свой конфигурационный файл и неповторимый способ запуска. Даже Graphite, сам по себе, состоит минимум из трех подсисем — демон сбора метрик (carbon), БД с метриками (whisper и др.) и веб-приложение для визуализации. Когда же необходимо добавить поддержку graph-explorer, все становится еще интереснее. Каждая из подсистем имеет свою обособленную документацию, но нигде нет документа, описывающего картину вцелом.
Метрика — это последовательность (числовых) значений во времени. Очень простая вещь по сути. Фактически есть некоторый строковый ключ и соответствующий ему ряд (sample1, time1), (sample2, time2),… Типичным для Graphite способом именования метрик является разделение строковых ключей на части при помощи символа ".", например, stats.web.request.GET.time. Graphite позволяет группировать метрики с общим префиксом, используя символ "*" при построении графиков. Очевидно, что это далеко не самый гибкий способ работы с ключами. Если понадобится добавить еще какой-либо компонент к ключу, это может поломать построение графиков. Например, приведение ключа из примера выше к виду stats.web.server1.request.GET.time нарушит общий префикс для исторических данных. Вторым существенным недостатком такого именования метрик является потенциальная неоднозначность их трактования. Куда более самодостаточными были бы метрики, обладающие ключами вида service=web server=server1 what=request_time unit=ms с дальнейшей возможностью построения комбинированных графиков по общим тегам, а не только общим префиксам. К счастью, ребята из vimeo придумали metrics 2.0 и запили свой graph-explorer, работающий поверх graphite. Основная идея — это логическое представление метрик, как сущностей с набором пар тег-значение. Каждая метрика в формате 2.0 все равно в конечном итоге преобразуется в обычный строковый ключ, разделенный точками и попадает в carbon, но предварительно в отдельном хранилище создается «индекс», хранящий информацию о соответствии этих ключей и пар тег-значение. Таким образом, пользуясь информацией из индексов, graph-explorer и реализует комбинирование различных метрик на одном графике.
В общем виде система сбора метрик может быть представлена следующий диаграммой:
Таким образом, приложение (веб-сервис, демон, etc.), написанное не любом языке, через некоторый интерфейс (прослойку) отправляет метрики в коллектор, коллектор их частично агрегирует, вычисляет частоту обновления (опционально) и раз в некоторый промежуток времени отправляет их в carbon, который постепенно складывает их в хранилище. Веб-приложение тянет данные из хранилища (и частично из самого carbon) и строит для нас графики. Демон carbon на самом деле — это целых 3 демона: carbon-cache, carbon-relay и carbon-aggregator. В простейшем случае можно использовать carbon-cache. Реализацию carbon-relay можно использовать с целью шардирования (распределение нагрузки между несколькими carbon-cache) или реплицирования (отправка одних и тех же метрик на несолько carbon-cache). Демон carbon-aggregator умеет выполнять промежуточную обработку метрик перед отправкой их в хранилище. Данные метрик в carbon могут быть переданы по одному из двух форматов: в plain text (т.н. line protocol) на порт 2003 и сериализованные в pickle на порт 2004. При этом carbon-relay на выход отдает данные только в pickle (важно!).
Надстройка graph-explorer добавляет еще одно хранилище для т.н. индексов метрик. В качестве такого хранилища используется elastic search. Очевидно, что в каком-то месте представленной на диаграмме системы необходимо добавить звено, которое будет осуществлять «индексацию» метрик. Таким звеном является carbon-tagger. В итоге, система принимает следующий вид:
Далее идет уже конкретика, в вашем случае возможно какой-то из компонентов будет заменен на другое решение.
Система предназначена для сбора именно метрик 2.0 с последующим их использованием в graph-explorer.
Установка будет происходить в директорию /opt/graphite, являющуюся директорией по умолчанию. Часть компонентов написана на Go, так что его также придется предварительно установить и прописать соответствующие переменные окружения. Установка подразумевает, что Go в системе один. Если у вас установлено несколько версий Go, то этот шаг можете пропустить и настроить нужную версию по своему усмотрению.
Также еще один важный момент при развертывании системы — внимательно следить за портами. Компонентов много, каждый использует по несколько портов и легко ошибиться, кого с кем связывать. Плюс большинство компонентов не обладает каким-либо встроенным механизмом авторизации и при этом по умолчанию слушают интерфейс 0.0.0.0. Поэтому настоятельно рекомендую везде, где это возможно менять интерфейс на локальный и закрывать все порты на сервере через iptables.
В качестве клиента, занимающегося отправкой метрик из приложения, выбран наиболее популярный statsd. Его реализация бесконечно проста. Фактически, это отправка текстовых данных на указанный UDP/TCP-порт с добавлением минимальных протокольных служебных данных.
Пример использования в коде приложения:
В «классической» схеме установки graphite зачастую в качестве промежуточного коллектора используется statsd. В нашем случае использован statsdaemon, так как он из коробки умеет работать с метриками 2.0, при этом сохраняя обратную совместимость с протоколом statsd. Он написан на Go и его установка чрезвычайно проста (осторожно, сейчас в README.md досадная ошибка в команде установки):
После этого в директории /opt/go/bin должен появиться исполняемый файл statsdaemon. Настройки этого демона достаточно просты:
Запуск statsdaemon:
На этом этапе уже можно запустить statsdaemon и послать в него несколько пакетов из приложения с помощью statsd-клиент. Вывод в консоль будет говорить сам за себя.
Актуальное руководство по установке находится здесь. Установку лучше проводить внутри virtual environment, располагающегося в /opt/graphite.
После установки, graphite будет располагаться в /opt/graphite. Далее необходимо выполнить его конфигурацию. Пример файлов с настройками находятся в /opt/graphite/conf. Минимум, что необходимо сделать, это создать файл настроек carbon и whisper.
Далее необходимо запустить carbon-cache:
И graphite webapp с помощью uwsgi + какой-либо web-сервер (например, nginx):
Настройки nginx:
Остается установить только carbon-tagger (именно он заполняет базу индексов для graph-explorer) и настроить дублирующую отправку метрик в carbon-cache и carbon-tagger при помощи carbon-relay. Но к сожалению, carbon-tagger не умеет работать по протоколу pickle, а carbon-relay отдает данные только в таком формате. Поэтому необходимо установить drop-in замену carbon-relay от vimeo — carbon-relay-ng:
Демон carbon-tagger написан на Go и занимается отправкой индексов метрик в Elastic Search для последующего их использования в graph-explorer. Прежде всего на сервере необходимо установить java и Elastic Search. Установка carbon-tagger:
Запуск carbon-tagger:
И наконец, установка гвоздя программы:
Запуск graph-explorer:
После этого веб-интерфейс graph-explorer будет доступен по адресу metrics.yourproject.net.
Хватитжить разрабатывать с закрытыми глазами, %habrauser%! Разворачивайте системы сбора метрик и делитесь занимательными графиками из ваших проектов! Спасибо за внимание!
Данная статья является руководством по настройке системы сбора и анализа метрик приложения на базе Graphite и vimeo/graph-explorer.
Мотивация
Система сбора метрик не является монолитом. При ее развертывании приходится иметь дело со значительным числом компонентов, каждый из которых каким-то образом взаимодействует с остальными, имеет свой конфигурационный файл и неповторимый способ запуска. Даже Graphite, сам по себе, состоит минимум из трех подсисем — демон сбора метрик (carbon), БД с метриками (whisper и др.) и веб-приложение для визуализации. Когда же необходимо добавить поддержку graph-explorer, все становится еще интереснее. Каждая из подсистем имеет свою обособленную документацию, но нигде нет документа, описывающего картину вцелом.
Метрики
Метрика — это последовательность (числовых) значений во времени. Очень простая вещь по сути. Фактически есть некоторый строковый ключ и соответствующий ему ряд (sample1, time1), (sample2, time2),… Типичным для Graphite способом именования метрик является разделение строковых ключей на части при помощи символа ".", например, stats.web.request.GET.time. Graphite позволяет группировать метрики с общим префиксом, используя символ "*" при построении графиков. Очевидно, что это далеко не самый гибкий способ работы с ключами. Если понадобится добавить еще какой-либо компонент к ключу, это может поломать построение графиков. Например, приведение ключа из примера выше к виду stats.web.server1.request.GET.time нарушит общий префикс для исторических данных. Вторым существенным недостатком такого именования метрик является потенциальная неоднозначность их трактования. Куда более самодостаточными были бы метрики, обладающие ключами вида service=web server=server1 what=request_time unit=ms с дальнейшей возможностью построения комбинированных графиков по общим тегам, а не только общим префиксам. К счастью, ребята из vimeo придумали metrics 2.0 и запили свой graph-explorer, работающий поверх graphite. Основная идея — это логическое представление метрик, как сущностей с набором пар тег-значение. Каждая метрика в формате 2.0 все равно в конечном итоге преобразуется в обычный строковый ключ, разделенный точками и попадает в carbon, но предварительно в отдельном хранилище создается «индекс», хранящий информацию о соответствии этих ключей и пар тег-значение. Таким образом, пользуясь информацией из индексов, graph-explorer и реализует комбинирование различных метрик на одном графике.
Общий взгляд
В общем виде система сбора метрик может быть представлена следующий диаграммой:
Таким образом, приложение (веб-сервис, демон, etc.), написанное не любом языке, через некоторый интерфейс (прослойку) отправляет метрики в коллектор, коллектор их частично агрегирует, вычисляет частоту обновления (опционально) и раз в некоторый промежуток времени отправляет их в carbon, который постепенно складывает их в хранилище. Веб-приложение тянет данные из хранилища (и частично из самого carbon) и строит для нас графики. Демон carbon на самом деле — это целых 3 демона: carbon-cache, carbon-relay и carbon-aggregator. В простейшем случае можно использовать carbon-cache. Реализацию carbon-relay можно использовать с целью шардирования (распределение нагрузки между несколькими carbon-cache) или реплицирования (отправка одних и тех же метрик на несолько carbon-cache). Демон carbon-aggregator умеет выполнять промежуточную обработку метрик перед отправкой их в хранилище. Данные метрик в carbon могут быть переданы по одному из двух форматов: в plain text (т.н. line protocol) на порт 2003 и сериализованные в pickle на порт 2004. При этом carbon-relay на выход отдает данные только в pickle (важно!).
Надстройка graph-explorer добавляет еще одно хранилище для т.н. индексов метрик. В качестве такого хранилища используется elastic search. Очевидно, что в каком-то месте представленной на диаграмме системы необходимо добавить звено, которое будет осуществлять «индексацию» метрик. Таким звеном является carbon-tagger. В итоге, система принимает следующий вид:
Стек технологий
Далее идет уже конкретика, в вашем случае возможно какой-то из компонентов будет заменен на другое решение.
- приложение на Python
- statsd клиент для Python
- statsd/statsdaemon
- graphite
- carbon-relay/carbon-relay-ng
- carbon-cache
- whisper
- webapp
- carbon-tagger
- graph-explorer
Система предназначена для сбора именно метрик 2.0 с последующим их использованием в graph-explorer.
Установка
Установка будет происходить в директорию /opt/graphite, являющуюся директорией по умолчанию. Часть компонентов написана на Go, так что его также придется предварительно установить и прописать соответствующие переменные окружения. Установка подразумевает, что Go в системе один. Если у вас установлено несколько версий Go, то этот шаг можете пропустить и настроить нужную версию по своему усмотрению.
cd /opt
wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.4.2.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
echo 'export GOPATH=/opt/go' >> /etc/profile
echo 'export GOBIN="$GOPATH/bin"' >> /etc/profile
echo 'export PATH=$PATH:$GOBIN' >> /etc/profile
source /etc/profile
go env
# Вывод должен содержать следующие строчки
#GOBIN="/opt/go/bin"
#GOPATH="/opt/go"
#GOROOT="/usr/local/go"
Также еще один важный момент при развертывании системы — внимательно следить за портами. Компонентов много, каждый использует по несколько портов и легко ошибиться, кого с кем связывать. Плюс большинство компонентов не обладает каким-либо встроенным механизмом авторизации и при этом по умолчанию слушают интерфейс 0.0.0.0. Поэтому настоятельно рекомендую везде, где это возможно менять интерфейс на локальный и закрывать все порты на сервере через iptables.
statsd python клиент
В качестве клиента, занимающегося отправкой метрик из приложения, выбран наиболее популярный statsd. Его реализация бесконечно проста. Фактически, это отправка текстовых данных на указанный UDP/TCP-порт с добавлением минимальных протокольных служебных данных.
# внутри виртуального окружения приложения
pip install statsd
Пример использования в коде приложения:
import statsd
client = statsd.StatsClient(host='statsdaemon.local', port=8125)
# отправка метрики в metrics 2.0 формате tag_is_value
client.gauge('service_is_myapp.server_is_web1.what_is_http_request.unit_is_ms', <execution_time>)
statsd
В «классической» схеме установки graphite зачастую в качестве промежуточного коллектора используется statsd. В нашем случае использован statsdaemon, так как он из коробки умеет работать с метриками 2.0, при этом сохраняя обратную совместимость с протоколом statsd. Он написан на Go и его установка чрезвычайно проста (осторожно, сейчас в README.md досадная ошибка в команде установки):
go get github.com/Vimeo/statsdaemon/statsdaemon
После этого в директории /opt/go/bin должен появиться исполняемый файл statsdaemon. Настройки этого демона достаточно просты:
statsdaemon.ini
# --- /etc/statsdaemon.ini ---
listen_addr = ":8125" # сюда будет слать метрики statsd-клиент (приложение)
admin_addr = ":8126"
graphite_addr = "carbon.local:2013" # адрес carbon для агрегированных метрик, раз в flush_interval сек
flush_interval = 30
prefix_rates = "stats."
prefix_timers = "stats.timers."
prefix_gauges = "stats.gauges."
percentile_thresholds = "90,75"
max_timers_per_s = 1000
Запуск statsdaemon:
statsdaemon -config_file="/etc/statsdaemon.ini" -debug=true
На этом этапе уже можно запустить statsdaemon и послать в него несколько пакетов из приложения с помощью statsd-клиент. Вывод в консоль будет говорить сам за себя.
Graphite
Актуальное руководство по установке находится здесь. Установку лучше проводить внутри virtual environment, располагающегося в /opt/graphite.
sudo apt-get install python-pip python-dev
pip install pip --upgrade
pip install virtualenv
mkdir /opt/graphite
virtualenv /opt/graphite
cd /opt/graphite
source bin/activate
sudo apt-get install libcairo2 python-cairo libffi-dev # установка нужных для graphite пакетов
pip install https://github.com/graphite-project/ceres/tarball/master
pip install whisper
pip install carbon # pip install carbon --install-option="--prefix=/opt/graphite" --install-option="--install-lib=/opt/graphite/lib"
pip install graphite-web # pip install graphite-web --install-option="--prefix=/opt/graphite" --install-option="--install-lib=/opt/graphite/webapp"
# нужно для Graphite WebApp
pip install uwsgi
pip install django
pip install cairocffi
pip install django-tagging
# инициализация webapp
(cd /opt/graphite/webapp/graphite; python manage.py syncdb)
После установки, graphite будет располагаться в /opt/graphite. Далее необходимо выполнить его конфигурацию. Пример файлов с настройками находятся в /opt/graphite/conf. Минимум, что необходимо сделать, это создать файл настроек carbon и whisper.
cp /opt/graphite/conf/carbon.conf.example /opt/graphite/conf/carbon.conf
# carbon.conf содержит настройки для carbon-cache, carbon-relay и carbon-aggregator.
# Необходимо настроить как минимум следующие значения в секции carbon-cache:
# LINE_RECEIVER_INTERFACE = 127.0.0.1
# LINE_RECEIVER_PORT = 2003
cp /opt/graphite/conf/storage-schemas.conf.example /opt/graphite/storage-schemas.conf
# storage-schemas.conf содержит настройки whisper, который по сути - fixed-size db.
# Аллокация места под метрики происходит 1 раз, поэтому нужно явно задать (по
# ключу метрики), с какой частотой дескритизации и за какой период хранить данные.
...
Далее необходимо запустить carbon-cache:
carbon-cache.py --conf=conf/carbon.conf start # --debug
tail -f /opt/graphite/storage/log/carbon-cache/carbon-cache-a/*.log
И graphite webapp с помощью uwsgi + какой-либо web-сервер (например, nginx):
cp /opt/graphite/webapp/graphite/local_settings.py.example /opt/graphite/webapp/graphite/local_settings.py
# в local_settings.py необходимо изменить SECRET_KEY и TIME_ZONE.
/opt/graphite/bin/uwsgi --socket localhost:6001 --master --processes 4 --home /opt/graphite --pythonpath /opt/graphite/webapp/graphite --wsgi-file=/opt/graphite/conf/graphite.wsgi.example --daemonize=/var/log/graphite-uwsgi.log
Настройки nginx:
graphite.conf
upstream graphite_upstream {
server 127.0.0.1:6001;
}
server {
listen 8085;
server_name graphite.local;
location / {
include uwsgi_params;
uwsgi_pass graphite_upstream;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'origin, authorization, accept';
add_header 'Access-Control-Allow-Credentials' 'true';
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
Остается установить только carbon-tagger (именно он заполняет базу индексов для graph-explorer) и настроить дублирующую отправку метрик в carbon-cache и carbon-tagger при помощи carbon-relay. Но к сожалению, carbon-tagger не умеет работать по протоколу pickle, а carbon-relay отдает данные только в таком формате. Поэтому необходимо установить drop-in замену carbon-relay от vimeo — carbon-relay-ng:
go get -d github.com/graphite-ng/carbon-relay-ng
go get github.com/jteeuwen/go-bindata/...
cd "/opt/go/src/github.com/graphite-ng/carbon-relay-ng"
make
cp carbon-relay-ng /opt/go/bin/carbon-relay-ng
touch /opt/graphite/conf/carbon-relay-ng.ini
cd /opt/graphite
carbon-relay-ng conf/carbon-relay-ng.ini
carbon-relay-ng.ini
instance = "default"
listen_addr = "127.0.0.1:2013"
admin_addr = "127.0.0.1:2014"
http_addr = "127.0.0.1:8081"
spool_dir = "spool"
log_level = "notice"
bad_metrics_max_age = "24h"
init = [
'addRoute sendAllMatch carbon-default 127.0.0.1:2003 spool=true pickle=false', # отправляем все в carbon-cache
'addRoute sendAllMatch carbon-tagger 127.0.0.1:2023 spool=true pickle=false' # отправляем все в carbon-tagger
]
[instrumentation]
graphite_addr = ""
graphite_interval = 1000
carbon-tagger
Демон carbon-tagger написан на Go и занимается отправкой индексов метрик в Elastic Search для последующего их использования в graph-explorer. Прежде всего на сервере необходимо установить java и Elastic Search. Установка carbon-tagger:
go get github.com/Vimeo/carbon-tagger
go get github.com/mjibson/party
go build github.com/Vimeo/carbon-tagger
carbon-tagger.conf
[in]
port = 2023 # сюда присылает метрики carbon-relay-ng
[elasticsearch]
host = "esearch.local"
port = 9200
index = "graphite_metrics2"
flush_interval = 2
max_backlog = 10000
max_pending = 5000
[stats]
host = "localhost"
port = 2003 # сюда carbon-tagger будет отправлять собственные внутренние метрики (не метрики приложения)
id = "default"
flush_interval = 10
http_addr = "127.0.0.1:8123"
Запуск carbon-tagger:
(cd /opt/go/src/github.com/Vimeo/carbon-tagger/; ./recreate_index.sh) # инициализация индексов в ES
carbon-tagger -config="/opt/graphite/conf/carbon-tagger.conf" -verbose=true
graph-explorer
И наконец, установка гвоздя программы:
pip install graph-explorer
graph-explorer.conf
[graph_explorer]
listen_host = 127.0.0.1 # локальный адрес, чтобы добавить HTTP Basic Auth через nginx
listen_port = 8080
filename_metrics = metrics.json
log_file = /var/log/graph-explorer/graph-explorer.log
[graphite]
url_server = http://localhost
url_client = http://graphite.local:8085 # адрес graphite webapp
nginx/graph-explorer.conf
server {
listen 80;
server_name metrics.yourproject.net;
location / {
auth_basic "Who are you?";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:8080;
}
}
Запуск graph-explorer:
mkdir /var/log/graph-explorer
run_graph_explorer.py /opt/graphite/conf/graph_explorer.conf
После этого веб-интерфейс graph-explorer будет доступен по адресу metrics.yourproject.net.
Вместо заключения
Хватит
Комментарии (10)
rudenkovk
22.06.2015 09:59+1Все отлично, кроме работы с большим количеством метрик. Когда количество приходящих метрик в пару-тройку десятков гигабайт в день, карбон будет просто «прекрасен», не говоря уж о отказоустойчивости.
И тут к нам на помощ приходят cyanite и cassandra. Или же influxdb.
А так же есть прекрасная морда для метрик Grafana.
monolithed
Все в Graphite хорошо, кроме кошмарной установки.