Bi îhtîmalek mezin, îro kes napirse çima pêdivî ye ku meriv pîvanên karûbarê berhev bike. Pêngava mentiqî ya din ev e ku meriv hişyariyek ji bo metrîkên berhevkirî saz bike, ku dê di kanalên ku ji we re rehet e (nameyê, Slack, Telegram) li ser her guheztina daneyan agahdar bike. Di xizmeta veqetandina otêlê ya serhêl de hemî pîvanên karûbarên me di InfluxDB de têne avêtin û li Grafana têne xuyang kirin, û hişyariya bingehîn jî li wir tê mîheng kirin. Ji bo karên wekî "divê hûn tiştek hesab bikin û pê re bidin ber hev," em Kapacitor bikar tînin.

Kapacitor beşek ji stûna TICK-ê ye ku dikare metrîkên ji InfluxDB pêvajoyê bike. Ew dikare çend pîvanan bi hev re girêbide (tevlî hev bibe), ji daneyên wergirtî tiştek bikêr hesab bike, encamê vegere InfluxDB binivîsîne, hişyariyek ji Slack/Telegram/nameyê re bişîne.
Tevahiya stack xweş û berfireh e , lê dê her gav tiştên kêrhatî hebin ku di destanan de bi eşkere nehatine destnîşan kirin. Di vê gotarê de, min biryar da ku ez çend serişteyên bikêr, ne-eşkere berhev bikim (hevoksaziya bingehîn a TICKscipt tê şirove kirin ) û nîşan bidin ka ew çawa dikarin bi karanîna mînakek çareserkirina yek ji pirsgirêkên me werin sepandin.
Em herin!
float & int, xeletiyên hesabkirinê
Pirsgirêkek bêkêmasî ya standard, ku bi kastanan ve tê çareser kirin:
var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))
Bikaranîna standard ()
Ger tag/qadek neyê dagirtin, dê xeletiyên hesabkirinê çêbibin:
|default()
.tag('status', 'empty')
.field('value', 0)
tevlêbûnê dagirin (hundirîn li hember derve)
Ji hêla xwerû, tevlêbûn dê xalên ku lê dane tune (hundirîn) ji holê rabike.
Bi fill('null'), dê tevlêbûnek derveyî were kirin, pişt re hûn hewce ne ku xwerû () bikin û nirxên vala dagirin:
var data = res1
|join(res2)
.as('res1', 'res2)
.fill('null')
|default()
.field('res1.value', 0.0)
.field('res2.value', 100.0)
Li vir hîn nuwazeyek heye. Di mînaka li jor de, heke yek ji rêze (res1 an res2) vala be, dê rêzika (dane) encam jî vala bibe. Li ser vê mijarê li ser Github gelek bilêt hene (, , ) - em li benda çareserî û êşek piçûk in.
Bikaranîna şertan di hesaban de (heke di lambda de)
|eval(lambda: if("value" > 0, true, false)
Ji bo heyamê pênc hûrdemên paşîn ji boriyê
Mînakî, hûn hewce ne ku nirxên pênc hûrdemên paşîn bi hefteya berê re bidin ber hev. Hûn dikarin du beşên daneyê di du beşên cûda de bigirin an jî beşek daneyê ji heyamek mezin derxînin:
|where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)
Alternatîfek ji bo pênc hûrdemên paşîn dê karanîna BarrierNode be, ku berî wextê diyarkirî daneyan qut dike:
|barrier()
.period(5m)
Nimûneyên karanîna şablonên Go di peyamê de
Şablon bi formata pakêtê re têkildar in Li jêr hinek puzzles ku pir caran têne dîtin hene.
ger-din
Em tiştan bi rêkûpêk dikin û careke din mirovan bi nivîsê nahêlin:
|alert()
...
.message(
'{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
)
Di peyamê de du reqe piştî xala dehiyê
Başkirina xwendina peyamê:
|alert()
...
.message(
'now value is {{ index .Fields "value" | printf "%0.2f" }}'
)
Berfirehkirina guherbarên di peyamê de
Em di peyamê de bêtir agahdarî nîşan didin ku bersiva pirsa "Çima diqîre" bidin?
var warnAlert = 10
|alert()
...
.message(
'Today value less then '+string(warnAlert)+'%'
)
Nasnameya hişyariya yekane
Dema ku di daneyê de ji yek komê zêdetir hebe ev tiştek pêdivî ye, wekî din tenê yek hişyarî dê were çêkirin:
|alert()
...
.id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')
Handler Custom
Di navnîşa mezin a hilberan de exec heye, ku dihêle hûn skrîpta xwe bi pîvanên derbasbûyî (stdin) bicîh bikin - afirîner û ne tiştek din!
Yek ji adetên me skrîptek Python a piçûk e ku ji bo şandina agahdariyan ji slackê re ye.
Di destpêkê de, me dixwest ku di peyamekê de wêneyek grafana parastî-destûr bişînin. Dûv re, di mijara hişyariya berê ya ji heman komê de OK binivîsin, û ne wekî peyamek cûda. Piçek paşe - di X deqeyên paşîn de xeletiya herî gelemperî li peyamê zêde bikin.
Mijarek veqetandî danûstendina bi karûbarên din re û her kiryarên ku ji hêla hişyariyê ve hatî destpêkirin e (tenê heke çavdêriya we têra xwe baş bixebite).
Nimûneyek ji ravekek handler, ku slack_handler.py skrîpta meya xwe-nivîsandî ye:
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"]
Meriv çawa debug bike?
Vebijêrk bi derketina têketinê
|log()
.level("error")
.prefix("something")
Temaşe bike (cli): kapacitor -url :9092 têketin lvl=çewtî
Vebijêrk bi httpOut
Daneyên di lûleya heyî de nîşan dide:
|httpOut('something')
Temaşe bikin (bigirin): :9092/kapacitor/v1/tasks/task_name/tiştek
Diagrama darvekirinê
- Her peywir darek darvekirinê bi hejmarên kêrhatî di formatê de vedigerîne .
- Blokek bistînin .
- Têkeve nav temaşevan, .
Li ku derê din hûn dikarin rakêşek bistînin?
mohra demê li influxdb li ser nivîsandinê
Mînakî, me hişyariyek ji bo yekjimariya daxwazan di saetekê de saz kir (groupBy(1h)) û dixwazin hişyariya ku di influxdb de çêbûye tomar bikin (ji bo ku rastiya pirsgirêkê li ser grafana di grafana de bi xweşikî nîşan bide).
influxDBOut() dê nirxa demê ji hişyariyê bigire heya mohra demjimêrê, li gorî vê yekê, xala li ser nexşeyê dê zûtir / dereng were nivîsandin.
Dema ku rastbûn hewce ye: em li dora vê pirsgirêkê dixebitin bi gazîkirina rêvekerek xwerû, ku dê daneyan li influxdb-ê bi nîşana demjimêra heyî binivîsîne.
doker, avakirin û bicihkirin
Di destpêkê de, kapacitor dikare peywiran, şablon û hilberan ji pelrêça ku di veavakirina di bloka [load] de hatî destnîşan kirin bar bike.
Ji bo afirandina peywirek rast, hûn hewceyên jêrîn hewce ne:
- Navê pelê - di nav id/navê nivîsarê de hate berfireh kirin
- Tîp - herikîn / kom
- dbrp - peyva sereke ku destnîşan dike ka kîjan databas + polîtîka skrîptê dimeşe (dbrp "dabînker.""xweser")
Ger hin peywira hevîrê xêzek bi dbrp re negire, dê tevahî karûbar dest pê neke û dê bi dilsozî li ser wê di têketinê de binivîsîne.
Di kronografê de, berevajî vê, divê ev rêz bi navgîniyê neyê pejirandin û xeletiyek çêdike.
Dema ku konteynir çêdike hack bike: Ger xetên bi //.+dbrp hebin, Dockerfile bi -1-ê derdikeve, ku dê bihêle ku hûn tavilê sedema têkçûnê dema berhevkirina çêkirinê fam bikin.
tevlî yek ji gelekan bibin
Karê Mînak: Pêdivî ye ku hûn hefteyek ji sedî 95-ê dema xebatê ya karûbarê bigirin, her deqeya 10-ên paşîn bi vê nirxê re bidin ber hev.
Hûn nekarin tevlêbûnek yek-bi-gelek bikin, paşîn/navgîn/navîn li ser komek xalan girêkê vediguherîne çemek, xeletiya "nikare hêlên hevhev ên zarokan lê zêde bike: hevî -> herik" dê were vegerandin.
Encama komekê, wekî guhêrbarek di bêjeyek lambda de jî nayê veguheztin.
Vebijarkek heye ku hûn hejmarên pêwîst ji koma yekem a pelê bi udf-ê hilînin û vê pelê bi barkirina kêlekê ve bar bikin.
Me bi vê yekê çi çareser kir?
Nêzîkî 100 dabînkerên otêlê me hene, her yek ji wan dikare çend pêwendiyan hebe, em jê re bibêjin kanalek. Nêzîkî 300 ji van kanalan hene, her kanalek dikare têkeve. Ji hemî pîvanên tomarkirî, em ê rêjeya xeletiyê (daxwaz û xeletî) bişopînin.
Çima ne grafana?
Hişyariyên çewtiyê yên ku li Grafana hatine mîheng kirin çend kêmasiyên xwe hene. Hinek krîtîk in, hinekan jî li gor rewşê hûn dikarin çavên xwe jê re bigirin.
Grafana nizane meriv di navbera pîvandin + hişyarkirinê de çawa hesab dike, lê ji me re rêjeyek (daxwaz-çewtî)/daxwaz hewce dike.
Çewtiyên xirab xuya dikin:

Gava ku bi daxwazên serketî têne dîtin kêmtir xirabî:

Baş e, em dikarin berî grafana rêjeya di karûbarê de pêş-hesab bikin, û di hin rewşan de ev ê bixebite. Lê ne di ya me de, ji ber ku ... ji bo her kanalek rêjeya wê "normal" tête hesibandin, û hişyarî li gorî nirxên statîk dixebitin (em bi çavên xwe li wan digerin, heke pir caran hişyarî hebin, wan diguhezînin).
Vana mînakên "normal" ji bo kanalên cihê ne:


Em xala berê paşguh dikin û texmîn dikin ku wêneya "normal" ji bo hemî peydakiran wekhev e. Naha her tişt baş e, û em dikarin bi hişyariyên di grafana de derbas bibin?
Em dikarin, lê em bi rastî naxwazin, ji ber ku divê em yek ji vebijarkan hilbijêrin:
a) ji bo her kanalek ji hev veqetandî gelek grafîkan çêbikin (û bi êş bi wan re bibin)
b) bi hemî kanalan re yek nexşeyê bihêlin (û di xetên rengîn û hişyariyên xwerû de winda bibin)

Te çawa kir?
Dîsa, di belgeyê de mînakek destpêkek baş heye (), dikare di pirsgirêkên wekhev de were şopandin an wekî bingehek were girtin.
Di dawiyê de me çi kir:
- di nav çend demjimêran de beşdarî du rêzefîlman bibin, li gorî kanalan kom bikin;
- Heke dane tunebûn rêzê bi komê tije bikin;
- navgîniya 10 deqîqeyên dawî bi daneyên berê re bidin ber hev;
- eger em tiştekî bibînin em diqîrin;
- em rêjeyên hesabkirî û hişyariyên ku di influxdb de derketine dinivîsin;
- ji slack re peyamek kêrhatî bişînin.
Bi dîtina min, me karî her tiştê ku me dixwest ku di dawiyê de bi dest bixin (û hetta piçekî din jî bi rêkarên xwerû) bi qasî ku pêkan xweş be.
Hûn dikarin li github.com binêrin и skrîpta encam.
Mînakek koda encam:
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)
Encam çi ye?
Kapacitor di pêkanîna çavdêrî-hişyariyên bi komek koman re, pêkanîna hesabên din ên li ser bingeha metrîkên jixwe hatine tomarkirin, pêkanîna kiryarên xwerû û xebitandina nivîsarên (udf) pir baş e.
Astengiya têketinê ne pir zêde ye - heke grafana an amûrên din daxwazên we bi tevahî têr nakin biceribînin.
Source: www.habr.com
