Trik kanggo pangolahan metrik ing Kapacitor

Paling kamungkinan, dina iki ora ana sing takon apa perlu kanggo ngumpulake metrik layanan. Langkah logis sabanjure yaiku nyiyapake tandha kanggo metrik sing diklumpukake, sing bakal menehi kabar babagan panyimpangan data ing saluran sing trep kanggo sampeyan (mail, Slack, Telegram). Ing layanan pesenan hotel online Ostrovok.ru kabeh metrik layanan kita diwutahake menyang InfluxDB lan ditampilake ing Grafana, lan tandha dhasar uga dikonfigurasi ing kana. Kanggo tugas kaya "sampeyan kudu ngetung soko lan mbandhingakΓ© karo," kita nggunakake Kapacitor.

Trik kanggo pangolahan metrik ing Kapacitor
Kapacitor minangka bagΓ©an saka tumpukan TICK sing bisa ngolah metrik saka InfluxDB. Bisa nyambung sawetara pangukuran bebarengan (gabungan), ngetung soko migunani saka data ditampa, nulis asil bali menyang InfluxDB, ngirim tandha kanggo Slack / Telegram / mail.

Kabeh tumpukan kelangan lan rinci dokumentasi, nanging mesthi ana barang sing migunani sing ora dituduhake kanthi jelas ing manual. Ing artikel iki, aku mutusake kanggo ngumpulake sawetara tips sing migunani lan ora jelas (sintaks dhasar TICKscipt diterangake kene) lan tuduhake carane bisa ditrapake nggunakake conto kanggo ngrampungake salah sawijining masalah.

Ayo ayo!

float & int, kasalahan pitungan

Masalah standar, ditanggulangi liwat kasta:

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

Nggunakake standar ()

Yen tag/kolom ora diisi, kesalahan pitungan bakal kedadeyan:

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

ngisi gabung (ing njero vs njaba)

Kanthi gawan, gabung bakal ngilangi titik sing ora ana data (inner).
Kanthi fill('null'), gabungan njaba bakal dileksanakake, sawise sampeyan kudu nindakake standar () lan isi ing nilai kosong:

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

Ing kene isih ana nuansa. Ing conto ing ndhuwur, yen salah siji saka seri (res1 utawa res2) kosong, seri asil (data) uga bakal kosong. Ana sawetara tiket babagan topik iki ing Github (1633, 1871, 6967) - kita nunggu perbaikan lan nandhang sangsara sethithik.

Nggunakake kahanan ing petungan (yen ing lambda)

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

Limang menit pungkasan saka pipa kanggo periode kasebut

Contone, sampeyan kudu mbandhingake nilai limang menit pungkasan karo minggu sadurunge. Sampeyan bisa njupuk rong kumpulan data ing rong kumpulan sing kapisah utawa ngekstrak bagean data saka periode sing luwih gedhe:

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

Alternatif kanggo limang menit pungkasan yaiku nggunakake BarrierNode, sing ngilangi data sadurunge wektu sing ditemtokake:

|barrier()
        .period(5m)

Conto nggunakake template Go ing pesen

Cithakan cocog karo format saka paket teks.templateIng ngisor iki sawetara teka-teki sing kerep ditemoni.

yen-liya

Kita ngatur kabeh lan ora micu wong nganggo teks maneh:

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

Loro digit sawise titik desimal ing pesen

Ngapikake keterbacaan pesen:

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

Ngembangake variabel ing pesen

Kita nampilake informasi luwih lengkap ing pesen kanggo mangsuli pitakon "Yagene bengok-bengok"?

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

Identifier tandha unik

Iki minangka prekara sing dibutuhake nalika ana luwih saka siji klompok ing data, yen ora mung siji tandha bakal diasilake:

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

Custom handler kang

Dhaptar pawang gedhe kalebu exec, sing ngidini sampeyan nglakokake skrip kanthi paramèter sing dilewati (stdin) - kreatifitas lan ora liya!

Salah sawijining adat istiadat yaiku skrip Python cilik kanggo ngirim kabar kanggo slack.
Kaping pisanan, kita pengin ngirim gambar grafana sing dilindhungi wewenang ing pesen. SawisΓ© iku, nulis OK ing thread kanggo tandha sadurungΓ© saka grup padha, lan ora minangka pesen kapisah. A little mengko - nambah pesen kesalahan paling umum ing menit X pungkasan.

Topik sing kapisah yaiku komunikasi karo layanan liyane lan tumindak apa wae sing diwiwiti kanthi tandha (mung yen pemantauan sampeyan bisa digunakake kanthi apik).
Conto katrangan panangan, ing ngendi slack_handler.py minangka skrip sing ditulis dhewe:

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

Carane debug?

Pilihan karo output log

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

Watch (cli): kapacitor -url host-utawa-ip:9092 log lvl=error

Opsi karo httpOut

Nuduhake data ing pipa saiki:

|httpOut('something')

Watch (entuk): host-utawa-ip:9092/kapacitor/v1/tasks/task_name/something

Skema eksekusi

  • Saben tugas ngasilake wit eksekusi kanthi nomer migunani ing format kasebut grafviz.
  • Njupuk blok dot.
  • Tempel menyang viewer, sekeca.

Ing endi maneh sampeyan bisa njaluk rake?

timestamp ing influxdb ing writeback

Contone, kita nyiyapake tandha kanggo jumlah panjalukan saben jam (groupBy(1h)) lan pengin ngrekam tandha sing kedadeyan ing influxdb (kanggo apik nuduhake kasunyatan masalah ing grafik ing grafana).

influxDBOut () bakal nulis nilai wektu saka tandha menyang timestamp; mula, titik ing grafik bakal ditulis luwih awal / luwih saka tandha teka.

Nalika akurasi dibutuhake: kita ngatasi masalah iki kanthi nelpon pawang khusus, sing bakal nulis data menyang influxdb karo cap wektu saiki.

docker, mbangun lan panyebaran

Nalika wiwitan, kapacitor bisa mbukak tugas, template lan panangan saka direktori sing ditemtokake ing konfigurasi ing blok [load].

Kanggo nggawe tugas kanthi bener, sampeyan butuh perkara ing ngisor iki:

  1. Jeneng berkas – ditambahi dadi skrip id/jeneng
  2. Tipe - stream / batch
  3. dbrp – tembung kunci kanggo nunjukake basis data + kabijakan skrip kasebut (dbrp "pemasok." "autogen")

Yen sawetara tugas batch ora ngemot baris karo dbrp, kabeh layanan bakal nolak miwiti lan bakal nulis kanthi jujur ​​babagan kasebut ing log.

Ing chronograf, sebaliknya, baris iki ora kudu ana, ora ditampa liwat antarmuka lan nggawe kesalahan.

Hack nalika mbangun wadhah: Dockerfile metu karo -1 yen ana baris karo //.+dbrp, sing bakal ngidini sampeyan langsung ngerti alesan kanggo Gagal nalika assembling mbangun.

gabung siji kanggo akeh

Conto tugas: sampeyan kudu njupuk persentil kaping 95 saka wektu operasi layanan sajrone seminggu, mbandhingake saben menit saka 10 pungkasan kanthi nilai iki.

Sampeyan ora bisa nindakake siji-kanggo-akeh gabung, pungkasan / tegese / median liwat klompok TCTerms nguripake simpul menyang stream, kesalahan "ora bisa nambah pinggiran anak mismatched: kumpulan -> stream" bakal bali.

Asil saka kumpulan, minangka variabel ing ekspresi lambda, uga ora diganti.

Ana pilihan kanggo nyimpen nomer sing dibutuhake saka kumpulan pisanan menyang file liwat udf lan mbukak file iki liwat sideload.

Apa kita ngatasi iki?

Kita duwe sekitar 100 supplier hotel, saben wong bisa duwe sawetara sambungan, ayo diarani saluran. Ana kira-kira 300 saluran kasebut, saben saluran bisa tiba. Saka kabeh metrik sing direkam, kita bakal ngawasi tingkat kesalahan (panjaluk lan kesalahan).

Kok ora grafana?

Tandha kesalahan sing dikonfigurasi ing Grafana duwe sawetara kekurangan. Sawetara kritis, sawetara sampeyan bisa nutup mata, gumantung saka kahanan.

Grafana ora ngerti carane ngetung antarane pangukuran + alerting, nanging kita kudu tingkat (requests-errors) / requests.

Kesalahan katon ala:

Trik kanggo pangolahan metrik ing Kapacitor

Lan kurang ala yen dideleng kanthi panjaluk sing sukses:

Trik kanggo pangolahan metrik ing Kapacitor

Oke, kita bisa ngetung tarif ing layanan sadurunge grafana, lan ing sawetara kasus iki bakal bisa. Nanging ora ing kita, amarga ... kanggo saben saluran, rasio dhewe dianggep "normal", lan tandha bisa digunakake miturut nilai statis (kita goleki kanthi mripat, ganti yen ana tandha asring).

Iki minangka conto "normal" kanggo macem-macem saluran:

Trik kanggo pangolahan metrik ing Kapacitor

Trik kanggo pangolahan metrik ing Kapacitor

Kita nglirwakake titik sadurunge lan nganggep yen gambar "normal" padha kanggo kabeh supplier. Saiki kabeh wis apik, lan kita bisa entuk tandha ing grafana?
Kita bisa, nanging kita pancene ora pengin, amarga kita kudu milih salah siji opsi:
a) nggawe akeh grafik kanggo saben saluran kanthi kapisah (lan ngiringi kanthi lara)
b) ninggalake siji grafik karo kabeh saluran (lan ilang ing garis warni lan tandha khusus)

Trik kanggo pangolahan metrik ing Kapacitor

Kepiye carane sampeyan nindakake?

Maneh, ana conto wiwitan sing apik ing dokumentasi (Ngitung tarif antarane seri gabungan), bisa dideleng utawa dijupuk minangka basis ing masalah sing padha.

Apa sing ditindakake ing pungkasan:

  • gabung karo rong seri sajrone sawetara jam, dikelompokake miturut saluran;
  • isi seri kanthi klompok yen ora ana data;
  • mbandhingake rata-rata 10 menit pungkasan karo data sadurunge;
  • kita bengok-bengok yen ketemu;
  • kita nulis tarif sing diwilang lan tandha sing kedadeyan ing influxdb;
  • ngirim pesen migunani kanggo slack.

Ing mratelakake panemume, kita ngatur kanggo entuk kabeh kita wanted kanggo njaluk ing mburi (lan malah sethitik liyane karo handler adat) minangka apik sabisa.

Sampeyan bisa ndeleng github.com conto kode ΠΈ sirkuit minimal (graphviz) skrip asil.

Conto kode asil:

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)

Lan apa kesimpulane?

Kapacitor gedhe ing nindakake ngawasi-tandha karo Bunch saka klompok, Performing petungan tambahan adhedhasar metrik wis direkam, nindakake tumindak adat lan mlaku Tulisan (udf).

Rintangan kanggo mlebu ora dhuwur banget - coba yen grafana utawa alat liyane ora bisa nyukupi kekarepan sampeyan.

Source: www.habr.com

Add a comment