Triciau ar gyfer prosesu metrigau yn Kapacitor

Yn fwyaf tebygol, heddiw does neb yn gofyn pam mae angen casglu metrigau gwasanaeth. Y cam rhesymegol nesaf yw sefydlu rhybudd ar gyfer y metrigau a gasglwyd, a fydd yn hysbysu am unrhyw wyriadau yn y data mewn sianeli sy'n gyfleus i chi (post, Slack, Telegram). Yn y gwasanaeth archebu gwesty ar-lein Ostrovok.ru mae holl fetrigau ein gwasanaethau yn cael eu tywallt i InfluxDB a'u harddangos yn Grafana, ac mae rhybuddion sylfaenol hefyd wedi'u ffurfweddu yno. Ar gyfer tasgau fel “mae angen i chi gyfrifo rhywbeth a'i gymharu ag ef,” rydyn ni'n defnyddio Kapacitor.

Triciau ar gyfer prosesu metrigau yn Kapacitor
Mae Kapacitor yn rhan o'r stack TICK sy'n gallu prosesu metrigau o InfluxDB. Gall gysylltu sawl mesuriad gyda'i gilydd (ymuno), cyfrifo rhywbeth defnyddiol o'r data a dderbyniwyd, ysgrifennu'r canlyniad yn ôl i InfluxDB, anfon rhybudd i Slack / Telegram / post.

Mae'r pentwr cyfan yn oer ac yn fanwl dogfennaeth, ond bydd pethau defnyddiol bob amser nad ydynt wedi'u nodi'n benodol yn y llawlyfrau. Yn yr erthygl hon, penderfynais gasglu nifer o awgrymiadau defnyddiol, nad ydynt yn amlwg (disgrifir cystrawen sylfaenol TICKscipt yma) a dangos sut y gellir eu cymhwyso gan ddefnyddio enghraifft o ddatrys un o'n problemau.

Gadewch i ni fynd!

arnofio & int, gwallau cyfrifo

Problem hollol safonol, wedi'i datrys trwy gastiau:

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

Defnyddio rhagosodedig()

Os na chaiff tag/maes ei lenwi, bydd gwallau cyfrifo yn digwydd:

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

llenwi ymuno (mewnol yn erbyn allanol)

Yn ddiofyn, bydd ymuno yn taflu pwyntiau lle nad oes data (mewnol).
Gyda llenwi ('null'), bydd uniad allanol yn cael ei berfformio, ac ar ôl hynny mae angen i chi wneud rhagosodiad() a llenwi'r gwerthoedd gwag:

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

Mae naws yma o hyd. Yn yr enghraifft uchod, os yw un o'r gyfres (res1 neu res2) yn wag, bydd y gyfres canlyniadol (data) hefyd yn wag. Mae sawl tocyn ar y pwnc hwn ar Github (1633, 1871, 6967) – rydym yn aros am atebion ac yn dioddef ychydig.

Defnyddio amodau mewn cyfrifiadau (os yw mewn lambda)

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

Pum munud olaf o'r gweill ar gyfer y cyfnod

Er enghraifft, mae angen i chi gymharu gwerthoedd y pum munud diwethaf â'r wythnos flaenorol. Gallwch gymryd dau swp o ddata mewn dau swp ar wahân neu dynnu rhan o'r data o gyfnod mwy:

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

Dewis arall ar gyfer y pum munud olaf fyddai defnyddio BarrierNode, sy'n torri data cyn yr amser penodedig:

|barrier()
        .period(5m)

Enghreifftiau o ddefnyddio templedi Go mewn neges

Mae templedi yn cyfateb i fformat y pecyn testun.templedIsod mae rhai posau y deuir ar eu traws yn aml.

os-arall

Rydyn ni'n rhoi pethau mewn trefn ac nid ydyn ni'n sbarduno pobl â thestun unwaith eto:

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

Dau ddigid ar ôl y pwynt degol yn y neges

Gwella darllenadwyedd y neges:

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

Ehangu newidynnau yn y neges

Rydym yn dangos mwy o wybodaeth yn y neges i ateb y cwestiwn “Pam ei fod yn gweiddi”?

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

Dynodydd rhybudd unigryw

Mae hyn yn angenrheidiol pan fo mwy nag un grŵp yn y data, fel arall dim ond un rhybudd a gynhyrchir:

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

Triniwr personol

Mae'r rhestr fawr o drinwyr yn cynnwys exec, sy'n eich galluogi i weithredu'ch sgript gyda'r paramedrau a basiwyd (stdin) - creadigrwydd a dim byd mwy!

Un o'n harferion yw sgript Python bach ar gyfer anfon hysbysiadau i slac.
Ar y dechrau, roeddem am anfon llun grafana wedi'i ddiogelu gan awdurdodiad mewn neges. Wedi hynny, ysgrifennwch Iawn yn yr edefyn i'r rhybudd blaenorol o'r un grŵp, ac nid fel neges ar wahân. Ychydig yn ddiweddarach - ychwanegu at y neges y camgymeriad mwyaf cyffredin yn yr X munud olaf.

Pwnc ar wahân yw cyfathrebu â gwasanaethau eraill ac unrhyw gamau a ysgogir gan rybudd (dim ond os yw'ch monitro'n gweithio'n ddigon da).
Enghraifft o ddisgrifiad triniwr, lle mae slack_handler.py yn ein sgript hunan-ysgrifenedig:

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

Sut i ddadfygio?

Opsiwn gydag allbwn log

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

Gwylio (cli): kapacitor -url gwesteiwr-neu-ip:9092 logiau lvl=gwall

Opsiwn gyda httpOut

Yn dangos data ar y gweill:

|httpOut('something')

Gwylio (cael): gwesteiwr-neu-ip:9092/kapacitor/v1/tasks/task_name/rhywbeth

Cynllun gweithredu

  • Mae pob tasg yn dychwelyd coeden gyflawni gyda rhifau defnyddiol yn y fformat graffviz.
  • Cymerwch bloc dot.
  • Gludwch ef i mewn i'r gwyliwr, mwynhau.

Ble arall allwch chi gael rhaca?

stamp amser mewn mewnlifiad wrth ysgrifennu'n ôl

Er enghraifft, rydym yn gosod rhybudd ar gyfer swm y ceisiadau yr awr (groupBy(1h)) ac rydym am gofnodi'r rhybudd a ddigwyddodd mewn influxdb (i ddangos yn hyfryd ffaith y broblem ar y graff mewn grafana).

bydd influxDBOut() yn ysgrifennu'r gwerth amser o'r rhybudd i'r stamp amser; yn unol â hynny, bydd y pwynt ar y siart yn cael ei ysgrifennu yn gynharach/yn hwyrach nag y cyrhaeddodd y rhybudd.

Pan fo angen cywirdeb: rydym yn gweithio o gwmpas y broblem hon trwy ffonio triniwr arfer, a fydd yn ysgrifennu data i influxdb gyda'r stamp amser cyfredol.

docwr, adeiladu a lleoli

Wrth gychwyn, gall kapacitor lwytho tasgau, templedi a thrinwyr o'r cyfeiriadur a nodir yn y ffurfwedd yn y bloc [load].

I greu tasg yn gywir, mae angen y pethau canlynol arnoch:

  1. Enw ffeil - wedi'i ehangu i ID/enw'r sgript
  2. Math – ffrwd/swp
  3. dbrp - allweddair i nodi ym mha gronfa ddata + polisi mae'r sgript yn rhedeg (dbrp “cyflenwr.” “autogen”)

Os nad yw rhyw dasg swp yn cynnwys llinell â dbrp, bydd y gwasanaeth cyfan yn gwrthod cychwyn a bydd yn ysgrifennu amdano yn onest yn y log.

Yn chronograf, i'r gwrthwyneb, ni ddylai'r llinell hon fodoli; ni chaiff ei dderbyn trwy'r rhyngwyneb ac mae'n cynhyrchu gwall.

Haciwch wrth adeiladu cynhwysydd: Mae Dockerfile yn gadael gyda -1 os oes llinellau gyda //.+dbrp, a fydd yn caniatáu ichi ddeall ar unwaith y rheswm dros y methiant wrth gydosod yr adeiladwaith.

ymuno un i lawer

Tasg enghreifftiol: mae angen i chi gymryd y 95fed canradd o amser gweithredu'r gwasanaeth am wythnos, cymharwch bob munud o'r 10 olaf gyda'r gwerth hwn.

Ni allwch wneud uniad un-i-lawer, mae olaf / cymedrig / canolrif dros grŵp o bwyntiau yn troi'r nod yn nant, bydd y gwall "methu ychwanegu ymylon camgyfatebol plentyn: swp -> ffrwd" yn cael ei ddychwelyd.

Nid yw canlyniad swp, fel newidyn mewn mynegiant lambda, yn cael ei ddisodli ychwaith.

Mae opsiwn i arbed y niferoedd angenrheidiol o'r swp cyntaf i ffeil trwy UDF a llwytho'r ffeil hon trwy sideload.

Beth wnaethon ni ei ddatrys gyda hyn?

Mae gennym tua 100 o gyflenwyr gwesty, gall pob un ohonynt gael nifer o gysylltiadau, gadewch i ni ei alw'n sianel. Mae tua 300 o'r sianeli hyn, gall pob un o'r sianeli ddisgyn i ffwrdd. O'r holl fetrigau a gofnodwyd, byddwn yn monitro'r gyfradd gwallau (ceisiadau a gwallau).

Beth am grafana?

Mae sawl anfantais i rybuddion gwall sydd wedi'u ffurfweddu yn Grafana. Mae rhai yn hollbwysig, rhai y gallwch chi gau eich llygaid iddyn nhw, yn dibynnu ar y sefyllfa.

Nid yw Grafana yn gwybod sut i gyfrifo rhwng mesuriadau + rhybuddio, ond mae angen cyfradd (ceisiadau-gwallau)/ceisiadau arnom.

Mae'r gwallau'n edrych yn gas:

Triciau ar gyfer prosesu metrigau yn Kapacitor

A llai o ddrwg pan edrychir arno gyda cheisiadau llwyddiannus:

Triciau ar gyfer prosesu metrigau yn Kapacitor

Iawn, gallwn rag-gyfrifo'r gyfradd yn y gwasanaeth cyn grafana, ac mewn rhai achosion bydd hyn yn gweithio. Ond nid yn ein un ni, oherwydd ... ar gyfer pob sianel mae ei gymhareb ei hun yn cael ei hystyried yn “normal”, ac mae rhybuddion yn gweithio yn ôl gwerthoedd statig (rydym yn edrych gyda'n llygaid, yn newid os oes rhybuddion aml).

Dyma enghreifftiau o “normal” ar gyfer gwahanol sianeli:

Triciau ar gyfer prosesu metrigau yn Kapacitor

Triciau ar gyfer prosesu metrigau yn Kapacitor

Anwybyddwn y pwynt blaenorol a thybiwn fod y darlun “normal” yn debyg i bob cyflenwr. Nawr mae popeth yn iawn, a gallwn ni fynd heibio gyda rhybuddion mewn grafana?
Gallwn, ond nid ydym wir eisiau gwneud hynny, oherwydd mae'n rhaid i ni ddewis un o'r opsiynau:
a) gwneud llawer o graffiau ar gyfer pob sianel ar wahân (a mynd gyda nhw yn boenus)
b) gadael un siart gyda phob sianel (a mynd ar goll yn y llinellau lliwgar a'r rhybuddion wedi'u haddasu)

Triciau ar gyfer prosesu metrigau yn Kapacitor

Sut wnaethoch chi?

Unwaith eto, mae enghraifft gychwynnol dda yn y ddogfennaeth (Cyfrifo cyfraddau ar draws cyfresi cysylltiedig), gellir edrych arno neu ei gymryd fel sail mewn problemau tebyg.

Yr hyn a wnaethom yn y diwedd:

  • ymuno â dwy gyfres mewn ychydig oriau, gan grwpio yn ôl sianeli;
  • llenwi'r gyfres fesul grŵp os nad oedd data;
  • cymharu canolrif y 10 munud diwethaf â data blaenorol;
  • bloeddiwn os canfyddwn rywbeth;
  • rydym yn ysgrifennu'r cyfraddau a'r rhybuddion a gyfrifwyd a ddigwyddodd mewn mewnlifiad;
  • anfon neges ddefnyddiol i slac.

Yn fy marn i, fe wnaethom lwyddo i gyflawni popeth yr oeddem am ei gael ar y diwedd (a hyd yn oed ychydig yn fwy gyda thrinwyr arfer) mor hyfryd â phosibl.

Gallwch edrych ar github.com enghraifft cod и cylched lleiaf (graffviz) y sgript canlyniadol.

Enghraifft o'r cod canlyniadol:

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)

Beth yw'r casgliad?

Mae Kapacitor yn wych am berfformio rhybuddion monitro gyda chriw o grwpiau, perfformio cyfrifiadau ychwanegol yn seiliedig ar fetrigau a gofnodwyd eisoes, perfformio gweithredoedd arfer a rhedeg sgriptiau (udf).

Nid yw'r rhwystr rhag mynediad yn uchel iawn - rhowch gynnig arni os nad yw grafana neu offer eraill yn bodloni'ch dymuniadau'n llawn.

Ffynhonnell: hab.com

Ychwanegu sylw