Wahrscheinlech, haut freet keen firwat et néideg ass Service Metriken ze sammelen. De nächste logesche Schrëtt ass eng Alarm fir déi gesammelt Metriken opzestellen, déi iwwer all Ofwäichunge vun den Donnéeën an de Kanäl, déi Iech bequem sinn (Mail, Slack, Telegram) informéieren. Am online Hotel Buchung Service all Metriken vun eise Servicer ginn an InfluxDB gegoss an a Grafana ugewisen, a Basisalarmung ass och do konfiguréiert. Fir Aufgaben wéi "Dir musst eppes ausrechnen a mat deem vergläichen", benotze mir Kapacitor.

Kapacitor ass Deel vum TICK Stack deen Metriken aus InfluxDB veraarbecht kann. Et kann e puer Miessunge matenee verbannen (matzemaachen), eppes nëtzlech aus de kritt Donnéeën berechent, d'Resultat zréck op InfluxDB schreiwen, eng Alarm op Slack / Telegram / Mail schécken.
De ganze Stack ass cool an detailléiert , awer et wäerten ëmmer nëtzlech Saachen sinn, déi net explizit an den Handbuch uginn sinn. An dësem Artikel hunn ech beschloss eng Rei vun esou nëtzlechen, net offensichtleche Tipps ze sammelen (d'Basissyntax vum TICKscipt gëtt beschriwwen ) a weisen wéi se applizéiert kënne ginn mat engem Beispill fir ee vun eise Problemer ze léisen.
Kommt go!
float & int, Berechnungsfehler
En absolut Standardproblem, duerch Kasten geléist:
var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))
Benotzt Standard ()
Wann en Tag/Feld net ausgefëllt ass, komme Berechnungsfehler op:
|default()
.tag('status', 'empty')
.field('value', 0)
ausfëllen join (bannenzeg vs baussenzeg)
Par défaut wäert de Joint Punkten ofwerfen wou et keng Donnéeën ass (bannenzeg).
Mat fill ('null') gëtt e baussenzege Bäitrëtt gemaach, duerno musst Dir e Standard () maachen an déi eidel Wäerter ausfëllen:
var data = res1
|join(res2)
.as('res1', 'res2)
.fill('null')
|default()
.field('res1.value', 0.0)
.field('res2.value', 100.0)
Et gëtt nach eng Nuance hei. Am Beispill hei uewen, wann eng vun de Serien (res1 oder res2) eidel ass, wäert déi resultéierend Serie (Daten) och eidel sinn. Et gi verschidde Ticketen iwwer dëst Thema op Github (, , ) - mir waarden op Fixen a leiden e bëssen.
Benotzt Konditioune bei Berechnungen (wann am Lambda)
|eval(lambda: if("value" > 0, true, false)
Déi lescht fënnef Minutte vun der Pipeline fir d'Period
Zum Beispill musst Dir d'Wäerter vun de leschte fënnef Minutten mat der Woch virdrun vergläichen. Dir kënnt zwee Chargen vun Daten an zwou getrennte Chargen huelen oder en Deel vun den Daten aus enger méi grousser Period extrahéieren:
|where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)
Eng Alternativ fir déi lescht fënnef Minutten wier eng BarrierNode ze benotzen, déi Daten virun der spezifizéierter Zäit ofschneiden:
|barrier()
.period(5m)
Beispiller fir Go Templates am Message ze benotzen
Schabloune entspriechen dem Format aus dem Package Drënner sinn e puer dacks begéint Puzzel.
wann-anescht
Mir setzen d'Saachen an Uerdnung a léisen d'Leit net nach eng Kéier mat Text aus:
|alert()
...
.message(
'{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
)
Zwee Zifferen nom Dezimalpunkt am Message
Verbesserung vun der Liesbarkeet vum Message:
|alert()
...
.message(
'now value is {{ index .Fields "value" | printf "%0.2f" }}'
)
Ausbau Verännerlechen am Message
Mir weisen méi Informatioun am Message fir d'Fro "Firwat jäizt et" ze beäntweren?
var warnAlert = 10
|alert()
...
.message(
'Today value less then '+string(warnAlert)+'%'
)
Eenzegaarteg Alarm Identifizéierer
Dëst ass eng noutwendeg Saach wann et méi wéi eng Grupp an den Donnéeën ass, soss gëtt nëmmen eng Alarm generéiert:
|alert()
...
.id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')
Benotzerdefinéiert Handler
Déi grouss Lëscht vun Handler enthält exec, wat erlaabt Iech Äre Skript mat de passéierte Parameteren auszeféieren (stdin) - Kreativitéit an näischt méi!
Ee vun eisen Douane ass e klenge Python Skript fir Notifikatiounen ze schécken.
Am Ufank wollte mir eng Autorisatioun geschützt Grafana Bild an engem Message schécken. Schreift duerno OK am Fuedem op déi viregt Alarm aus der selwechter Grupp, an net als separat Message. E bësse méi spéit - füügt de Message un de meeschte gemeinsame Feeler an de leschten X Minutten.
En separat Thema ass d'Kommunikatioun mat anere Servicer an all Aktiounen, déi duerch eng Alarm initiéiert ginn (nëmme wann Är Iwwerwaachung gutt genuch funktionnéiert).
E Beispill vun enger Handlerbeschreiwung, wou slack_handler.py eist selbstgeschriwwene Skript ass:
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"]
Wéi debuggen?
Optioun mat Logausgang
|log()
.level("error")
.prefix("something")
Kuckt (cli): kapacitor -url :9092 Logbicher lvl = Feeler
Optioun mat httpOut
Weist Daten an der aktueller Pipeline:
|httpOut('something')
Kuckt (kréien): :9092/kapacitor/v1/tasks/task_name/eppes
Ausféierung Diagramm
- All Aufgab gëtt en Ausféierungsbaum mat nëtzlechen Zuelen am Format zréck .
- Huelt e Block .
- Paste et an de Viewer, .
Wou soss kënnt Dir eng Rake kréien?
Zäitstempel am influxdb op Schreifweis
Zum Beispill setzen mir eng Alarm fir d'Zomm vun den Ufroen pro Stonn (groupBy(1h)) a wëllen d'Alarm notéieren, déi am influxdb geschitt ass (fir de Fakt vum Problem op der Grafik an Grafana schéin ze weisen).
influxDBOut () schreift den Zäitwäert vun der Alarm op den Zäitstempel entspriechend, de Punkt op der Grafik gëtt fréier / méi spéit geschriwwen wéi d'Alarm ukomm ass.
Wann Genauegkeet erfuerderlech ass: Mir schaffen ëm dëse Problem andeems mir e personaliséierten Handler ruffen, deen Daten op influxdb mam aktuellen Zäitstempel schreift.
docker, bauen an Détachement
Beim Start kann Kapacitor Aufgaben, Templates an Handler aus dem Verzeechnes, deen an der Konfiguratioun am Block [Lueden] spezifizéiert ass, lueden.
Fir eng Aufgab richteg ze kreéieren, braucht Dir déi folgend Saachen:
- Dateinumm - erweidert an Skript ID / Numm
- Typ - Stream / Batch
- dbrp - Schlësselwuert fir unzeginn wéi eng Datebank + Politik de Skript leeft (dbrp "Supplier." "autogen")
Wann eng Partie Aufgab keng Zeil mat dbrp enthält, refuséiert de ganze Service ze starten a wäert éierlech doriwwer am Logbuch schreiwen.
Am Chronograf, am Géigendeel, soll dës Linn net existéieren, et gëtt net duerch den Interface akzeptéiert a generéiert e Feeler.
Hack wann Dir e Container baut: Dockerfile geet mat -1 eraus wann et Zeilen mat //.+dbrp gëtt, wat Iech erlaabt direkt de Grond fir den Echec ze verstoen wann Dir de Bau montéiert.
bei engem zu vill
Beispill Aufgab: Dir musst de 95. Prozentsaz vun der Operatiounszäit vum Service fir eng Woch huelen, vergläicht all Minutt vun de leschten 10 mat dësem Wäert.
Dir kënnt net eng een-zu-vill Bäiträg maachen, lescht / mëttler / median iwwer e Grupp vu Punkte verwandelt den Node an e Stroum, de Feeler "kann net Kand falsch Kanten addéieren: Batch -> Stream" gëtt zréck.
D'Resultat vun enger Batch, als Variabel an engem Lambda Ausdrock, gëtt och net ersat.
Et gëtt eng Optioun fir déi néideg Zuelen aus der éischter Partie op eng Datei iwwer udf ze späicheren an dës Datei iwwer Sideload ze lueden.
Wat hu mir domat geléist?
Mir hunn ongeféier 100 Hotel Ubidder, jidderee vun hinnen kann e puer Verbindungen hunn, loosst eis et e Kanal nennen. Et gi ongeféier 300 vun dëse Kanäl, jidderee vun de Kanäl ka falen. Vun allen opgehollen Metriken iwwerwaache mir de Fehlerquote (Ufroen a Feeler).
Firwat net Grafana?
Feeler Alarmer konfiguréiert an Grafana hunn e puer Nodeeler. Verschiddener sinn kritesch, anerer kënnt Dir Är Aen zoumaachen, jee no Situatioun.
Grafana weess net wéi tëscht Miessunge + Alarm ze berechnen, mä mir brauchen en Taux (Ufro-Feeler) / Ufroen.
D'Feeler kucken béis:

A manner béis wann se mat erfollegräichen Ufroe gekuckt ginn:

Okay, mir kënnen den Taux am Service virum Grafana virbereeden, an e puer Fäll wäert dat funktionnéieren. Awer net bei eis, well ... fir all Kanal gëtt säin eegene Verhältnis als "normal" ugesinn, an d'Alarmer funktionnéieren no statesche Wäerter (mir kucken no se mat eisen Aen, änneren se wann et dacks Alarmer gëtt).
Dëst sinn Beispiller vu "normalen" fir verschidde Kanäl:


Mir ignoréieren de fréiere Punkt an huelen un datt dat "normalt" Bild fir all Fournisseur ähnlech ass. Elo ass alles gutt, a mir kënne mat Alarmer a Grafana duerchgoen?
Mir kënnen, awer mir wëllen et wierklech net, well mir eng vun den Optiounen musse wielen:
a) maacht vill Grafike fir all Kanal separat (a begleeden se schmerzhaft)
b) loosst en Diagramm mat alle Kanäl (a verléiert Iech an de faarwege Linnen a personaliséierten Alarmer)

Wéi hutt Dir et gemaach?
Nach eng Kéier gëtt et e gutt Startbeispill an der Dokumentatioun (), kënne gekuckt ginn oder als Basis an ähnleche Probleemer geholl ginn.
Wat mir um Enn gemaach hunn:
- mat zwou Serien an e puer Stonnen, gruppéiere vun Channels;
- fëllt d'Serie no Grupp aus wann et keng Donnéeën waren;
- vergläicht de Median vun de leschten 10 Minutten mat fréieren Donnéeën;
- mir ruffen wa mir eppes fannen;
- mir schreiwen déi berechent Tariffer an Alarmer déi am influxdb geschitt sinn;
- schéckt eng nëtzlech Noriicht ze slack.
Menger Meenung no hu mir et fäerdeg bruecht alles wat mir um Enn wollte kréien (an nach e bësse méi mat Custom Handleren) esou schéin wéi méiglech z'erreechen.
Dir kënnt et op github.com gesinn и déi doraus resultéierend Schrëft.
E Beispill vum resultéierende Code:
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)
Wat ass d'Conclusioun?
Kapacitor ass super fir Iwwerwaachungsalarmer mat enger Rëtsch Gruppéierungen auszeféieren, zousätzlech Berechnungen auszeféieren op Basis vu scho opgeholle Metriken, personaliséiert Aktiounen auszeféieren an Scripten (udf) auszeféieren.
D'Barrière fir d'Entrée ass net ganz héich - probéiert et wann Grafana oder aner Tools Är Wënsch net voll zefridden stellen.
Source: will.com
