Tricks foar it ferwurkjen fan metriken yn Kapacitor

Meast wierskynlik, hjoed freget gjinien wêrom't it nedich is om servicemetriken te sammeljen. De folgjende logyske stap is om in warskôging yn te stellen foar de sammele metriken, dy't sil ynformearje oer alle ôfwikingen yn 'e gegevens yn kanalen dy't jo handich binne (post, Slack, Telegram). Yn de online hotel booking tsjinst Ostrovok.ru alle metriken fan ús tsjinsten wurde yn InfluxDB yngien en werjûn yn Grafana, en basis warskôging is dêr ek konfigureare. Foar taken lykas "jo moatte wat berekkenje en dermei fergelykje," brûke wy Kapacitor.

Tricks foar it ferwurkjen fan metriken yn Kapacitor
Kapacitor is diel fan 'e TICK-stapel dy't metriken kin ferwurkje fan InfluxDB. It kin ferskate mjittingen ferbine (meidwaan), wat nuttich berekkenje fan 'e ûntfongen gegevens, it resultaat weromskriuwe nei InfluxDB, in warskôging stjoere nei Slack / Telegram / mail.

De heule stapel is koel en detaillearre dokumintaasje, mar d'r sille altyd nuttige dingen wêze dy't net eksplisyt oanjûn binne yn 'e hantliedingen. Yn dit artikel besleat ik in oantal fan sokke nuttige, net-foar de hân lizzende tips te sammeljen (de basissyntaksis fan TICKscipt wurdt beskreaun hjir) en lit sjen hoe't se kinne wurde tapast mei in foarbyld fan it oplossen fan ien fan ús problemen.

Lit ús gean!

float & int, berekkeningsflaters

In absolút standert probleem, oplost troch kasten:

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

It brûken fan standert()

As in tag/fjild net ynfolle is, sille berekkeningsflaters foarkomme:

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

ynfolje join (innerlik vs uterlik)

Standert sil join punten ferwiderje wêr't gjin gegevens binne (ynderlik).
Mei fill('null'), sil in eksterne join útfierd wurde, wêrnei't jo in standert () moatte dwaan en de lege wearden ynfolje:

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

Hjir is noch in nuânse. Yn it foarbyld hjirboppe, as ien fan 'e searjes (res1 of res2) leech is, sil de resultearjende searje (gegevens) ek leech wêze. D'r binne ferskate kaartsjes oer dit ûnderwerp op Github (1633, 1871, 6967) - wy wachtsje op fixes en lijen in bytsje.

Betingsten brûke yn berekkeningen (as yn lambda)

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

Lêste fiif minuten fan de pipeline foar de perioade

Jo moatte bygelyks de wearden fan 'e lêste fiif minuten fergelykje mei de foarige wike. Jo kinne twa batches gegevens yn twa aparte batches nimme of in diel fan 'e gegevens út in gruttere perioade ekstrahearje:

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

In alternatyf foar de lêste fiif minuten soe wêze om in BarrierNode te brûken, dy't gegevens foar de opjûne tiid ôfsnijt:

|barrier()
        .period(5m)

Foarbylden fan it brûken fan Go-sjabloanen yn berjocht

Sjabloanen oerienkomme mei it formaat fan it pakket text.templateHjirûnder binne guon faak tsjinkaam puzels.

as oars

Wy sette dingen yn oarder en triggerje minsken net wer mei tekst:

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

Twa sifers nei it desimaal punt yn it berjocht

De lêsberens fan it berjocht ferbetterje:

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

It útwreidzjen fan fariabelen yn berjocht

Wy litte mear ynformaasje sjen yn it berjocht om de fraach "Wêrom is it te roppen" te beantwurdzjen?

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

Unike alert identifier

Dit is in needsaaklik ding as d'r mear dan ien groep yn 'e gegevens is, oars sil mar ien warskôging wurde generearre:

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

Oanpaste handlers

De grutte list fan handlers omfettet exec, wêrtroch jo jo skript kinne útfiere mei de trochjûne parameters (stdin) - kreativiteit en neat mear!

Ien fan ús gewoanten is in lyts Python-skript foar it ferstjoeren fan notifikaasjes nei slack.
Earst woene wy ​​in autorisaasje-beskerme grafana-ôfbylding yn in berjocht stjoere. Skriuw dêrnei OK yn de tried nei de foarige warskôging fan deselde groep, en net as in apart berjocht. In bytsje letter - tafoegje oan it berjocht de meast foarkommende flater yn 'e lêste X minuten.

In apart ûnderwerp is kommunikaasje mei oare tsjinsten en alle aksjes inisjearre troch in warskôging (allinich as jo kontrôle goed genôch wurket).
In foarbyld fan in handlerbeskriuwing, wêrby't slack_handler.py ús selsskreaune skript 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 debuggen?

Opsje mei logútfier

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

Sjoch (cli): kapacitor -url host-of-ip:9092 logs lvl=flater

Opsje mei httpOut

Toant gegevens yn 'e hjoeddeistige pipeline:

|httpOut('something')

Sjoch (krije): host-of-ip:9092/kapacitor/v1/tasks/task_name/something

Útfiering skema

  • Elke taak jout in útfieringsbeam werom mei nuttige nûmers yn it formaat grafyk.
  • Nim in blokje dot.
  • Plak it yn viewer genietsje.

Wêr oars kinne jo krije in rake?

tiidstempel yn influxdb op weromskriuwing

Bygelyks, wy sette in warskôging foar de som fan fersiken per oere (groupBy(1h)) en wolle opnimme de warskôging dy't barde yn influxdb (om moai sjen litte it feit fan it probleem op 'e grafyk yn grafana).

influxDBOut () sil de tiidwearde fan 'e warskôging nei it tiidstempel skriuwe; dêrom sil it punt op 'e kaart earder / letter skreaun wurde as de warskôging oankaam.

Wannear't krektens is fereaske: wy wurkje om dit probleem troch in belje in oanpaste handler, dy't sil skriuwe gegevens nei influxdb mei de hjoeddeiske tiidstempel.

docker, bouwe en ynset

By it opstarten kin kapacitor taken, sjabloanen en handlers lade út 'e map oantsjutte yn' e konfiguraasje yn it blok [load].

Om in taak korrekt te meitsjen, hawwe jo de folgjende dingen nedich:

  1. Bestânsnamme - útwreide yn skript id / namme
  2. Type - stream / batch
  3. dbrp - kaaiwurd om oan te jaan yn hokker databank + belied it skript rint (dbrp "leveransier." "autogen")

As guon batchtaak gjin line mei dbrp befettet, sil de heule tsjinst wegerje om te begjinnen en sil der earlik oer skriuwe yn it log.

Yn chronograf, krekt oarsom, dizze line soe net moatte bestean; it wurdt net akseptearre fia de ynterface en genereart in flater.

Hack by it bouwen fan in kontener: Dockerfile giet út mei -1 as d'r rigels binne mei //.+dbrp, wêrtroch jo de reden foar it mislearjen fuortendaliks kinne begripe by it gearstallen fan de bou.

join ien nei in protte

Foarbyld taak: jo moatte de 95e percentile fan 'e operaasjetiid fan' e tsjinst nimme foar in wike, fergelykje elke minút fan 'e lêste 10 mei dizze wearde.

Jo kinne gjin ien-nei-mannich join dwaan, lêste / gemiddelde / mediaan oer in groep punten feroaret de knooppunt yn in stream, de flater "kin net tafoegje bern net oerienkommende rânen: batch -> stream" sil weromjûn wurde.

It resultaat fan in batch, as fariabele yn in lambda-ekspresje, wurdt ek net ferfongen.

D'r is in opsje om de nedige nûmers fan 'e earste batch te bewarjen nei in bestân fia udf en dizze bestân fia sideload te laden.

Wat hawwe wy hjirmei oplost?

Wy hawwe sawat 100 hotelleveransiers, elk fan har kin ferskate ferbiningen hawwe, litte wy it in kanaal neame. D'r binne sawat 300 fan dizze kanalen, elk fan 'e kanalen kin falle. Fan alle opnommen metriken sille wy it flaterrate (fersiken en flaters) kontrolearje.

Wêrom net grafana?

Flater warskôgings konfigurearre yn Grafana hawwe ferskate neidielen. Guon binne kritysk, guon kinne jo de eagen slute, ôfhinklik fan 'e situaasje.

Grafana wit net hoe te berekkenjen tusken mjittingen + warskôging, mar wy moatte in taryf (fersiken-flaters) / fersiken.

De flaters lykje ferfelend:

Tricks foar it ferwurkjen fan metriken yn Kapacitor

En minder kwea as besjoen mei suksesfolle oanfragen:

Tricks foar it ferwurkjen fan metriken yn Kapacitor

Okee, wy kinne it taryf yn 'e tsjinst foarôf berekkenje foar grafana, en yn guon gefallen sil dit wurkje. Mar net yn ús, want... foar elk kanaal wurdt syn eigen ferhâlding beskôge as "normaal", en warskôgings wurkje neffens statyske wearden (wy sykje se mei ús eagen, feroarje se as d'r faak warskôgings binne).

Dit binne foarbylden fan "normaal" foar ferskate kanalen:

Tricks foar it ferwurkjen fan metriken yn Kapacitor

Tricks foar it ferwurkjen fan metriken yn Kapacitor

Wy negearje it foarige punt en gean der fan út dat it "normale" byld fergelykber is foar alle leveransiers. No alles is goed, en wy kinne krije troch mei warskôgings yn grafana?
Wy kinne, mar wy wolle echt net, om't wy ien fan 'e opsjes moatte kieze:
a) meitsje in protte grafiken foar elk kanaal apart (en begeliede se pynlik)
b) lit ien kaart litte mei alle kanalen (en ferdwale yn 'e kleurige rigels en oanpaste warskôgings)

Tricks foar it ferwurkjen fan metriken yn Kapacitor

Hoe hasto it dien?

Nochris is d'r in goed begjinfoarbyld yn 'e dokumintaasje (Tariven berekkenje oer ferbûne searjes), kin yn ferlykbere problemen besjoen wurde of as basis nommen wurde.

Wat wy op it lêst dien hawwe:

  • meidwaan oan twa searjes yn in pear oeren, groepearje troch kanalen;
  • folje de rige per groep yn as der gjin gegevens wiene;
  • fergelykje de mediaan fan 'e lêste 10 minuten mei eardere gegevens;
  • wy roppe as wy wat fine;
  • wy skriuwe de berekkene tariven en warskôgings dy't barde yn influxdb;
  • stjoer in nuttich berjocht nei slack.

Neffens my is it ús slagge om alles wat wy oan 'e ein krije woene (en noch wat mear mei oanpaste handlers) sa moai mooglik te berikken.

Jo kinne sjen op github.com koade foarbyld и minimal circuit (graphviz) it resultearjende skript.

In foarbyld fan de resultearjende koade:

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)

Wat is de konklúzje?

Kapacitor is geweldich by it útfieren fan tafersjoch-alerts mei in boskje groepearrings, it útfieren fan ekstra berekkeningen basearre op al opnommen metriken, it útfieren fan oanpaste aksjes en it útfieren fan skripts (udf).

De barriêre foar yngong is net heul heech - besykje it as grafana of oare ark jo winsken net folslein befredigje.

Boarne: www.habr.com

Keapje betroubere hosting foar siden mei DDoS-beskerming, VPS VDS-tsjinners 🔥 Keapje betroubere websidehosting mei DDoS-beskerming, VPS VDS-tsjinners | ProHoster