Збір та візуалізація метрик програми в Graphite і Graph-Explorer

Часто виникає необхідність відстежувати різні параметри роботи програми/сервісу. Наприклад, інтерес представляє кількість запитів в секунду, середній час відповіді сервера, кількість відповідей сервера з різним 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, то можете пропустити цей крок і налаштувати потрібну версію на свій розсуд.

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.

Замість висновку
Вистачить жити розробляти з закритими очима, %habrauser%! Розгортайте системи збору метрик та діліться цікавими графіками з ваших проектів! Спасибі за увагу!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.