VisticamÄk, Å”odien neviens nejautÄ, kÄpÄc ir jÄvÄc servisa rÄdÄ«tÄji. NÄkamais loÄ£iskais solis ir ievÄktajiem rÄdÄ«tÄjiem iestatÄ«t brÄ«dinÄjumu, kas jums Ärtos kanÄlos (pasts, Slack, Telegram) ziÅos par jebkÄdÄm novirzÄm datos. TieÅ”saistes viesnÄ«cu rezervÄÅ”anas pakalpojumÄ visi mÅ«su pakalpojumu rÄdÄ«tÄji tiek ievietoti InfluxDB un parÄdÄ«ti Grafana, un tur ir arÄ« konfigurÄti pamata brÄ«dinÄjumi. TÄdiem uzdevumiem kÄ ājums kaut kas jÄaprÄÄ·ina un jÄsalÄ«dzina ar toā, mÄs izmantojam Kapacitor.

Kapacitor ir daļa no TICK steka, kas var apstrÄdÄt metriku no InfluxDB. Tas var savienot vairÄkus mÄrÄ«jumus kopÄ (savienoties), aprÄÄ·inÄt kaut ko noderÄ«gu no saÅemtajiem datiem, atrakstÄ«t rezultÄtu atpakaļ uz InfluxDB, nosÅ«tÄ«t brÄ«dinÄjumu uz Slack/Telegram/mail.
Visa kaudze ir forÅ”a un detalizÄta , taÄu vienmÄr bÅ«s noderÄ«gas lietas, kas rokasgrÄmatÄs nav skaidri norÄdÄ«tas. Å ajÄ rakstÄ es nolÄmu apkopot vairÄkus Å”Ädus noderÄ«gus, nepÄrprotamus padomus (ir aprakstÄ«ta TICKscipt pamata sintakse ) un parÄdiet, kÄ tos var pielietot, izmantojot piemÄru, kÄ atrisinÄt kÄdu no mÅ«su problÄmÄm.
Iesim!
float & int, aprÄÄ·inu kļūdas
AbsolÅ«ti standarta problÄma, kas atrisinÄta caur kastÄm:
var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))
Izmantojot noklusÄjuma ()
Ja atzÄ«me/lauks nav aizpildÄ«ts, aprÄÄ·inos var rasties kļūdas:
|default()
.tag('status', 'empty')
.field('value', 0)
aizpildiet savienojumu (iekÅ”Äjais vai ÄrÄjais)
PÄc noklusÄjuma pievienoÅ”anÄs atmetÄ«s punktus, kur nav datu (iekÅ”Äjie).
Izmantojot fill('null'), tiks veikta ÄrÄja savienoÅ”ana, pÄc kuras jums ir jÄveic noklusÄjuma () un jÄaizpilda tukÅ”Äs vÄrtÄ«bas:
var data = res1
|join(res2)
.as('res1', 'res2)
.fill('null')
|default()
.field('res1.value', 0.0)
.field('res2.value', 100.0)
Å eit joprojÄm ir nianse. IepriekÅ” minÄtajÄ piemÄrÄ, ja viena no sÄrijÄm (res1 vai res2) ir tukÅ”a, iegÅ«tÄ sÄrija (dati) arÄ« bÅ«s tukÅ”a. VietnÄ Github ir vairÄkas biļetes par Å”o tÄmu (, , ) ā gaidÄm labojumus un nedaudz cieÅ”am.
NosacÄ«jumu izmantoÅ”ana aprÄÄ·inos (ja lambda)
|eval(lambda: if("value" > 0, true, false)
PÄdÄjÄs piecas minÅ«tes no cauruļvada periodam
PiemÄram, jums ir jÄsalÄ«dzina pÄdÄjo piecu minūŔu vÄrtÄ«bas ar iepriekÅ”Äjo nedÄļu. Varat Åemt divas datu paketes divÄs atseviŔķÄs paketÄs vai iegÅ«t daļu datu no lielÄka perioda:
|where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)
AlternatÄ«va pÄdÄjÄm piecÄm minÅ«tÄm bÅ«tu izmantot BarrierNode, kas izslÄdz datus pirms norÄdÄ«tÄ laika:
|barrier()
.period(5m)
Go veidÅu izmantoÅ”anas piemÄri ziÅojumÄ
Veidnes atbilst formÄtam no iepakojuma ZemÄk ir dažas bieži sastopamas mÄ«klas.
ja-vÄl
MÄs sakÄrtojam lietas un vairs neizraisÄm cilvÄkus ar tekstu:
|alert()
...
.message(
'{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
)
Divi cipari aiz komata ziÅojumÄ
ZiÅojuma lasÄmÄ«bas uzlaboÅ”ana:
|alert()
...
.message(
'now value is {{ index .Fields "value" | printf "%0.2f" }}'
)
MainÄ«go izvÄrÅ”ana ziÅojumÄ
ZiÅojumÄ parÄdÄm vairÄk informÄcijas, lai atbildÄtu uz jautÄjumu āKÄpÄc tÄ kliedzā?
var warnAlert = 10
|alert()
...
.message(
'Today value less then '+string(warnAlert)+'%'
)
UnikÄls brÄ«dinÄjuma identifikators
Tas ir nepiecieÅ”ams, ja datos ir vairÄk nekÄ viena grupa, pretÄjÄ gadÄ«jumÄ tiks Ä£enerÄts tikai viens brÄ«dinÄjums:
|alert()
...
.id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')
PielÄgots apstrÄdÄtÄjs
LielajÄ apdarinÄtÄju sarakstÄ ir iekļauts exec, kas ļauj izpildÄ«t savu skriptu ar nodotajiem parametriem (stdin) - radoÅ”ums un nekas vairÄk!
Viena no mÅ«su paražÄm ir neliels Python skripts paziÅojumu nosÅ«tīŔanai uz atslÄbumu.
SÄkumÄ mÄs gribÄjÄm nosÅ«tÄ«t ar autorizÄciju aizsargÄtu grafana attÄlu ziÅojumÄ. PÄc tam ierakstiet OK pavedienÄ uz iepriekÅ”Äjo brÄ«dinÄjumu no tÄs paÅ”as grupas, nevis kÄ atseviŔķu ziÅojumu. Nedaudz vÄlÄk - pievienojiet ziÅojumam biežÄko kļūdu pÄdÄjo X minūŔu laikÄ.
AtseviŔķa tÄma ir saziÅa ar citiem dienestiem un jebkÄdas darbÄ«bas, ko ierosina brÄ«dinÄjums (tikai tad, ja jÅ«su uzraudzÄ«ba darbojas pietiekami labi).
ApdarinÄtÄja apraksta piemÄrs, kur slack_handler.py ir mÅ«su paÅ”u rakstÄ«tais skripts:
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"]
KÄ atkļūdot?
IespÄja ar žurnÄla izvadi
|log()
.level("error")
.prefix("something")
SkatÄ«ties (cli): kapacitor -url :9092 žurnÄli lvl=kļūda
Opcija ar httpOut
RÄda datus paÅ”reizÄjÄ konveijerÄ:
|httpOut('something')
SkatÄ«ties (saÅemt): :9092/kapacitor/v1/tasks/task_name/something
Izpildes shÄma
- Katrs uzdevums atgriež izpildes koku ar noderÄ«giem skaitļiem formÄtÄ .
- PaÅemiet bloku .
- IelÄ«mÄjiet to skatÄ«tÄjÄ, .
Kur vÄl var dabÅ«t grÄbekli?
laika zÄ«mogs influxdb ierakstīŔanas laikÄ
PiemÄram, mÄs iestatÄm brÄ«dinÄjumu par pieprasÄ«jumu summu stundÄ (groupBy(1h)) un vÄlamies reÄ£istrÄt brÄ«dinÄjumu, kas notika influxdb (lai skaisti parÄdÄ«tu problÄmas faktu grafana diagrammÄ).
influxDBOut() ierakstÄ«s laika vÄrtÄ«bu no brÄ«dinÄjuma lÄ«dz laika zÄ«mogam; attiecÄ«gi punkts diagrammÄ tiks ierakstÄ«ts agrÄk/vÄlÄk nekÄ brÄ«dinÄjums ir saÅemts.
Ja nepiecieÅ”ama precizitÄte: mÄs apstrÄdÄjam Å”o problÄmu, izsaucot pielÄgotu apdarinÄtÄju, kas ierakstÄ«s datus influxdb ar paÅ”reizÄjo laikspiedolu.
doks, uzbūve un izvietoŔana
StartÄÅ”anas laikÄ kapacitor var ielÄdÄt uzdevumus, veidnes un apstrÄdÄtÄjus no direktorija, kas norÄdÄ«ta konfigurÄcijÄ blokÄ [load].
Lai pareizi izveidotu uzdevumu, ir nepiecieÅ”amas Å”Ädas lietas:
- Faila nosaukums ā izvÄrsts skripta ID/nosaukumÄ
- Tips ā straume/partija
- dbrp ā atslÄgvÄrds, kas norÄda, kurÄ datu bÄzÄ + politikÄ skripts darbojas (dbrp āpiegÄdÄtÄjs.ā āautogenā)
Ja kÄdÄ pakeÅ”u uzdevumÄ nav rindas ar dbrp, viss pakalpojums atteiksies sÄkt un godÄ«gi ierakstÄ«s par to žurnÄlÄ.
Gluži pretÄji, hronogrÄfijÄ Å”ai lÄ«nijai nevajadzÄtu pastÄvÄt; tÄ netiek pieÅemta caur interfeisu un rada kļūdu.
Uzlauzt, veidojot konteineru: Dockerfile iziet ar -1, ja ir rindas ar //.+dbrp, kas ļaus jums uzreiz saprast neveiksmes iemeslu, montÄjot bÅ«vi.
pievienoties viens pret daudziem
Uzdevuma piemÄrs: jums ir jÄÅem pakalpojuma darbÄ«bas laika nedÄļas 95. procentile, salÄ«dziniet katru minÅ«ti no pÄdÄjÄm 10 ar Å”o vÄrtÄ«bu.
JÅ«s nevarat veikt savienojumu viens pret daudziem, pÄdÄjais/vidÄjais/vidÄjais punktu grupÄ pÄrvÄrÅ” mezglu par straumi, tiks atgriezta kļūda ānevar pievienot pakÄrtotÄs malas neatbilstoÅ”as: partija ā> straumeā.
ArÄ« partijas rezultÄts kÄ mainÄ«gais lambda izteiksmÄ netiek aizstÄts.
Ir iespÄja saglabÄt nepiecieÅ”amos numurus no pirmÄs partijas failÄ, izmantojot udf, un ielÄdÄt Å”o failu, izmantojot sÄnu ielÄdi.
Ko mÄs ar Å”o atrisinÄjÄm?
Mums ir ap 100 viesnÄ«cu piegÄdÄtÄju, katram var bÅ«t vairÄki pieslÄgumi, sauksim to par kanÄlu. Å o kanÄlu ir aptuveni 300, katrs no tiem var nokrist. No visiem reÄ£istrÄtajiem rÄdÄ«tÄjiem mÄs uzraudzÄ«sim kļūdu lÄ«meni (pieprasÄ«jumus un kļūdas).
KÄpÄc ne grafana?
Grafana konfigurÄtajiem kļūdu brÄ«dinÄjumiem ir vairÄki trÅ«kumi. Daži no tiem ir kritiski, pret dažiem jÅ«s varat aizvÄrt acis atkarÄ«bÄ no situÄcijas.
Grafana neprot rÄÄ·inÄt starp mÄrÄ«jumiem + brÄ«dinÄjumiem, bet mums vajag Ätrumu (pieprasÄ«jumi-kļūdas)/pieprasÄ«jumi.
Kļūdas izskatÄs nepatÄ«kamas:

Un mazÄk ļaunuma, ja to aplÅ«ko ar veiksmÄ«giem pieprasÄ«jumiem:

Labi, mÄs varam iepriekÅ” aprÄÄ·inÄt likmi pakalpojumÄ pirms grafana, un dažos gadÄ«jumos tas darbosies. Bet ne pie mums, jo... katram kanÄlam tÄ attiecÄ«ba tiek uzskatÄ«ta par ānormÄluā, un brÄ«dinÄjumi darbojas saskaÅÄ ar statiskÄm vÄrtÄ«bÄm (mÄs tos meklÄjam ar acÄ«m, mainÄm, ja ir bieži brÄ«dinÄjumi).
Å ie ir āparastoā dažÄdu kanÄlu piemÄri:


MÄs ignorÄjam iepriekÅ”Äjo punktu un pieÅemam, ka āparastÄā aina visiem piegÄdÄtÄjiem ir lÄ«dzÄ«ga. Tagad viss ir kÄrtÄ«bÄ, un mÄs varam iztikt ar brÄ«dinÄjumiem grafana?
MÄs varam, bet mÄs ļoti nevÄlamies, jo mums ir jÄizvÄlas viena no iespÄjÄm:
a) izveidojiet daudz grafiku katram kanÄlam atseviŔķi (un sÄpÄ«gi pavadiet tos)
b) atstÄjiet vienu diagrammu ar visiem kanÄliem (un pazÅ«diet krÄsainajÄs lÄ«nijÄs un pielÄgotajos brÄ«dinÄjumos)

KÄ tu to izdarÄ«ji?
ArÄ« dokumentÄcijÄ ir labs sÄkuma piemÄrs (), var aplÅ«kot vai Åemt par pamatu lÄ«dzÄ«gÄs problÄmÄs.
Ko mÄs beigÄs izdarÄ«jÄm:
- pievienoties divÄm sÄrijÄm dažu stundu laikÄ, grupÄjot pÄc kanÄliem;
- aizpildiet sÄriju pa grupÄm, ja datu nebija;
- salÄ«dzinÄt pÄdÄjo 10 minūŔu mediÄnu ar iepriekÅ”Äjiem datiem;
- mÄs kliedzam, ja kaut ko atrodam;
- mÄs rakstÄm aprÄÄ·inÄtÄs likmes un brÄ«dinÄjumus, kas radÄs influxdb;
- nosÅ«tiet noderÄ«gu ziÅu slack.
ManuprÄt, mums izdevÄs beigÄs sasniegt visu, ko vÄlÄjÄmies iegÅ«t (un pat nedaudz vairÄk ar pielÄgotiem hendleriem) pÄc iespÄjas skaistÄk.
Jūs varat apskatīt github.com и iegūtais skripts.
IegÅ«tÄ koda piemÄrs:
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)
KÄds ir secinÄjums?
Kapacitor lieliski spÄj veikt uzraudzÄ«bas brÄ«dinÄjumus ar virkni grupÄjumu, veikt papildu aprÄÄ·inus, pamatojoties uz jau reÄ£istrÄtiem rÄdÄ«tÄjiem, veikt pielÄgotas darbÄ«bas un palaist skriptus (udf).
IekļūŔanas barjera nav Ä«paÅ”i augsta ā izmÄÄ£iniet to, ja grafana vai citi instrumenti pilnÄ«bÄ neapmierina jÅ«su vÄlmes.
Avots: www.habr.com
