Trucchi per trasfurmà e metriche in Kapacitor

Hè assai prubabile, oghje nimu ùn si dumanda perchè hè necessariu di cullà metriche di serviziu. U prossimu passu logicu hè di stallà una alerta per e metriche raccolte, chì avviserà ogni deviazione in i dati in i canali convenienti per voi (mail, Slack, Telegram). In u serviziu di prenotazione hotel in linea Ostrovok.ru tutte e metriche di i nostri servizii sò versati in InfluxDB è affissati in Grafana, è l'alerta di basa hè ancu cunfigurata quì. Per i travaglii cum'è "avete bisognu di calculà qualcosa è paragunate cun ella", avemu aduprà Kapacitor.

Trucchi per trasfurmà e metriche in Kapacitor
Kapacitor hè parti di a pila TICK chì pò processà metriche da InfluxDB. Pò cunnette parechje misurazioni inseme (join), calculà qualcosa d'utile da i dati ricevuti, scrivite u risultatu torna à InfluxDB, mandà una alerta à Slack / Telegram / mail.

L'intera pila hè fresca è dettagliata documentazione, ma ci saranu sempre cose utili chì ùn sò micca esplicitamente indicati in i manuali. In questu articulu, aghju decisu di cullà una quantità di tali cunsiglii utili, micca evidenti (a sintassi di basa di TICKscipt hè descritta ccà) è mostra cumu si ponu esse applicati cù un esempiu di risolve unu di i nostri prublemi.

Let's go!

float & int, errori di calculu

Un prublema assolutamente standard, risoltu attraversu caste:

var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))

Utilizendu default ()

Se un tag/campu ùn hè micca cumpletu, l'errori di calculu saranu:

|default()
        .tag('status', 'empty')
        .field('value', 0)

riempie join (internu versus esternu)

Per automaticamente, unisce scarta punti induve ùn ci hè micca dati (internu).
Cù fill('null'), una unione esterna serà realizata, dopu à quale avete bisognu di fà un default () è riempie i valori vacanti:

var data = res1
    |join(res2)
        .as('res1', 'res2)
        .fill('null')
    |default()
        .field('res1.value', 0.0)
        .field('res2.value', 100.0)

Ci hè sempre una sfumatura quì. In l'esempiu supra, se una di e serie (res1 o res2) hè viota, a serie resultanti (dati) serà ancu viota. Ci sò parechji biglietti nantu à questu tema nantu à Github (1633, 1871, 6967) - aspettemu correzioni è soffrenu un pocu.

Utilizà e cundizioni in i calculi (se in lambda)

|eval(lambda: if("value" > 0, true, false)

Ultimi cinque minuti da u pipeline per u periodu

Per esempiu, avete bisognu di paragunà i valori di l'ultimi cinque minuti cù a settimana precedente. Pudete piglià dui batch di dati in dui batch separati o estrae una parte di e dati da un periodu più grande:

 |where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)

Una alternativa per l'ultimi cinque minuti seria di utilizà un BarrierNode, chì taglia i dati prima di l'ora specificata:

|barrier()
        .period(5m)

Esempii di usu di mudelli Go in u messagiu

I mudelli currispondenu à u formatu da u pacchettu testu.mudelluQuì sottu sò qualchi puzziche frequenti.

si-altru

Pudemu e cose in ordine è ùn attivate micca e persone cù u testu una volta di più:

|alert()
    ...
    .message(
        '{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
    )

Dui cifre dopu à u puntu decimale in u messagiu

Migliurà a leghjibilità di u messagiu:

|alert()
    ...
    .message(
        'now value is {{ index .Fields "value" | printf "%0.2f" }}'
    )

Espansione di variabili in u messagiu

Mostremu più infurmazione in u missaghju per risponde à a quistione "Perchè urla"?

var warnAlert = 10
  |alert()
    ...
    .message(
       'Today value less then '+string(warnAlert)+'%'
    )

Identificatore unicu di alerta

Questa hè una cosa necessaria quandu ci hè più di un gruppu in i dati, altrimenti una sola alerta serà generata:

|alert()
      ...
      .id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')

Gestione persunalizata

A larga lista di gestori include exec, chì vi permette di eseguisce u vostru script cù i paràmetri passati (stdin) - creatività è nunda di più!

Unu di i nostri costumi hè un picculu script Python per mandà notificazioni à slack.
À u principiu, avemu vulsutu mandà una stampa grafana prutetta da l'autorizazione in un missaghju. Dopu, scrivite OK in u filu à l'alerta previa da u stessu gruppu, è micca cum'è un missaghju separatu. Un pocu dopu - aghjunghje à u messagiu l'errore più cumuni in l'ultimi X minuti.

Un tema separatu hè a cumunicazione cù altri servizii è qualsiasi azzione iniziata da una alerta (solu se u vostru monitoraghju funziona abbastanza bè).
Un esempiu di una descrizzione di gestore, induve slack_handler.py hè u nostru script auto-scrittu:

topic: slack_graph
id: slack_graph.alert
match: level() != INFO AND changed() == TRUE
kind: exec
options:
  prog: /sbin/slack_handler.py
  args: ["-c", "CHANNELID", "--graph", "--search"]

Cumu debug?

Opzione cù output log

|log()
      .level("error")
      .prefix("something")

Watch (cli): kapacitor -url host-or-ip: 9092 logs lvl = errore

Opzione cù httpOut

Mostra i dati in u pipeline attuale:

|httpOut('something')

Fighjate (ottene): host-or-ip:9092/kapacitor/v1/tasks/task_name/qualcosa

Schema di esecuzione

  • Ogni compitu torna un arbre di esecuzione cù numeri utili in u furmatu graphviz.
  • Pigliate un bloccu Islande.
  • Incolla in u visore, piacè.

Induve altru pudete piglià un rake?

timestamp in influxdb in scrittura

Per esempiu, avemu stabilitu una alerta per a summa di e dumande per ora (groupBy(1h)) è vulete registrà l'alerta chì hè accaduta in influxdb (per vede bè u fattu di u prublema nantu à u graficu in grafana).

influxDBOut() scriverà u valore di u tempu da l'alerta à u timestamp; per quessa, u puntu nantu à u graficu serà scrittu prima / dopu chì l'alerta hè ghjunta.

Quandu l'accuratezza hè necessaria: travagliemu intornu à stu prublema chjamendu un gestore persunalizatu, chì scriverà dati à influxdb cù u timestamp attuale.

docker, custruisce è implementazione

À l'iniziu, kapacitor pò carricà i travaglii, mudelli è handlers da u repertoriu specificatu in a cunfigurazione in u bloccu [load].

Per creà currettamente un compitu, avete bisognu di e seguenti cose:

  1. File name - allargatu in script id/name
  2. Tipu - flussu / batch
  3. dbrp - keyword per indicà quale basa di dati + pulitica in cui u script corre (dbrp "supplier." "autogen")

Se un batch task ùn cuntene micca una linea cù dbrp, tuttu u serviziu ricusarà di principià è onestamente scriverà nantu à questu in u logu.

In chronograf, à u cuntrariu, sta linea ùn deve esse micca; ùn hè micca accettata per l'interfaccia è genera un errore.

Hack quandu custruisce un containeru: Dockerfile esce cù -1 s'ellu ci sò linii cù //.+dbrp, chì vi permetterà di capisce immediatamente u mutivu di u fallimentu quandu assemble a custruzzione.

unisce unu à parechji

Esempiu di travagliu: avete bisognu di piglià u percentile 95 di u tempu di u serviziu di u serviziu per una settimana, paragunate ogni minutu di l'ultimi 10 cù questu valore.

Ùn pudete micca fà un unitu unu à parechji, l'ultimu / mediu / mediana annantu à un gruppu di punti trasforma u node in un flussu, l'errore "ùn pò micca aghjunghje i bordi discordati di u zitellu: batch -> stream" serà tornatu.

U risultatu di un batch, cum'è una variabile in una espressione lambda, ùn hè ancu micca sustituitu.

Ci hè una opzione per salvà i numeri necessarii da u primu batch à un schedariu via udf è carricà stu schedariu via sideload.

Chì avemu risolve cù questu?

Avemu circa 100 fornitori di hotel, ognunu di elli pò avè parechje cunnessione, chjamemu un canale. Ci sò circa 300 di sti canali, ognuna di i canali pò cascà. Di tutte e metriche registrate, monitoremu a rata di errore (richieste è errori).

Perchè micca grafana?

L'alerte d'errore cunfigurate in Grafana anu parechji svantaghji. Certi sò critichi, alcuni pudete chjude l'ochji, secondu a situazione.

Grafana ùn sapi micca cumu calculà trà e misurazioni + alerting, ma avemu bisognu di una tarifa (requests-errors) / dumande.

L'errori parenu brutti:

Trucchi per trasfurmà e metriche in Kapacitor

È menu male quandu si vede cù richieste di successu:

Trucchi per trasfurmà e metriche in Kapacitor

Va bè, pudemu pre-calculà a tarifa in u serviziu prima di grafana, è in certi casi questu travaglià. Ma micca in u nostru, perchè... per ogni canale u so propiu rapportu hè cunsideratu "normale", è l'alerte funzionanu secondu i valori statichi (guardemu cù i nostri ochji, cambiate s'ellu ci sò avvisi frequenti).

Quessi sò esempi di "normali" per diversi canali:

Trucchi per trasfurmà e metriche in Kapacitor

Trucchi per trasfurmà e metriche in Kapacitor

Ignuramu u puntu precedente è assumemu chì a stampa "normale" hè simile per tutti i fornituri. Avà tuttu hè bè, è pudemu passà cù alerti in grafana?
Pudemu, ma ùn vulemu veramente, perchè avemu da sceglie una di l'opzioni:
a) fate assai grafici per ogni canale separatamente (è accumpagnà dolorosamente)
b) lasciate un graficu cù tutti i canali (è perdete in e linee culurite è avvisi persunalizati)

Trucchi per trasfurmà e metriche in Kapacitor

Cumu avete fattu ?

In novu, ci hè un bon esempiu di partenza in a documentazione (Càlculu di i tassi trà e serie unite), pò esse peeked at o pigliatu cum'è una basa in prublemi simili.

Ciò chì avemu fattu à a fine:

  • unisce duie serie in uni pochi d'ore, raggruppendu per canali;
  • compie a serie per gruppu s'ellu ùn ci era micca dati;
  • paragunate a mediana di l'ultimi 10 minuti cù e dati previ;
  • gridemu si truvamu qualcosa;
  • scrivimu i tassi calculati è alerti chì sò accaduti in influxdb;
  • mandate un missaghju utile à slack.

In u mo parè, avemu riesciutu à ottene tuttu ciò chì vulemu ottene à a fine (è ancu un pocu di più cù i manichi persunalizati) u più bellu pussibule.

Pudete vede github.com esempiu codice и circuit minimale (graphviz) u script risultatu.

Un esempiu di u codice risultatu:

dbrp "supplier"."autogen"
var name = 'requests.rate'
var grafana_dash = 'pczpmYZWU/mydashboard'
var grafana_panel = '26'
var period = 8h
var todayPeriod = 10m
var every = 1m
var warnAlert = 15
var warnReset = 5
var reqQuery = 'SELECT sum("count") AS value FROM "supplier"."autogen"."requests"'
var errQuery = 'SELECT sum("count") AS value FROM "supplier"."autogen"."errors"'

var prevErr = batch
    |query(errQuery)
        .period(period)
        .every(every)
        .groupBy(1m, 'channel', 'supplier')

var prevReq = batch
    |query(reqQuery)
        .period(period)
        .every(every)
        .groupBy(1m, 'channel', 'supplier')

var rates = prevReq
    |join(prevErr)
        .as('req', 'err')
        .tolerance(1m)
        .fill('null')
    // заполняем значения нулями, если их не было
    |default()
        .field('err.value', 0.0)
        .field('req.value', 0.0)
    // if в lambda: считаем рейт, только если ошибки были
    |eval(lambda: if("err.value" > 0, 100.0 * (float("req.value") - float("err.value")) / float("req.value"), 100.0))
        .as('rate')

// записываем посчитанные значения в инфлюкс
rates
    |influxDBOut()
        .quiet()
        .create()
        .database('kapacitor')
        .retentionPolicy('autogen')
        .measurement('rates')

// выбираем данные за последние 10 минут, считаем медиану
var todayRate = rates
    |where(lambda: duration((unixNano(now()) - unixNano("time")) / 1000, 1u) < todayPeriod)
    |median('rate')
        .as('median')

var prevRate = rates
    |median('rate')
        .as('median')

var joined = todayRate
    |join(prevRate)
        .as('today', 'prev')
    |httpOut('join')

var trigger = joined
    |alert()
        .warn(lambda: ("prev.median" - "today.median") > warnAlert)
        .warnReset(lambda: ("prev.median" - "today.median") < warnReset)
        .flapping(0.25, 0.5)
        .stateChangesOnly()
        // собираем в message ссылку на график дашборда графаны
        .message(
            '{{ .Level }}: {{ index .Tags "channel" }} err/req ratio ({{ index .Tags "supplier" }})
{{ if eq .Level "OK" }}It is ok now{{ else }}
'+string(todayPeriod)+' median is {{ index .Fields "today.median" | printf "%0.2f" }}%, by previous '+string(period)+' is {{ index .Fields "prev.median" | printf "%0.2f" }}%{{ end }}
http://grafana.ostrovok.in/d/'+string(grafana_dash)+
'?var-supplier={{ index .Tags "supplier" }}&var-channel={{ index .Tags "channel" }}&panelId='+string(grafana_panel)+'&fullscreen&tz=UTC%2B03%3A00'
        )
        .id('{{ index .Tags "name" }}/{{ index .Tags "channel" }}')
        .levelTag('level')
        .messageField('message')
        .durationField('duration')
        .topic('slack_graph')

// "today.median" дублируем как "value", также пишем в инфлюкс остальные филды алерта (keep)
trigger
    |eval(lambda: "today.median")
        .as('value')
        .keep()
    |influxDBOut()
        .quiet()
        .create()
        .database('kapacitor')
        .retentionPolicy('autogen')
        .measurement('alerts')
        .tag('alertName', name)

Chì ghjè a cunclusione ?

Kapacitor hè grande à realizà avvisi di monitoraghju cù una mansa di raggruppamenti, eseguisce calculi supplementari basati nantu à metriche già registrate, eseguendu azioni persunalizate è eseguendu scripts (udf).

A barriera à l'ingressu ùn hè micca assai alta - pruvate se grafana o altre arnesi ùn satisfanu micca i vostri desideri.

Source: www.habr.com

Add a comment