Tõenäoliselt ei küsi täna keegi, miks on vaja teenindusmõõdikuid koguda. Järgmine loogiline samm on kogutud mõõdikute jaoks märguande seadistamine, mis annab teile sobivates kanalites (post, Slack, Telegram) teada andmete kõrvalekalletest. Interneti-hotellibroneerimisteenuses Ostrovok.ru kõik meie teenuste mõõdikud valatakse InfluxDB-sse ja kuvatakse Grafanas ning seal on konfigureeritud ka põhihoiatused. Selliste ülesannete jaoks nagu "peate midagi arvutama ja sellega võrdlema" kasutame Kapacitorit.
Kapacitor on osa TICKi pinust, mis suudab töödelda InfluxDB mõõdikuid. Sellega saab mitu mõõtmist kokku ühendada (liituda), saadud andmetest midagi kasulikku välja arvutada, tulemuse InfluxDB-sse tagasi kirjutada, Slacki/Telegrami/maili hoiatuse saata.
Kogu virn on lahe ja detailne dokumentatsioon, kuid alati leidub kasulikke asju, mida juhendites selgesõnaliselt märgitud pole. Sellesse artiklisse otsustasin koguda mitmeid selliseid kasulikke, mitteilmseid näpunäiteid (kirjeldatakse TICKscipti põhisüntaksit siin) ja näidake, kuidas neid ühe meie probleemi lahendamise näitel rakendada.
Mine!
float & int, arvutusvead
Täiesti standardne probleem, mis lahendatakse kastide kaudu:
var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))
Vaikimisi tühistab liitumine punktid, kus andmed puuduvad (sisemine).
Funktsiooniga fill('null') teostatakse välimine ühendamine, mille järel peate tegema default() ja täitma tühjad väärtused:
var data = res1
|join(res2)
.as('res1', 'res2)
.fill('null')
|default()
.field('res1.value', 0.0)
.field('res2.value', 100.0)
Siin on ikkagi nüanss. Ülaltoodud näites, kui üks seeriatest (res1 või res2) on tühi, on ka tulemuseks olev seeria (andmed) tühi. Githubis on sellel teemal mitu piletit (1633, 1871, 6967) – ootame parandusi ja kannatame veidi.
Tingimuste kasutamine arvutustes (kui lambda)
|eval(lambda: if("value" > 0, true, false)
Viimased viis minutit torujuhtmest perioodi kohta
Näiteks peate võrdlema viimase viie minuti väärtusi eelmise nädalaga. Võite võtta kaks andmepaketti kahes eraldi komplektis või eraldada osa andmetest suuremast perioodist:
Viimase viie minuti alternatiiviks oleks kasutada BarrierNode'i, mis katkestab andmed enne määratud aega:
|barrier()
.period(5m)
Näiteid Go mallide kasutamisest sõnumis
Mallid vastavad pakendis olevale vormingule tekst.mallAllpool on mõned sageli esinevad mõistatused.
kui-veel
Paneme asjad paika ja ei vallanda inimesi uuesti tekstiga:
|alert()
...
.message(
'{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
)
Kaks numbrit pärast koma sõnumis
Sõnumi loetavuse parandamine:
|alert()
...
.message(
'now value is {{ index .Fields "value" | printf "%0.2f" }}'
)
Muutujate laiendamine sõnumis
Kuvame sõnumis lisateavet, et vastata küsimusele "Miks see karjub"?
var warnAlert = 10
|alert()
...
.message(
'Today value less then '+string(warnAlert)+'%'
)
Unikaalne hoiatuse identifikaator
See on vajalik, kui andmetes on rohkem kui üks rühm, vastasel juhul genereeritakse ainult üks hoiatus:
|alert()
...
.id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')
Kohandatud töötleja
Käsitlejate suures loendis on exec, mis võimaldab täita oma skripti läbitud parameetritega (stdin) - loovus ja ei midagi muud!
Üks meie tavadest on väike Pythoni skript, mis võimaldab saata teateid lõdvaks.
Alguses tahtsime sõnumiga saata autoriseerimiskaitsega grafana pilti. Seejärel kirjutage sama rühma eelmise märguande lõime OK, mitte eraldi sõnumina. Veidi hiljem – lisa sõnumile viimase X minuti kõige levinum viga.
Eraldi teema on suhtlemine teiste teenustega ja kõik hoiatusega algatatud toimingud (ainult siis, kui teie monitooring töötab piisavalt hästi).
Näide töötleja kirjeldusest, kus slack_handler.py on meie enda kirjutatud skript:
Näiteks seadistame märguande taotluste summa kohta tunnis (groupBy(1h)) ja tahame salvestada influxdb-s ilmnenud hoiatuse (et kaunilt näidata probleemi fakti grafanas graafikul).
influxDBOut() kirjutab ajaväärtuse hoiatusest ajatemplisse; vastavalt sellele kirjutatakse diagrammi punkt varem/hiljem kui hoiatus saabus.
Kui nõutakse täpsust: lahendame selle probleemi, kutsudes välja kohandatud töötleja, mis kirjutab andmed praeguse ajatempliga kataloogi influxdb.
dokkija, ehitamine ja juurutamine
Käivitamisel saab kapacitor laadida ülesandeid, malle ja töötlejaid ploki [load] konfiguratsioonis määratud kataloogist.
dbrp – märksõna, mis näitab, millises andmebaasis + poliitikas skript töötab (dbrp “tarnija.” “autogen”)
Kui mõni pakkülesanne ei sisalda rida koos dbrp-ga, keeldub kogu teenus käivitamast ja kirjutab selle ausalt logisse.
Vastupidi, kronografis ei tohiks seda rida eksisteerida, seda ei aktsepteerita liidese kaudu ja see tekitab vea.
Häkkimine konteineri ehitamisel: Dockerfile väljub -1-ga, kui on read //.+dbrp, mis võimaldab teil ehituse kokkupanemisel kohe aru saada ebaõnnestumise põhjusest.
liitu üks paljudega
Näidisülesanne: peate võtma teenuse nädala tööajast 95. protsentiili, võrrelge viimase 10 minuti iga minutit selle väärtusega.
Üks-mitmele liitumist ei saa teha, punktide grupi viimane/keskmine/mediaan muudab sõlme vooks, tagastatakse tõrketeade "ei saa lisada lapse mittesobivaid servi: partii -> voog".
Ka partii tulemust kui muutujat lambda-avaldises ei asendata.
On võimalus salvestada vajalikud numbrid esimesest partiist faili udf-i kaudu ja laadida see fail külglaadimise kaudu.
Mida me sellega lahendasime?
Meil on umbes 100 hotellipakkujat, igaühel neist võib olla mitu ühendust, nimetagem seda kanaliks. Neid kanaleid on umbes 300, kõik kanalid võivad välja kukkuda. Kõigist salvestatud mõõdikutest jälgime veamäära (päringud ja vead).
Miks mitte grafana?
Grafanas konfigureeritud veahoiatustel on mitmeid puudusi. Mõned on kriitilised, mõne ees võite olenevalt olukorrast silmad sulgeda.
Grafana ei oska mõõtmiste + hoiatamise vahel arvutada, aga meil on vaja määra (päringud-vead)/päringud.
Vead näevad vastikud välja:
Ja edukate taotlustega vaadates vähem kurja:
Olgu, saame teenuses enne grafana tariifi eelnevalt välja arvutada ja mõnel juhul see toimib. Aga mitte meie omas, sest... iga kanali jaoks peetakse oma suhet "normaalseks" ja hoiatused töötavad staatiliste väärtuste järgi (otsime neid silmadega, muudame neid, kui hoiatusi on sageli).
Need on erinevate kanalite "tavaliste" näited:
Jätame eelneva punkti tähelepanuta ja eeldame, et “tavaline” pilt on kõigi tarnijate puhul sarnane. Nüüd on kõik korras ja saame grafanas hoiatustega hakkama?
Me saame, aga me tõesti ei taha, sest peame valima ühe järgmistest valikutest:
a) koostage iga kanali kohta eraldi palju graafikuid (ja kandke neid valusalt kaasas)
b) jätke üks diagramm kõigi kanalitega (ja eksige värvilistesse joontesse ja kohandatud hoiatustesse)
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)
Mis on järeldus?
Kapacitor on suurepärane rühmitustega jälgimise-hoiatuste tegemiseks, juba salvestatud mõõdikute põhjal täiendavate arvutuste tegemiseks, kohandatud toimingute tegemiseks ja skriptide (udf) käivitamiseks.
Sisenemise barjäär ei ole väga kõrge – proovige järele, kui grafana või muud tööriistad ei rahulda täielikult teie soove.