Truuks vir die hantering van statistieke in Kapasitor

Heel waarskynlik, vandag het niemand 'n vraag hoekom dit nodig is om diensstatistieke te versamel nie. Die volgende logiese stap is om 'n waarskuwing vir die versamelde maatstawwe op te stel, wat jou in kennis stel van enige afwykings in die data na kanale wat vir jou gerieflik is (pos, Slack, Telegram). Aanlyn hotelbesprekingsdiens Ostrovok.ru alle maatstawwe van ons dienste word in InfluxDB gegooi en in Grafana vertoon, die basiese waarskuwing word ook daar gekonfigureer. Vir take soos "jy moet iets bereken en daarmee vergelyk", gebruik ons ​​Kapacitor.

Truuks vir die hantering van statistieke in Kapasitor
Kapasitor is deel van die TICK-stapel wat metrieke van InfluxDB kan hanteer. Hy kan verskeie metings aan mekaar koppel (join), iets nuttigs uit die ontvangde data bereken, die resultaat terugskryf na InfluxDB, 'n waarskuwing na Slack/Telegram/pos stuur.

Die hele stapel het 'n koel en gedetailleerde dokumentasie, maar daar sal altyd nuttige dinge wees wat nie uitdruklik in die handleidings aangedui word nie. In hierdie artikel het ek besluit om 'n aantal sulke nuttige nie-vanselfsprekende wenke te versamel (die hoofsintaksis van TICKscipt word beskryf hier) en wys hoe hulle toegepas kan word deur die voorbeeld van die oplossing van een van ons probleme te gebruik.

Kom ons gaan!

float & int, berekeningsfoute

Absoluut standaard probleem, opgelos deur rolverdeling:

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

Gebruik verstek()

As die merker/veld leeg is, sal foute in die berekeninge voorkom:

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

vul in join (binne vs buitenste)

By verstek sal join punte weggooi waar daar geen data is nie (binne).
Met fill('null') sal 'n outer join uitgevoer word, waarna jy 'n default() moet doen en die leë waardes moet invul:

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

Hier is nog 'n nuanse. As in die voorbeeld hierbo een van die reekse (res1 of res2) leeg is, sal die finale reeks (data) ook leeg wees. Daar is verskeie github-kaartjies oor hierdie onderwerp (1633, 1871, 6967) - ons wag vir regstellings en ly 'n bietjie.

Gebruik voorwaardes in berekeninge (indien in lambda)

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

Die laaste vyf minute van die pyplyn vir die tydperk

Byvoorbeeld, jy moet die waardes van die laaste vyf minute met die vorige week vergelyk. Jy kan twee bondels data in twee afsonderlike bondels neem of 'n deel van die data uit 'n groter tydperk uittrek:

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

'n Alternatief vir die laaste vyf minute sou wees om die BarrierNode-nodus te gebruik, wat die data voor die gespesifiseerde tyd afsny:

|barrier()
        .period(5m)

Voorbeelde van die gebruik van Go se sjablone in boodskap

Sjablone pas by die formaat van die pakket teks.sjabloon, hieronder is 'n paar algemene probleme.

as-anders

Om dinge in orde te stel, nie weer mense met teks te aktiveer nie:

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

Twee syfers na die desimale punt in boodskap

Verbetering van die leesbaarheid van die boodskap:

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

Uitbreidende veranderlikes in boodskap

Ons vertoon meer inligting in die boodskap om die vraag "Hoekom skree dit" te beantwoord?

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

Waarskuwing unieke ID

'n Noodsaaklike ding wanneer daar meer as een groep in die data is, anders sal net een waarskuwing gegenereer word:

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

Pasgemaakte hanteerder's

Daar is 'n uitvoerende hoof in die groot lys hanteerders, wat jou toelaat om jou skrif uit te voer met die parameters geslaag (stdin) - kreatiwiteit en meer!

Een van ons gewoontes is 'n klein luislangskrif vir die stuur van kennisgewings te slap.
Ons wou eers 'n foto van 'n magtiging-beskermde grafana in 'n boodskap stuur. Na - skryf OK in die draad na die vorige waarskuwing van dieselfde groep, en nie as 'n aparte boodskap nie. Voeg 'n bietjie later die mees algemene fout in die laaste X minute by die boodskap.

'n Aparte onderwerp is kommunikasie met ander dienste en enige aksies wat deur 'n waarskuwing geïnisieer word (slegs as jou monitering goed genoeg werk).
'n Voorbeeld van 'n hanteerderbeskrywing, waar slack_handler.py ons selfgeskrewe skrif is:

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"]

Hoe om te ontfout?

Teken opsie

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

Kyk (cli): kapasitor -url gasheer-of-ip:9092 logs lvl=fout

Variant met httpOut

Wys die data in die huidige pyplyn:

|httpOut('something')

Kyk (kry): gasheer-of-ip:9092/kapacitor/v1/tasks/taaknaam/iets

Uitvoering skema

  • Elke taak gee 'n uitvoeringsboom met nuttige nommers in die formaat terug grafiek.
  • Ons vat 'n blok dot.
  • Plak in die kyker geniet.

Waar anders kan jy 'n hark kry

tydstempel in influxdb op terugskrywing

Ons stel byvoorbeeld 'n waarskuwing op vir die hoeveelheid versoeke per uur (groupBy(1h)) en wil die waarskuwing wat in influxdb gebeur het, aanteken (om die feit pragtig te wys dat daar 'n probleem op die grafiek in grafana is).

influxDBOut() sal die tydwaarde van die waarskuwing na die tydstempel onderskeidelik skryf, die punt op die grafiek sal vroeër/later geskryf word as wat die waarskuwing gekom het.

Wanneer akkuraatheid vereis word: ons omseil hierdie probleem deur 'n pasgemaakte hanteerder te skakel, wat die data na influxdb sal skryf met die huidige tydstempel.

docker, bou en ontplooi

By opstart kan kapasitor take, sjablone en hanteerders laai vanaf die gids gespesifiseer in die konfigurasie, in die [load] blok.

Om 'n taak korrek te skep, het jy die volgende dinge nodig:

  1. Lêernaam - brei uit na ID/naam van die skrif
  2. Tipe - stroom/batch
  3. dbrp - 'n sleutelwoord om te spesifiseer in watter databasis + beleid die skrif werk (dbrp "verskaffer". "autogen")

As daar geen lyn met dbrp in een of ander bondeltaak is nie, sal die hele diens weier om te begin en eerlik daaroor in die log skryf.

In chronograf, inteendeel, hierdie lyn behoort nie te bestaan ​​nie, dit word nie deur die koppelvlak aanvaar nie en gee 'n fout.

Hack wanneer 'n houer gebou word: Dockerfile gaan uit met -1 as daar lyne is met //.+dbrp, wat jou sal toelaat om dadelik die rede vir die mislukking te verstaan ​​wanneer die bou gebou word.

sluit een by baie aan

'n Voorbeeldtaak: jy moet die 95ste persentiel van die dienstyd vir 'n week neem, vergelyk elke minuut van die laaste 10 met hierdie waarde.

Jy kan nie 'n een-tot-veel aansluiting doen nie, laaste/gemiddelde/mediaan oor 'n groep punte verander die nodus in 'n stroom, die fout "kan nie kind wat nie ooreenstem met rande byvoeg nie: bondel -> stroom" sal teruggestuur word.

Die resultaat van 'n bondel, as 'n veranderlike in 'n lambda-uitdrukking, word ook nie vervang nie.

Daar is 'n opsie om die nodige nommers van die eerste bondel na 'n lêer te stoor via udf en hierdie lêer op te laai via sideload.

Wat het ons besluit?

Ons het ongeveer 100 hotelverskaffers, elkeen van hulle kan verskeie verbindings hê, kom ons noem dit 'n kanaal. Daar is ongeveer 300 van hierdie kanale, elkeen van die kanale kan afval. Van al die aangetekende maatstawwe sal ons die foutkoers (versoeke en foute) monitor.

Hoekom nie graphana nie?

Foutwaarskuwings wat in grafana opgestel is, het verskeie nadele. Sommige is krities, sommige kan jy jou oë voor sluit, afhangende van die situasie.

Grafana kan nie tussen dimensies + waarskuwing bereken nie, maar ons benodig 'n koers (versoeke-foute)/versoeke.

Die foute lyk sleg:

Truuks vir die hantering van statistieke in Kapasitor

En minder boos as dit met suksesvolle versoeke beskou word:

Truuks vir die hantering van statistieke in Kapasitor

Goed, ons kan die koers in die diens voor grafana vooraf bereken, en in sommige gevalle sal dit werk. Maar nie in ons s'n nie, want. vir elke kanaal word die verhouding daarvan as "normaal" beskou, en waarskuwings werk volgens statiese waardes (ons soek oë, verander as dit gereeld waarsku).

Hierdie is voorbeelde van "normaal" vir verskillende kanale:

Truuks vir die hantering van statistieke in Kapasitor

Truuks vir die hantering van statistieke in Kapasitor

Kom ons ignoreer die vorige punt en neem aan dat alle verskaffers 'n soortgelyke "normale" prentjie het. Nou is alles reg, en ons kan klaarkom met grafana-waarskuwings?
Ons kan, maar ons wil regtig nie, want ons moet een van die opsies kies:
a) maak baie grafieke vir elke kanaal afsonderlik (en vergesel dit pynlik)
b) laat een grafiek met alle kanale (en verdwaal in kleurvolle lyne en pasgemaakte waarskuwings)

Truuks vir die hantering van statistieke in Kapasitor

Hoe het jy dit gedoen?

Weereens, daar is 'n goeie beginvoorbeeld in die dokumentasie (Berekening van tariewe oor gevoegde reekse), kan jy loer of as basis neem in soortgelyke take.

Wat hulle op die ou end gedoen het:

  • sluit binne 'n paar uur by twee reekse aan, groepeer volgens kanale;
  • vul die reeks volgens groepe in as daar geen data was nie;
  • vergelyk die mediaan van die afgelope 10 minute met die vorige data;
  • skree as iets gevind word;
  • skryf berekende tariewe en waarskuwings wat voorgekom het in influxdb;
  • stuur 'n nuttige boodskap aan slack.

Myns insiens het ons alles wat ons graag by die uitset wil kry so mooi moontlik reggekry (en selfs 'n bietjie meer met pasgemaakte hanteerders).

Op github.com kan jy sien kode voorbeeld и minimale skema (graphviz) skrif ontvang het.

'n Voorbeeld van die gevolglike kode:

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)

En wat is die gevolgtrekking?

Kapacitor is wonderlik om moniteringwaarskuwing met 'n klomp groeperings uit te voer, bykomende berekeninge op reeds aangetekende statistieke uit te voer, pasgemaakte aksies uit te voer en skrifte (udf) uit te voer.

Die toegangsdrempel is nie baie hoog nie - probeer dit as grafana of ander instrumente nie ten volle aan jou wenslys voldoen nie.

Bron: will.com

Koop betroubare hosting vir werwe met DDoS-beskerming, VPS VDS-bedieners 🔥 Koop betroubare webwerfhosting met DDoS-beskerming, VPS VDS-bedieners | ProHoster