Kapacitori mõõdikute töötlemise nipid

Tõenäoliselt ei küsi täna keegi, miks on vaja teenindusmõõdikuid koguda. Järgmine loogiline samm on kogutud mõõdikute jaoks märguande seadistamine, mis annab teile sobivates kanalites (post, Slack, Telegram) teada andmete kõrvalekalletest. Interneti-hotellibroneerimisteenuses Ostrovok.ru kõik meie teenuste mõõdikud valatakse InfluxDB-sse ja kuvatakse Grafanas ning seal on konfigureeritud ka põhihoiatused. Selliste ülesannete jaoks nagu "peate midagi arvutama ja sellega võrdlema" kasutame Kapacitorit.

Kapacitori mõõdikute töötlemise nipid
Kapacitor on osa TICKi pinust, mis suudab töödelda InfluxDB mõõdikuid. Sellega saab mitu mõõtmist kokku ühendada (liituda), saadud andmetest midagi kasulikku välja arvutada, tulemuse InfluxDB-sse tagasi kirjutada, Slacki/Telegrami/maili hoiatuse saata.

Kogu virn on lahe ja detailne dokumentatsioon, kuid alati leidub kasulikke asju, mida juhendites selgesõnaliselt märgitud pole. Sellesse artiklisse otsustasin koguda mitmeid selliseid kasulikke, mitteilmseid näpunäiteid (kirjeldatakse TICKscipti põhisüntaksit siin) ja näidake, kuidas neid ühe meie probleemi lahendamise näitel rakendada.

Mine!

float & int, arvutusvead

Täiesti standardne probleem, mis lahendatakse kastide kaudu:

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

Vaikimisi () kasutamine

Kui silt/väli on täitmata, ilmnevad arvutusvead:

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

täitke ühendus (sisemine vs välimine)

Vaikimisi tühistab liitumine punktid, kus andmed puuduvad (sisemine).
Funktsiooniga fill('null') teostatakse välimine ühendamine, mille järel peate tegema default() ja täitma tühjad väärtused:

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

Siin on ikkagi nüanss. Ülaltoodud näites, kui üks seeriatest (res1 või res2) on tühi, on ka tulemuseks olev seeria (andmed) tühi. Githubis on sellel teemal mitu piletit (1633, 1871, 6967) – ootame parandusi ja kannatame veidi.

Tingimuste kasutamine arvutustes (kui lambda)

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

Viimased viis minutit torujuhtmest perioodi kohta

Näiteks peate võrdlema viimase viie minuti väärtusi eelmise nädalaga. Võite võtta kaks andmepaketti kahes eraldi komplektis või eraldada osa andmetest suuremast perioodist:

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

Viimase viie minuti alternatiiviks oleks kasutada BarrierNode'i, mis katkestab andmed enne määratud aega:

|barrier()
        .period(5m)

Näiteid Go mallide kasutamisest sõnumis

Mallid vastavad pakendis olevale vormingule tekst.mallAllpool on mõned sageli esinevad mõistatused.

kui-veel

Paneme asjad paika ja ei vallanda inimesi uuesti tekstiga:

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

Kaks numbrit pärast koma sõnumis

Sõnumi loetavuse parandamine:

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

Muutujate laiendamine sõnumis

Kuvame sõnumis lisateavet, et vastata küsimusele "Miks see karjub"?

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

Unikaalne hoiatuse identifikaator

See on vajalik, kui andmetes on rohkem kui üks rühm, vastasel juhul genereeritakse ainult üks hoiatus:

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

Kohandatud töötleja

Käsitlejate suures loendis on exec, mis võimaldab täita oma skripti läbitud parameetritega (stdin) - loovus ja ei midagi muud!

Üks meie tavadest on väike Pythoni skript, mis võimaldab saata teateid lõdvaks.
Alguses tahtsime sõnumiga saata autoriseerimiskaitsega grafana pilti. Seejärel kirjutage sama rühma eelmise märguande lõime OK, mitte eraldi sõnumina. Veidi hiljem – lisa sõnumile viimase X minuti kõige levinum viga.

Eraldi teema on suhtlemine teiste teenustega ja kõik hoiatusega algatatud toimingud (ainult siis, kui teie monitooring töötab piisavalt hästi).
Näide töötleja kirjeldusest, kus slack_handler.py on meie enda kirjutatud skript:

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

Kuidas siluda?

Logi väljundiga võimalus

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

Vaata (cli): kapacitor -url host-või-ip:9092 logi lvl=viga

Valik koos httpOutiga

Näitab andmeid praeguses torujuhtmes:

|httpOut('something')

Vaata (saa): host-või-ip:9092/kapacitor/v1/tasks/task_name/something

Täitmisskeem

  • Iga ülesanne tagastab vormingus kasulike numbritega täitmispuu grafvis.
  • Võtke plokk täpp.
  • Kleepige see vaatajasse, naudi.

Kust veel reha saab?

ajatempel influxdb-s tagasikirjutamisel

Näiteks seadistame märguande taotluste summa kohta tunnis (groupBy(1h)) ja tahame salvestada influxdb-s ilmnenud hoiatuse (et kaunilt näidata probleemi fakti grafanas graafikul).

influxDBOut() kirjutab ajaväärtuse hoiatusest ajatemplisse; vastavalt sellele kirjutatakse diagrammi punkt varem/hiljem kui hoiatus saabus.

Kui nõutakse täpsust: lahendame selle probleemi, kutsudes välja kohandatud töötleja, mis kirjutab andmed praeguse ajatempliga kataloogi influxdb.

dokkija, ehitamine ja juurutamine

Käivitamisel saab kapacitor laadida ülesandeid, malle ja töötlejaid ploki [load] konfiguratsioonis määratud kataloogist.

Ülesande korrektseks loomiseks vajate järgmisi asju:

  1. Faili nimi – laiendatud skripti ID-ks/nimeks
  2. Tüüp – voog/partii
  3. dbrp – märksõna, mis näitab, millises andmebaasis + poliitikas skript töötab (dbrp “tarnija.” “autogen”)

Kui mõni pakkülesanne ei sisalda rida koos dbrp-ga, keeldub kogu teenus käivitamast ja kirjutab selle ausalt logisse.

Vastupidi, kronografis ei tohiks seda rida eksisteerida, seda ei aktsepteerita liidese kaudu ja see tekitab vea.

Häkkimine konteineri ehitamisel: Dockerfile väljub -1-ga, kui on read //.+dbrp, mis võimaldab teil ehituse kokkupanemisel kohe aru saada ebaõnnestumise põhjusest.

liitu üks paljudega

Näidisülesanne: peate võtma teenuse nädala tööajast 95. protsentiili, võrrelge viimase 10 minuti iga minutit selle väärtusega.

Üks-mitmele liitumist ei saa teha, punktide grupi viimane/keskmine/mediaan muudab sõlme vooks, tagastatakse tõrketeade "ei saa lisada lapse mittesobivaid servi: partii -> voog".

Ka partii tulemust kui muutujat lambda-avaldises ei asendata.

On võimalus salvestada vajalikud numbrid esimesest partiist faili udf-i kaudu ja laadida see fail külglaadimise kaudu.

Mida me sellega lahendasime?

Meil on umbes 100 hotellipakkujat, igaühel neist võib olla mitu ühendust, nimetagem seda kanaliks. Neid kanaleid on umbes 300, kõik kanalid võivad välja kukkuda. Kõigist salvestatud mõõdikutest jälgime veamäära (päringud ja vead).

Miks mitte grafana?

Grafanas konfigureeritud veahoiatustel on mitmeid puudusi. Mõned on kriitilised, mõne ees võite olenevalt olukorrast silmad sulgeda.

Grafana ei oska mõõtmiste + hoiatamise vahel arvutada, aga meil on vaja määra (päringud-vead)/päringud.

Vead näevad vastikud välja:

Kapacitori mõõdikute töötlemise nipid

Ja edukate taotlustega vaadates vähem kurja:

Kapacitori mõõdikute töötlemise nipid

Olgu, saame teenuses enne grafana tariifi eelnevalt välja arvutada ja mõnel juhul see toimib. Aga mitte meie omas, sest... iga kanali jaoks peetakse oma suhet "normaalseks" ja hoiatused töötavad staatiliste väärtuste järgi (otsime neid silmadega, muudame neid, kui hoiatusi on sageli).

Need on erinevate kanalite "tavaliste" näited:

Kapacitori mõõdikute töötlemise nipid

Kapacitori mõõdikute töötlemise nipid

Jätame eelneva punkti tähelepanuta ja eeldame, et “tavaline” pilt on kõigi tarnijate puhul sarnane. Nüüd on kõik korras ja saame grafanas hoiatustega hakkama?
Me saame, aga me tõesti ei taha, sest peame valima ühe järgmistest valikutest:
a) koostage iga kanali kohta eraldi palju graafikuid (ja kandke neid valusalt kaasas)
b) jätke üks diagramm kõigi kanalitega (ja eksige värvilistesse joontesse ja kohandatud hoiatustesse)

Kapacitori mõõdikute töötlemise nipid

Kuidas sa seda tegid?

Jällegi on dokumentatsioonis hea algusnäide (Liidetud seeriate intressimäärade arvutamine), saab piiluda või sarnaste probleemide puhul aluseks võtta.

Mida me lõpuks tegime:

  • liita paari tunni jooksul kaks sarja, rühmitades kanalite kaupa;
  • andmete puudumisel täida seeria rühmade kaupa;
  • võrrelda viimase 10 minuti mediaani varasemate andmetega;
  • karjume, kui midagi leiame;
  • kirjutame välja arvutatud määrad ja märguanded, mis ilmnesid influxdb-s;
  • saatke slackile kasulik sõnum.

Minu meelest õnnestus meil lõpuks kõik, mida tahtsime saada (ja kohandatud händleritega veidi rohkemgi) võimalikult ilusasti saavutada.

Saate vaadata saiti github.com koodi näide и minimaalne vooluahel (graphviz) saadud skript.

Saadud koodi näide:

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)

Mis on järeldus?

Kapacitor on suurepärane rühmitustega jälgimise-hoiatuste tegemiseks, juba salvestatud mõõdikute põhjal täiendavate arvutuste tegemiseks, kohandatud toimingute tegemiseks ja skriptide (udf) käivitamiseks.

Sisenemise barjäär ei ole väga kõrge – proovige järele, kui grafana või muud tööriistad ei rahulda täielikult teie soove.

Allikas: www.habr.com

Lisa kommentaar