Mbinu za kuchakata vipimo katika Kapacitor

Uwezekano mkubwa zaidi, leo hakuna mtu anayeuliza kwa nini ni muhimu kukusanya metrics za huduma. Hatua inayofuata ya kimantiki ni kuweka arifa ya vipimo vilivyokusanywa, ambayo itakuarifu kuhusu hitilafu zozote za data katika vituo vinavyokufaa (barua, Slack, Telegramu). Katika huduma ya uhifadhi wa hoteli mtandaoni Ostrovok.ru vipimo vyote vya huduma zetu humiminwa kwenye InfluxDB na kuonyeshwa Grafana, na arifa za kimsingi pia zimesanidiwa hapo. Kwa kazi kama vile "unahitaji kuhesabu kitu na kulinganisha nacho," tunatumia Kapacitor.

Mbinu za kuchakata vipimo katika Kapacitor
Kapacitor ni sehemu ya safu ya TICK inayoweza kuchakata vipimo kutoka kwa InfluxDB. Inaweza kuunganisha vipimo kadhaa pamoja (kujiunga), kukokotoa kitu muhimu kutoka kwa data iliyopokelewa, kuandika matokeo kwa InfluxDB, kutuma arifa kwa Slack/Telegram/mail.

Mkusanyiko mzima ni mzuri na wa kina nyaraka, lakini daima kutakuwa na mambo muhimu ambayo hayajaonyeshwa kwa uwazi katika miongozo. Katika nakala hii, niliamua kukusanya vidokezo kadhaa muhimu, visivyo dhahiri (syntax ya msingi ya TICKscipt imeelezewa. hapa) na uonyeshe jinsi yanavyoweza kutumika kwa kutumia mfano wa kutatua mojawapo ya matatizo yetu.

Hebu kwenda!

kuelea & int, makosa ya hesabu

Shida ya kawaida kabisa, iliyotatuliwa kupitia castes:

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

Kwa kutumia chaguo-msingi()

Ikiwa lebo/uga haujajazwa, hitilafu za hesabu zitatokea:

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

jaza kujiunga (ndani dhidi ya nje)

Kwa chaguomsingi, kujiunga kutatupa pointi ambapo hakuna data (ndani).
Wakati fill('null') uunganisho wa nje utafanywa, baada ya hapo unahitaji kufanya chaguo-msingi() na ujaze maadili tupu:

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

Bado kuna nuance hapa. Katika mfano hapo juu, ikiwa moja ya safu (res1 au res2) haina tupu, safu inayotokana (data) pia itakuwa tupu. Kuna tikiti kadhaa kwenye mada hii kwenye Github (1633, 1871, 6967) - tunasubiri marekebisho na mateso kidogo.

Kutumia hali katika mahesabu (ikiwa katika lambda)

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

Dakika tano za mwisho kutoka kwa bomba kwa kipindi hicho

Kwa mfano, unahitaji kulinganisha maadili ya dakika tano zilizopita na wiki iliyopita. Unaweza kuchukua makundi mawili ya data katika makundi mawili tofauti au kutoa sehemu ya data kutoka kwa kipindi kikubwa zaidi:

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

Njia mbadala kwa dakika tano zilizopita itakuwa kutumia BarrierNode, ambayo hukata data kabla ya muda uliowekwa:

|barrier()
        .period(5m)

Mifano ya kutumia violezo vya Go katika ujumbe

Violezo vinalingana na umbizo kutoka kwa kifurushi maandishi.kiolezoChini ni baadhi ya mafumbo yanayokutana mara kwa mara.

ikiwa -ngine

Tunaweka mambo kwa mpangilio na tusiwachochee watu kwa maandishi tena:

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

Nambari mbili baada ya nukta ya desimali katika ujumbe

Kuboresha usomaji wa ujumbe:

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

Kupanua vigezo katika ujumbe

Tunaonyesha maelezo zaidi katika ujumbe ili kujibu swali "Kwa nini inapiga kelele"?

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

Kitambulisho cha kipekee cha tahadhari

Hili ni jambo la lazima wakati kuna zaidi ya kikundi kimoja kwenye data, vinginevyo tahadhari moja tu itatolewa:

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

Vidhibiti maalum

Orodha kubwa ya washughulikiaji ni pamoja na exec, ambayo hukuruhusu kutekeleza hati yako na vigezo vilivyopitishwa (stdin) - ubunifu na hakuna zaidi!

Moja ya desturi zetu ni hati ndogo ya Python ya kutuma arifa kwa kulegea.
Mwanzoni, tulitaka kutuma picha ya grafana iliyoidhinishwa katika ujumbe. Baadaye, andika SAWA kwenye uzi kwa arifa iliyotangulia kutoka kwa kikundi kimoja, na sio kama ujumbe tofauti. Baadaye kidogo - ongeza kwenye ujumbe kosa la kawaida katika dakika X zilizopita.

Mada tofauti ni mawasiliano na huduma zingine na hatua zozote zinazoanzishwa na arifa (ikiwa tu ufuatiliaji wako utafanya kazi vya kutosha).
Mfano wa maelezo ya kidhibiti, ambapo slack_handler.py ni hati yetu iliyojiandika:

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

Jinsi ya kurekebisha?

Chaguo na pato la logi

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

Tazama (cli): kapacitor -url mwenyeji-au-ip: kumbukumbu 9092 lvl=error

Chaguo na httpOut

Inaonyesha data katika bomba la sasa:

|httpOut('something')

Tazama (pata): mwenyeji-au-ip:9092/kapacitor/v1/tasks/task_name/something

Mchoro wa utekelezaji

  • Kila kazi inarudisha mti wa utekelezaji na nambari muhimu katika umbizo graphviz.
  • Chukua kizuizi dot.
  • Ibandike kwenye mtazamaji, kufurahia.

Ni wapi pengine ambapo unaweza kupata reki?

timestamp katika influxdb juu ya kuandika tena

Kwa mfano, tunaweka arifa ya jumla ya maombi kwa saa (groupBy(1h)) na tunataka kurekodi arifa iliyotokea katika influxdb (ili kuonyesha kwa uzuri ukweli wa tatizo kwenye grafana kwenye grafana).

influxDBOut() itaandika thamani ya muda kutoka kwa tahadhari hadi muhuri wa muda; kwa hivyo, sehemu kwenye chati itaandikwa mapema/baadaye kuliko tahadhari ilipofika.

Wakati usahihi unahitajika: tunashughulikia tatizo hili kwa kupiga simu kidhibiti maalum, ambacho kitaandika data kwa influxdb na muhuri wa saa wa sasa.

docker, kujenga na kupeleka

Wakati wa kuanza, kapacitor inaweza kupakia kazi, violezo na vidhibiti kutoka kwa saraka iliyobainishwa kwenye usanidi kwenye kizuizi cha [mzigo].

Ili kuunda kazi kwa usahihi, unahitaji vitu vifuatavyo:

  1. Jina la faili - limepanuliwa kuwa kitambulisho cha hati/jina
  2. Aina - mkondo/bechi
  3. dbrp - neno kuu la kuonyesha ni sera gani ya hifadhidata + ambayo hati inatumika (dbrp "mtoa huduma.""autogen")

Ikiwa kazi fulani ya kundi haina mstari na dbrp, huduma nzima itakataa kuanza na itaandika kwa uaminifu juu yake kwenye logi.

Katika chronograf, kinyume chake, mstari huu haupaswi kuwepo; haikubaliki kupitia interface na hutoa kosa.

Hack wakati wa kujenga chombo: Dockerfile hutoka na -1 ikiwa kuna mistari iliyo na // +dbrp, ambayo itakuruhusu kuelewa mara moja sababu ya kutofaulu wakati wa kukusanya jengo.

kujiunga moja kwa wengi

Kazi ya mfano: unahitaji kuchukua asilimia 95 ya muda wa uendeshaji wa huduma kwa wiki, kulinganisha kila dakika ya 10 iliyopita na thamani hii.

Huwezi kuunganisha moja hadi nyingi, mwisho/wastani/wastani juu ya kundi la pointi hugeuza nodi kuwa mtiririko, hitilafu "haiwezi kuongeza kingo zisizolingana za mtoto: batch -> mkondo" itarejeshwa.

Matokeo ya kundi, kama kigezo katika usemi wa lambda, pia hayabadilishwi.

Kuna chaguo la kuhifadhi nambari zinazohitajika kutoka kwa kundi la kwanza hadi faili kupitia udf na kupakia faili hii kupitia upakiaji wa kando.

Tulisuluhisha nini na hii?

Tuna takriban wauzaji 100 wa hoteli, kila mmoja wao anaweza kuwa na viunganisho kadhaa, wacha tuite kituo. Kuna takriban 300 ya chaneli hizi, kila moja ya chaneli inaweza kuanguka. Kati ya vipimo vyote vilivyorekodiwa, tutafuatilia kiwango cha makosa (maombi na hitilafu).

Kwa nini sio grafana?

Arifa za hitilafu zilizosanidiwa huko Grafana zina hasara kadhaa. Baadhi ni muhimu, wengine unaweza kufunga macho yako, kulingana na hali hiyo.

Grafana hajui jinsi ya kukokotoa kati ya vipimo + arifa, lakini tunahitaji kiwango (maombi-makosa)/maombi.

Makosa yanaonekana kuwa mabaya:

Mbinu za kuchakata vipimo katika Kapacitor

Na ubaya mdogo unapotazamwa na maombi yaliyofaulu:

Mbinu za kuchakata vipimo katika Kapacitor

Sawa, tunaweza kuhesabu mapema kiwango cha huduma kabla ya grafana, na katika hali zingine hii itafanya kazi. Lakini sio kwetu, kwa sababu ... kwa kila chaneli uwiano wake unachukuliwa kuwa "kawaida", na arifu hufanya kazi kulingana na maadili tuli (tunazitafuta kwa macho yetu, zibadilishe ikiwa kuna arifu za mara kwa mara).

Hii ni mifano ya "kawaida" kwa chaneli tofauti:

Mbinu za kuchakata vipimo katika Kapacitor

Mbinu za kuchakata vipimo katika Kapacitor

Tunapuuza hatua ya awali na kudhani kuwa picha "ya kawaida" ni sawa kwa wasambazaji wote. Sasa kila kitu kiko sawa, na tunaweza kuendelea na arifa katika grafana?
Tunaweza, lakini hatutaki, kwa sababu tunapaswa kuchagua moja ya chaguzi:
a) tengeneza grafu nyingi kwa kila chaneli kando (na uandamane nazo kwa uchungu)
b) acha chati moja iliyo na chaneli zote (na upotee katika mistari ya rangi na arifa zilizobinafsishwa)

Mbinu za kuchakata vipimo katika Kapacitor

Ulifanyaje?

Tena, kuna mfano mzuri wa kuanzia kwenye nyaraka (Kukokotoa viwango katika mfululizo uliounganishwa), inaweza kuchunguzwa au kuchukuliwa kama msingi wa matatizo sawa.

Tulichofanya mwishoni:

  • jiunge na safu mbili kwa masaa machache, ukipanga kwa njia;
  • jaza mfululizo kwa kikundi ikiwa hapakuwa na data;
  • kulinganisha wastani wa dakika 10 zilizopita na data ya awali;
  • tunapiga kelele tukipata kitu;
  • tunaandika viwango vilivyohesabiwa na arifa zilizotokea katika influxdb;
  • tuma ujumbe muhimu kwa ulegevu.

Kwa maoni yangu, tulifanikiwa kufikia kila kitu tulichotaka kupata mwishoni (na hata kidogo zaidi na washughulikiaji maalum) kwa uzuri iwezekanavyo.

Unaweza kuangalia github.com mfano wa kanuni ΠΈ mzunguko mdogo (graphviz) hati inayosababisha.

Mfano wa nambari inayosababisha:

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)

Hitimisho ni nini?

Kapacitor ni mzuri katika kutekeleza arifa za ufuatiliaji na kundi la vikundi, akifanya hesabu za ziada kulingana na vipimo vilivyorekodiwa, kutekeleza vitendo maalum na hati zinazoendesha (udf).

Kizuizi cha kuingia sio juu sana - jaribu ikiwa grafana au zana zingine hazikidhi kabisa matamanio yako.

Chanzo: mapenzi.com

Kuongeza maoni