Truket për përpunimin e metrikave në Kapacitor

Me tmerrësisht, sot askush nuk e diskuton më pse është e nevojshme të mbledhësh metrika të shërbimeve. Hapi tjetër logjik është të konfiguroni alertë për metrikat e mbledhura, që do t'ju njoftojnë për çdo devijim në të dhëna në kanalet tuaja të preferuara (email, Slack, Telegram). Në shërbimin e rezervimeve online të hoteleve Ostrovok.ru të gjitha metrikat e shërbimeve tona derdhen në InfluxDB dhe shfaqen në Grafana, ku është vendosur gjithashtu alertimi bazë. Për detyra si "më duhet të llogaris diçka dhe ta krahasoj me këtë" ne përdorim Kapacitor.

Truket për përpunimin e metrikave në Kapacitor
Kapacitor është pjesë e grumbullit TICK, e cila di të përpunojë metrikat nga InfluxDB. Ai mund të lidhë disa matje mes vete (join), të llogarisë diçka të dobishme nga të dhënat e marra, ta regjistrojë rezultatin përsëri në InfluxDB, dhe të dërgojë një alert në Slack/Telgram/email.

E gjithë staku ka një dokumentacion të shkëlqyer dhe të detajuar dokumentacioni, por gjithmonë ka gjëra të dobishme që nuk janë pararenduar në manuale. Në këtë artikull vendosa të mbledh disa këshilla të shumta që nuk janë kaq të dukshme (sintaksa kryesore e TICKscript është përshkruar këtu) dhe të tregoj si mund t'i aplikoni ato, duke marrë shembull zgjidhjen e një prej detyrave tona.

Fillojmë!

float & int, gabimet në llogaritje

Një problem krejtësisht standard, zgjidhet përmes kastit:

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

Përdorimi i default()

Nëse tagu/fusha nuk është plotësuar, do të ndodhin gabime në llogaritje:

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

fill në join (inner vs outer)

Në mënyrë default, join do të heqë pikat ku nuk ka të dhëna (inner).
Me fill(‘null’) do tĂ« realizohet njĂ« outer join, pas tĂ« cilit duhet tĂ« bĂ«ni default() dhe tĂ« plotĂ«soni vlerat e zbrazĂ«ta:

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

KĂ«tu gjithsesi ka njĂ« nuancĂ«. NĂ«se nĂ« shembullin e mĂ«sipĂ«rm njĂ«ra nga seritĂ« (res1 ose res2) Ă«shtĂ« e zbrazĂ«t, seria pĂ«rfundimtare (data) gjithashtu do tĂ« jetĂ« e zbrazĂ«t. PĂ«r kĂ«tĂ« temĂ« ekzistojnĂ« disa ticketa nĂ« GitHub (1633, 1871, 6967) – presim pĂ«r rregullime dhe ndonjĂ« shqetĂ«sim.

Përdorimi i kushteve në llogaritje (if në lambda)

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

Pesë minutat e fundit nga pipeline për periudhën

Për shembull, ju duhet të krahasoni vlerat e pesë minutave të fundit me javën e kaluar. Mund të merrni dy grupe të dhënash me dy batch të ndara ose të nxirrni një pjesë të të dhënave nga një periudhë më të madhe:

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

Një alternativë për pesë minutat e fundit mund të jetë përdorimi i BarrierNode, e cila ndalon të dhënat më herët se koha e caktuar:

|barrier()
        .period(5m)

Shembuj të përdorimit të template-ve Go në message

Template-t përputhen me formatin nga paketi text.template, më poshtë disa detyra të zakonshme.

if-else

Të bëjmë rregull, pa e aktivizuar njerëzit me tekstin e panevojshëm:

|alert()
    ...
    .message(
        '{{ if eq .Level "OK" }}Tani është në rregull{{ else }}Shefi, gjithçka është e prishur{{end}}'
    )

Dy numra pas presjes në mesazh

Përmirësimi i lexueshmërisë së mesazhit:

|alert()
    ...
    .message(
        'Tani vlera është {{ index .Fields "value" | printf "%0.2f" }}'
    )

Rozhvillimi i variablave në mesazh

Të japim më shumë informacion në mesazh për pyetjen "Pse është në alarm?"

var warnAlert = 10
  |alert()
    ...
    .message(
       'Vlera e sotme është më pak se '+string(warnAlert)+'%'
    )

Identifikuesi unik i alarmin

Një gjë e nevojshme, kur në të dhëna ka më shumë se një grup, përndryshe do të gjenerohet vetëm një alarm:

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

Handler-a të personalizuara

NĂ« njĂ« listĂ« tĂ« madhe handler-a ka exec, i cili lejon ekzekutimin e skriptit tuaj me parametrat e dĂ«rguar (stdin) – krijimtaria vetĂ«m!

Një nga personalizimet tona është një skript i vogël në Python për të dërguar njoftime në Slack.
Fillimisht donim tĂ« dĂ«rgonim nĂ« mesazh njĂ« imazh nga Grafana, tĂ« mbrojtur me autorizim. MĂ« pas – tĂ« shkruanim OK nĂ« thread-in e alarmin e mĂ«parshĂ«m nga e njĂ«jta grup, jo si njĂ« mesazh tĂ« veçantĂ«. Edhe mĂ« vonĂ« – tĂ« shtonim nĂ« mesazh gabimin mĂ« tĂ« shpeshtĂ« pĂ«r minutat e fundit.

Një temë e veçantë është lidhja me shërbime të tjera dhe çdo veprim të iniciuar nga alarmi (vetëm nëse monitorimi juaj funksionon mjaft mirë).
Shembuj përshkrimi i handler-it, ku slack_handler.py është skripti jonë i shkruar vetë:

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

Si të debagoni?

Një opsion për të shkruajtur në log

|log()
      .level("error")
      .prefix("ndodhi diçka")

Shikoni (cli): kapacitor -url host-or-ip:9092 logs lvl=error

Një opsion me httpOut

Tregon të dhënat në pipeline aktual:

|httpOut('dicka')

Shikoni (get): host-or-ip:9092/kapacitor/v1/tasks/task_name/dicka

Skema e ekzekutimit

  • Çdo detyrĂ« kthen njĂ« pemĂ« ekzekutimi me tĂ« dhĂ«na tĂ« dobishme nĂ« formatin graphviz.
  • Merrni bllokun dot.
  • Shtoni nĂ« shikuesin, shijoni.

Nga ku mund të merrni timestamp në influxdb gjatë regjistrimit të kundërt

timestamp në influxdb gjatë regjistrimit të kundërt

Për shembull, ne konfiguroni një alarmin për shumën e kërkesave për orë (groupBy(1h)) dhe duam të regjistrojmë alarmin e ndodhur në influxdb (për ta treguar siç duhet faktin e pranisë së problemit në grafik në grafana).

influxDBOut() do të regjistrojë në timestamp vlerën time nga alarmi, përkatësisht, pika në grafikë do të regjistrohet më herët/ më vonë se sa ka ardhur alarmi.

Kur kërkohet saktësi: e zgjidhim këtë problem përmes thirrjes së një handleri personalizues, i cili do të regjistrojë të dhënat në influxdb me timestamp-in aktual.

docker, ndërtim dhe shpërndarje

Kur fillon kapacitor, ai mund të ngarkojë detyrat, shabllonet dhe handlerët nga direktorja që është caktuar në konfigurim, në bllokun [load].

Për krijimin e saktë të një detyre ne kemi nevojë për këto gjëra:

  1. Emri i skedarit – shndĂ«rrohet nĂ« id/emrin e skriptit
  2. Tipi – stream/batch
  3. dbrp – fjalĂ« kyçe pĂ«r tĂ« pĂ«rcaktuar nĂ« cilĂ«n bazĂ« + politikĂ« punon skripti (dbrp "supplier"."autogen")

Nëse në ndonjë detyrë batch nuk do të ketë rreshtin me dbrp, e gjithë shërbimi do të refuzojë të fillojë dhe do ta shkruajë këtë në log.

Në chronograf, përkundrazi, ky rresht nuk duhet të jetë, ajo nuk pranohet përmes ndërfaqes dhe jep një gabim.

Haku gjatë ndërtimit të konteinerit: Dockerfile del me -1 nëse ka rreshta me //.+dbrp, që do të lejojë menjëherë të kuptoni shkakun e dështimit gjatë ndërtimit.

join një në shumë

Shembulli i detyrës: duhet të marrim percentilin e 95-të të kohës së funksionimit të shërbimit për javën, të krahasojmë çdo minutë nga 10 të fundit me këtë vlerë.

Nuk është e mundur të bësh join një në shumë, last/mean/median për grupin e pikave e shndërron noden në stream, do të kthejë gabimin "cannot add child mismatched edges: batch -> stream."

Rezultati i batch-it, si një variabël në shprehjen lambda, gjithashtu nuk vendoset.

Ka një mundësi të ruajmë numrat e nevojshëm nga batch-i i parë në skedarin përmes udf dhe ta ngarkojmë atë skedar përmes sideload.

ÇfarĂ« po zgjidhim me kĂ«tĂ«?

Kemi rreth 100 furnizues hotelesh, për secilin prej tyre mund të ketë disa lidhje, le të quajmë këtë kanal. Ka rreth 300 nga këto kanale, çdo kanal mund të dështojë. Nga të gjitha metrikat që regjistrojmë, do të monitorojmë shkallën e gabimeve (kërcimet dhe gabimet).

Pse jo grafana?

Alertet për gabimet, të konfigurura në grafana, kanë disa disavantazhe. Disa janë kritike, për disa mund të mbyllim sytë, në varësi të situatës.

Grafana nuk e di si të bëjë llogaritjet midis matjeve + alarmin, ndërsa ne kemi nevojë për shkallën (kërcimet-gabime)/kërcimet.

Gabimet duken të rënda:

Truket për përpunimin e metrikave në Kapacitor

Dhe më pak të rënda, nëse shikojmë me kërkesat e suksesshme:

Truket për përpunimin e metrikave në Kapacitor

Mirë, mund ta llogarisim paraprakisht shkallën në shërbim para grafanës, dhe në disa raste kjo do të ishte e mjaftueshme. Por jo në rastin tonë, pasi për çdo kanal llogaritet një raport "normal", ndërsa alarmin funksionon sipas vlerave statike (i kërkojmë me sy, e ndryshojmë nëse alarmon shpesh).

Këto janë shembuj "normal" për kanale të ndryshme:

Truket për përpunimin e metrikave në Kapacitor

Truket për përpunimin e metrikave në Kapacitor

Ne e injorojmë pikën e mëparshme dhe supozojmë se të gjithë furnizuesit kanë një pamje "normale" të ngjashme. Tani është gjithçka në rregull, dhe a mund të menaxhojmë me alarmin në grafana?
Mundemi, por nuk dëshirojmë aspak, sepse duhet të zgjedhim një nga opsionet:
a) të bëjmë shumë grafikë për secilin kanal veçmas (dhe t'i mirëmbajmë ato me mundim)
b) të mbajmë një grafik me të gjitha kanalet (dhe të humbim në linjat me ngjyra dhe alarmin e rregulluar)

Truket për përpunimin e metrikave në Kapacitor

Si vepruam?

Përsëri, në dokumentacion ka një shembull të mirë fillestar (Calculating rates across joined series), mund ta shihni ose ta merrni për bazë në detyra të ngjashme.

ÇfarĂ« bĂ«mĂ« nĂ« fund:

  • join tĂ« dy serisĂ« pĂ«r disa orĂ«, grupimi sipas kanaleve;
  • plotesojmĂ« seritĂ« sipas grupeve, nĂ«se nuk kishte tĂ« dhĂ«na;
  • krahasoni median e 10 minutave tĂ« fundit me tĂ« dhĂ«nat paraprake;
  • kemi thirrur nĂ«se kemi zbuluar diçka;
  • shkruajmĂ« shkallĂ«t e llogaritura dhe alarmin e ndodhur nĂ« influxdb;
  • dĂ«rgojmĂ« njĂ« mesazh tĂ« dobishĂ«m nĂ« slack.

Sipaso mendimit tim, arritëm maksimalisht mirë në gjithçka që do të donim të merrnim si rezultat (dhe madje pak më shumë me handlerët e personalizuar).

Në github.com mund të shihni shembullin e kodit dhe schema minimale (graphviz) të skriptit të marrë.

Shembulli i kodit të ardhur:

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')
 // plotojme vlerat me zero, nese nuk ka pasur
 |default()
 .field('err.value', 0.0)
 .field('req.value', 0.0)
 // nese errors ishin, llogarisim rate
 |eval(lambda: if("err.value" > 0, 100.0 * (float("req.value") - float("err.value")) / float("req.value"), 100.0))
 .as('rate')

// regjistrojmë vlerat e llogaritura në influx
rates
 |influxDBOut()
 .quiet()
 .create()
 .database('kapacitor')
 .retentionPolicy('autogen')
 .measurement('rates')

// marrim të dhënat për 10 minutat e fundit, llogarisim median
var todayRate = rates
 |where(lambda: duration((unixNano(now()) - unixNano("time")) / 1000, 1u)  warnAlert)
 .warnReset(lambda: ("prev.median" - "today.median") < warnReset)
 .flapping(0.25, 0.5)
 .stateChangesOnly()
 // mbledhim linkun për grafik
 .message(
 '{{ .Level }}: {{ index .Tags "channel" }} err/req ratio ({{ index .Tags "supplier" }})
{{ if eq .Level "OK" }}Tani është në rregull{{ else }}
'+string(todayPeriod)+' median është {{ index .Fields "today.median" | printf "%0.2f" }}%, krahasuar me '+string(period)+' e kaluar është {{ 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=UTC0300'
 )
 .id('{{ index .Tags "name" }}{{ index .Tags "channel" }}')
 .levelTag('level')
 .messageField('message')
 .durationField('duration')
 .topic('slack_graph')

// "today.median" e mbledhim si "value", gjithashtu e regjistrojmë në influx fushat e tjera të alarmin
trigger
 |eval(lambda: "today.median")
 .as('value')
 .keep()
 |influxDBOut()
 .quiet()
 .create()
 .database('kapacitor')
 .retentionPolicy('autogen')
 .measurement('alerts')
 .tag('alertName', name)

Dhe çfarë dolen?

Kapacitor është fantastik për monitorimin dhe alarmin me shumë grupime, për të kryer llogaritje të tjera bazuar në metrika të regjistruara, për të ekzekutuar veprime të personalizuara dhe për të nisur skripte (udf).

PĂ«rkufizimi i hyrjes nuk Ă«shtĂ« shumĂ« i lartĂ« – provoni atĂ« nĂ«se Grafana apo mjete tĂ« tjera nuk pĂ«rmbushin plotĂ«sisht kĂ«rkesat tuaja.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster