Не знаю, совпадение это, или нет, но обычно самые странные скачки в метриках приложения происходят сразу же после того, как его обновили. Это настолько часто происходит, что на проблемных серверах я иду в историю обновлений раньше, чем в логи. Поэтому хранить такие события рядом в метриками было бы очень удобно.
Предположим, наши метрики лежат в Graphite. Как бы мы хранили в нём еще и события?
События в Graphite
Оказывается, Графит умеет работать с событиями прямо из коробки. В его понимании событие — это запись о каком-то свершившемся факте. Например, о релизе или апдэйте.
Есть два способа, как его туда добавить:
- через UI,
- программно, через JSON API.
Создаём событие через UI
Самый простой способ создать событие — это, конечно, через UI. В Graphite-web на домашней странице в верхнем правом углу есть ссылка ‘events’, которая ведет на страницу очень сомнительной эстетики:
Там всего несколько полей, из которых самые важные, как мне кажется, дата и тэги. С датой и так всё понятно, а вот тэги — это основной способ искать события в дальнейшем, так на этом этапе их лучше не пропускать.
После того как форма заполнена, нажимаем ‘save’ и любуемся новым событием в списке. Рисовать мы его будем немного позже.
Создаём событие через API
Самый практичный способ создать событие — программно, через JSON API. Не нанимать же человека только для того, чтобы он руками вводил новое событие каждые раз, когда мы выкатываем релиз. Это же будущее, у нас есть машины для этого.
Графитовский JSON API прекрасен в своей простоте. Каждое поле из формы можно передать в виде свойства JSON объекта и отправить в Graphite через тот же curl
:
1 2 |
curl -X POST http://localhost/events/ \ -d '{"what": "Service patch", "tags": "update patch", "date": '`date +%s`', "data": "Yet another service update. v2.1"}' |
Кстати, так как устанавливать свежий Graphite из сырцов — тот еще геморрой, я пошёл простым путём и взял более старую версию из apt-get
. Но они отличаются. Например, в свежих версиях тэги события передаются как массив строк, а не как строка с пробелами. Да и стили немного поменялись.
А теперь мы можем убедиться, что новое событие действительно добавилось:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
curl -s http://localhost/events/get_data | json_pp #[ # { # "tags" : "update", # "what" : "Service update", # "id" : 14, # "when" : 1485839100, # "data" : "From v1.0 to v2.0" # }, # { # "data" : "Yet another service update. v2.1", # "when" : 1485839795, # "id" : 15, # "what" : "Service patch", # "tags" : "update" # } #] |
Рисуем события в Graphite
Отрисовка графитовских событий не подпадает под категорию интуитивно понятных вещей, так что вот маленькая инструкция. На домашней странице Графита, прямо под графиком, есть кнопка ‘graph data’. Она откроет список рядов для отрисовки, в который можно добавить drawAsInfinite(events('*'))
, и наслаждаться эффектом:
Функция drawAsInfinite
нужна для того, чтобы события отрисовывались как вертикальные линии, а не точки со значением 1. events
же возвращает список событий. В неё можно передать список тэгов в качестве фильтра, или *
, если нам нужны все.
Кстати, событиям можно дать более вменяемое имя при помощи функции alias
, например: alias(drawAsInfinite(events('upgrade')), 'Upgrade')
. И да, можно добавить какой-нибудь ряд с метриками для пущего эффекта:
Выглядит внушительно.
Рисуем события в Grafana.
C Grafana удобно создавать дашборды для различных источников метрик, в том числе и для Graphite. Графитовские события она тоже понимает, и их можно подключить в настройках дашборда через раздел ‘Annotations’:
Добавить события очень просто. Вводим тэги нужных событий, выбираем имя и минут 5-15 эксперементируем с цветом линий:
В результате получается такая красота:
Заключение
Хотя собирать метрики приложения — очень полезное занятие, оно будет ещё полезнее, если вместе с ними собирать и события, которые на эти метрики могли повлиять. Релизы, патчи, обновления железа, люди, пытающиеся починить продакшен сервер в середине рабочего дня — всё это скорее всего отразится на графиках, поэтому такую информацию лучше держать на одном экране.