Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar

Ehtimol, bugungi kunda hech kim nima uchun xizmat ko'rsatkichlarini yig'ish kerakligini so'ramaydi. Keyingi mantiqiy qadam - to'plangan ko'rsatkichlar uchun ogohlantirishni o'rnatish, u siz uchun qulay bo'lgan kanallarda (pochta, Slack, Telegram) ma'lumotlardagi har qanday og'ishlar haqida xabar beradi. Mehmonxonani onlayn bron qilish xizmatida Ostrovok.ru xizmatlarimizning barcha ko'rsatkichlari InfluxDB-ga quyiladi va Grafana-da ko'rsatiladi va asosiy ogohlantirishlar ham u erda sozlangan. "Siz biror narsani hisoblashingiz va u bilan taqqoslashingiz kerak" kabi vazifalar uchun biz Kapacitor-dan foydalanamiz.

Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar
Kapacitor InfluxDB dan ko'rsatkichlarni qayta ishlay oladigan TICK stekining bir qismidir. U bir nechta o'lchovlarni birlashtirishi (qo'shilishi), olingan ma'lumotlardan foydali narsani hisoblashi, natijani InfluxDB-ga yozishi, Slack/Telegram/mail-ga ogohlantirish yuborishi mumkin.

Butun stack salqin va batafsil hujjatlar, lekin har doim qo'llanmalarda aniq ko'rsatilmagan foydali narsalar bo'ladi. Ushbu maqolada men bir qator foydali, aniq bo'lmagan maslahatlarni to'plashga qaror qildim (TICKsciptning asosiy sintaksisi tasvirlangan) shu yerda) va bizning muammolarimizdan birini hal qilish misolidan foydalanib, ularni qanday qo'llash mumkinligini ko'rsating.

Yuringlar!

float & int, hisoblash xatolari

Kastalar orqali hal qilinadigan mutlaqo standart muammo:

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

Standart() dan foydalanish

Agar teg/maydon to'ldirilmasa, hisoblash xatolari yuzaga keladi:

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

qo'shishni to'ldirish (ichki va tashqi)

Odatiy bo'lib, qo'shilish hech qanday ma'lumot bo'lmagan (ichki) nuqtalarni olib tashlaydi.
Fill('null') bilan tashqi birlashma amalga oshiriladi, shundan so'ng siz default() ni bajarishingiz va bo'sh qiymatlarni to'ldirishingiz kerak:

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

Bu erda hali ham bir nuance bor. Yuqoridagi misolda seriyalardan biri (res1 yoki res2) bo'sh bo'lsa, natijada olingan qator (ma'lumotlar) ham bo'sh bo'ladi. Github-da ushbu mavzu bo'yicha bir nechta chiptalar mavjud (1633, 1871, 6967) - biz tuzatishlarni kutmoqdamiz va biroz azob chekamiz.

Hisoblashda shartlardan foydalanish (agar lambda bo'lsa)

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

Davr uchun quvur liniyasidan so'nggi besh daqiqa

Masalan, oxirgi besh daqiqaning qiymatlarini oldingi hafta bilan solishtirish kerak. Siz ikkita alohida to'plamda ikkita ma'lumot to'plamini olishingiz yoki kattaroq davrdan ma'lumotlarning bir qismini olishingiz mumkin:

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

Belgilangan vaqtdan oldin ma'lumotlarni o'chirib qo'yadigan BarrierNode-dan foydalanish oxirgi besh daqiqaga alternativa bo'ladi:

|barrier()
        .period(5m)

Xabarda Go shablonlaridan foydalanishga misollar

Shablonlar paketdagi formatga mos keladi matn.shablonQuyida tez-tez uchraydigan jumboqlar keltirilgan.

agar bo'lsa

Biz narsalarni tartibga solamiz va odamlarni matn bilan yana bir bor qo'zg'atmaymiz:

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

Xabardagi kasrdan keyin ikki raqam

Xabarni o'qish qobiliyatini yaxshilash:

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

Xabardagi o'zgaruvchilarni kengaytirish

Biz "Nega qichqirmoqda" degan savolga javob berish uchun xabarda ko'proq ma'lumotni ko'rsatamiz?

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

Noyob ogohlantirish identifikatori

Ma'lumotlarda bir nechta guruhlar mavjud bo'lganda, bu zaruriy narsa, aks holda faqat bitta ogohlantirish hosil bo'ladi:

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

Maxsus ishlov beruvchilar

Ishlovchilarning katta ro'yxati exec-ni o'z ichiga oladi, bu sizning skriptingizni o'tgan parametrlar (stdin) bilan bajarishga imkon beradi - ijodkorlik va boshqa hech narsa!

Bizning odatlarimizdan biri bo'shashmasdan bildirishnomalarni yuborish uchun kichik Python skriptidir.
Avvaliga biz avtorizatsiya bilan himoyalangan grafana rasmini xabar orqali yubormoqchi edik. Shundan so'ng, alohida xabar sifatida emas, balki xuddi shu guruhdan oldingi ogohlantirishga OK deb yozing. Birozdan keyin - xabarga oxirgi X daqiqada eng ko'p uchraydigan xatoni qo'shing.

Alohida mavzu - boshqa xizmatlar bilan aloqa va ogohlantirish bilan boshlangan har qanday harakatlar (faqat sizning monitoringingiz etarlicha yaxshi ishlayotgan bo'lsa).
Ishlovchi tavsifiga misol, bu erda slack_handler.py o'zimiz yozgan skriptimizdir:

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

Qanday qilib disk raskadrovka qilish kerak?

Jurnal chiqishi bilan variant

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

Watch (cli): kapacitor -url xost-yoki-ip:9092 jurnallar lvl=xato

httpOut bilan variant

Joriy quvur liniyasidagi ma'lumotlarni ko'rsatadi:

|httpOut('something')

Tomosha qiling (oling): xost-yoki-ip:9092/kapacitor/v1/tasks/task_name/bir narsa

Amalga oshirish sxemasi

  • Har bir vazifa formatdagi foydali raqamlarga ega ijro daraxtini qaytaradi grafviz.
  • Blokni oling dot.
  • Uni tomoshabinga joylashtiring, rohatlaning.

Rakni yana qayerdan olishingiz mumkin?

qayta yozishda influxdb da vaqt tamg'asi

Misol uchun, biz soatiga so'rovlar yig'indisi uchun ogohlantirish o'rnatdik (groupBy(1h)) va influxdb-da sodir bo'lgan ogohlantirishni yozib olishni xohlaymiz (muammo faktini grafanadagi grafikda chiroyli ko'rsatish uchun).

influxDBOut() signaldan vaqt tamg'asiga vaqt qiymatini yozadi; shunga ko'ra, diagrammadagi nuqta ogohlantirish kelganidan oldin/kechroq yoziladi.

Aniqlik talab qilinganda: biz joriy vaqt tamg'asi bilan influxdb ga ma'lumotlarni yozadigan maxsus ishlov beruvchini chaqirish orqali ushbu muammoni hal qilamiz.

docker, qurish va joylashtirish

Ishga tushganda, kapasitor [load] blokidagi konfiguratsiyada ko'rsatilgan katalogdan vazifalarni, shablonlarni va ishlov beruvchilarni yuklashi mumkin.

Vazifani to'g'ri yaratish uchun sizga quyidagilar kerak bo'ladi:

  1. Fayl nomi - skript identifikatori/nomiga kengaytirilgan
  2. Turi - oqim/to'plam
  3. dbrp - skript qaysi ma'lumotlar bazasi + siyosatida ishlayotganligini ko'rsatadigan kalit so'z (dbrp "ta'minotchi." "avtojen")

Agar ba'zi bir ommaviy topshiriqda dbrp bilan chiziq bo'lmasa, butun xizmat ishga tushishni rad etadi va bu haqda jurnalga halollik bilan yozadi.

Xronografda, aksincha, bu chiziq mavjud bo'lmasligi kerak, u interfeys orqali qabul qilinmaydi va xatolikni keltirib chiqaradi.

Konteynerni qurishda buzish: Agar //.+dbrp bilan qatorlar mavjud bo'lsa, Dockerfile -1 bilan chiqadi, bu konstruktsiyani yig'ishda muvaffaqiyatsizlik sababini darhol tushunishga imkon beradi.

ko'pchilikka qo'shiling

Misol topshiriq: siz bir hafta davomida xizmatning ish vaqtining 95 foizini olishingiz kerak, oxirgi 10 daqiqaning har bir daqiqasini ushbu qiymat bilan solishtiring.

Siz bittadan ko‘pga qo‘shilishni amalga oshira olmaysiz, nuqtalar guruhi bo‘yicha oxirgi/o‘rtacha/median tugunni oqimga aylantiradi, “bola mos kelmaydigan qirralarni qo‘shib bo‘lmaydi: to‘plam -> oqim” xatosi qaytariladi.

Lambda ifodasidagi o'zgaruvchi sifatida partiyaning natijasi ham almashtirilmaydi.

Birinchi partiyadan kerakli raqamlarni udf orqali faylga saqlash va bu faylni sideload orqali yuklash imkoniyati mavjud.

Bu bilan nimani hal qildik?

Bizda 100 ga yaqin mehmonxona yetkazib beruvchilar bor, ularning har biri bir nechta ulanishga ega bo'lishi mumkin, keling, uni kanal deb ataymiz. Ushbu kanallarning taxminan 300 tasi bor, ularning har biri tushib ketishi mumkin. Barcha qayd etilgan ko'rsatkichlardan biz xatolik darajasini (so'rovlar va xatolar) kuzatib boramiz.

Nega grafana emas?

Grafana-da sozlangan xato ogohlantirishlari bir nechta kamchiliklarga ega. Ba'zilari tanqidiy, ba'zilari esa vaziyatga qarab ko'zingizni yumishingiz mumkin.

Grafana o'lchovlar + ogohlantirish o'rtasida qanday hisoblashni bilmaydi, lekin bizga stavka (so'rovlar-xatolar)/so'rovlar kerak.

Xatolar yomon ko'rinadi:

Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar

Muvaffaqiyatli so'rovlar bilan ko'rilganda kamroq yomonlik:

Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar

Yaxshi, biz grafanadan oldin xizmatdagi tarifni oldindan hisoblashimiz mumkin va ba'zi hollarda bu ishlaydi. Lekin biznikida emas, chunki... har bir kanal uchun o'z nisbati "normal" deb hisoblanadi va ogohlantirishlar statik qiymatlarga muvofiq ishlaydi (biz ularni ko'zimiz bilan qidiramiz, agar tez-tez ogohlantirishlar bo'lsa, ularni o'zgartiramiz).

Bular turli kanallar uchun "normal" misollar:

Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar

Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar

Biz oldingi nuqtani e'tiborsiz qoldiramiz va "oddiy" rasm barcha etkazib beruvchilar uchun o'xshash deb hisoblaymiz. Endi hammasi yaxshi va biz grafanadagi ogohlantirishlar bilan ishlay olamizmi?
Biz qila olamiz, lekin biz buni xohlamaymiz, chunki biz variantlardan birini tanlashimiz kerak:
a) har bir kanal uchun alohida-alohida ko'plab grafikalar yarating (va ularga og'riqli tarzda hamrohlik qiling)
b) barcha kanallar bilan bitta diagramma qoldiring (va rangli chiziqlar va moslashtirilgan ogohlantirishlarda yo'qoling)

Kapasitorda ko'rsatkichlarni qayta ishlash uchun fokuslar

Buni qanday qildingiz?

Shunga qaramay, hujjatlarda yaxshi boshlang'ich misol bor (Birlashtirilgan seriyalar bo'yicha stavkalarni hisoblash), shunga o'xshash muammolarni ko'rib chiqish yoki asos sifatida olish mumkin.

Oxirida nima qildik:

  • bir necha soat ichida ikkita seriyaga qo'shiling, kanallar bo'yicha guruhlash;
  • agar ma'lumotlar bo'lmasa, seriyani guruh bo'yicha to'ldiring;
  • oxirgi 10 daqiqaning medianasini oldingi ma'lumotlar bilan solishtiring;
  • biror narsa topsak, baqiramiz;
  • biz influxdb da sodir bo'lgan hisoblangan stavkalar va ogohlantirishlarni yozamiz;
  • sustlikka foydali xabar yuboring.

Menimcha, biz oxirida erishmoqchi bo'lgan hamma narsaga (va hatto maxsus ishlov beruvchilar bilan bir oz ko'proq) iloji boricha chiroyli tarzda erisha oldik.

Siz github.com ga qarashingiz mumkin kod misoli и minimal sxema (grafik) olingan skript.

Olingan kodga misol:

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)

Xulosa nima?

Kapacitor bir nechta guruhlar bilan monitoring-ogohlantirishlarni amalga oshirishda, allaqachon qayd etilgan ko'rsatkichlar asosida qo'shimcha hisob-kitoblarni amalga oshirishda, maxsus harakatlarni bajarishda va skriptlarni (udf) ishga tushirishda ajoyibdir.

Kirish uchun to'siq unchalik yuqori emas - agar grafana yoki boshqa vositalar sizning xohishingizni to'liq qondirmasa, sinab ko'ring.

Manba: www.habr.com

a Izoh qo'shish