hello, world
Первый пост будет про про TICK DSL с простым примером. На момент написания (февраль 2017го) актуальная версия 1.2, приведённый же пример запускался на 1.0, разница между ними минимальна. Чейнджлог. К сожалению, примеры с алёртами найти непросто, поэтому хорошей идеей будет добавить также и их.
TICK - это DSL для описания цепочки преобразований неких данных, для получения определённой аналитики. Интепретатор языка запускается в сервисе Kapacitor, который представляет собой, цитируя оф. документацию, data processing framework and ETL jobs launcher. Данный сервис является частью стека Telegraf+InfluxDB но, в отличие от первых двух, на данный момент Kapacitor не умеет работать ни с каким другим хранилищем, кроме InfluxDB. Логика работы следующая - Kapacitor подписывается (subscription) на обновления в базе InfluxDB и подаёт эти данные на вход интепретатору TICK-скриптов. Загрузка данных из InfluxDB может происходить как как батчами так и стримом. Соответственно, TICK скрипты будут работать именно с этими порциями данных. Как именно работать - решает пользователь, например, можно придумать логику, по которой в загруженной порции данных выявляется некоторая аномалия, о которой необходимо сообщить операторам с помощью алёрта. Согласно рекламе на оф. сайте, одной из сильных сторон Kapacitor является как раз возможность реализовать логику по anomaly detection.
Семантически TICK скрипт напоминает Pig Latin из мира Hadoop, здесь тоже присутствует декларативность в описании цепочек преобразований. Типичный TICK скрипт выглядит как длинная цепочка: data flow, агрегации, вычисления. Имеется возможность написать свою UDF как в виде тривиального bash-скрипта, так и Go-бинарника. Результатом работы TICK скрипта является довольно сложный InfluxQL-запрос (аналогично с Pig, где в результатом является MapReduce джоба), вывод которого можно записать рядом в ту же InfluxDB, либо же породить алёрт. Цепочки преобразований образуют пайплайны (pipelines). В терминологии TICK пайплайн это: - множество вершин (nodes), которые являются логическими точками процессинга данных - множество рёбер (edges) соединяющих вершины (nodes) Таким образом, пайплайн это буквально граф, причём нужно отметить, что в TICK скриптах пайплайны являются ориентированными ациклическими графами (DAG), а значит в процессе интерпретации TICK скипта будет сохранён порядок преобразований и они не будут происходить по кругу. Вершины (nodes) описывают преобразования с данными. Каждая ноде имеет 2 типа методов: property и chaining. Property методы меняют состояние (state) ноды, но не двигают ход выполнения скрипта по цепочке нод дальше. Chaining методы порождают новую ноду и переключают выполнение скрипта на неё. Согласно оф. документации each edge has a type, этот тип связан с типом нод, которые данная вершина связывает. На практике,это выглядит следущим образом: если первая нода в пайплайне была StreamNode то тип всех edges между порождёнными от неё нодами будет StreamEdge. Если же первая нода была BatchNode, то и тип edges будет BatchEdge. Нужно заметить, что на момент написания нет возможности статически проанализировать написанный TICK скрипт до его фактического запуска Kapacitor'ом, поэтому все ошибки будут обнаружены лишь в рантайме. Примером частой ошибки является вызов chaining method'a, которого нет у данной ноды.
Ниже приведён тривиальный пример алёрта на высокие значения LA (load average). Данные измерений - стандартные, полученные из system input в Telegraf. Дополнительно добавлен тег environment для симуляции реального кейса с несколькими стендами.
// 'в данном примере qa1 это название некоторого стенда, с которого приходит телеметрия'
var load_data_qa1 = stream // 'Вот нода с которой всё начинается'
// 'для удобства можно её закинуть в переменную, чтобы дальше можно было с ней что-то делать'
|from() // 'вызов chaining метода from() - получаем ноду FromNode'
.measurement('system')
.groupBy('host')
.where(lambda: "environment" == 'qa1')
var load_alert_qa1 = load_data_qa1
|alert()
.stateChangesOnly()
.id('{{ index .Tags "host" }}/Load')
.warn(lambda: "load5" > 5)
.crit(lambda: "load5" > 10)
.message('{{ .Level }} alert: {{ .ID }}')
// 'Есть возможность создавать красивые HTML-formatted сообщения'
.details('''
<h1>5 minute load average on {{ index .Tags "host" }} is {{ index .Fields "load5" }}</h1>
<h3>Additional info</h3>
<br>Host: {{ index .Tags "host" }}</br>
<br>Metric: system.load5</br>
<br>Value: {{ index .Fields "load5" }}</br>
<br>Time: {{ .Time }}</br>
<br>--</br>
<br>Our awesome monitoring system</br>
''')
.email()
// 'для сохранения истории сработавших алёртов, можно положить информацию об алёрте в InfluxDB'
load_alert_qa1
|influxDBOut()
.database('my_influx_db')
.retentionPolicy('default')
.measurement('load_alerts')
.precision('m')
.tag('environment', 'qa1')