Ամենայն հավանականությամբ, այսօր ոչ ոք չի հարցնում, թե ինչու է անհրաժեշտ հավաքագրել սպասարկման չափանիշները: Հաջորդ տրամաբանական քայլը հավաքագրված չափումների համար ահազանգի ստեղծումն է, որը կտեղեկացնի ձեզ հարմար ալիքներում (փոստ, Slack, Telegram) տվյալների ցանկացած շեղումների մասին: Հյուրանոցների առցանց ամրագրման ծառայությունում Ostrovok.ru Մեր ծառայությունների բոլոր ցուցանիշները լցվում են InfluxDB և ցուցադրվում Grafana-ում, և հիմնական ծանուցումը նույնպես կազմաձևված է այնտեղ: «Դուք պետք է ինչ-որ բան հաշվարկեք և համեմատեք դրա հետ» նման առաջադրանքների համար մենք օգտագործում ենք Kapacitor:
Kapacitor-ը TICK փաթեթի մի մասն է, որը կարող է մշակել չափումներ InfluxDB-ից: Այն կարող է միացնել մի քանի չափումներ միասին (միանալ), ստացված տվյալներից հաշվարկել ինչ-որ օգտակար բան, արդյունքը հետ գրել InfluxDB-ին, ծանուցում ուղարկել Slack/Telegram/mail:
Ամբողջ բուրգը զով է և մանրամասն փաստաթղթեր, բայց միշտ կլինեն օգտակար բաներ, որոնք հստակ նշված չեն ձեռնարկներում: Այս հոդվածում ես որոշեցի հավաքել մի շարք նման օգտակար, ոչ ակնհայտ խորհուրդներ (նկարագրված է TICKscipt-ի հիմնական շարահյուսությունը. այստեղ) և ցույց տվեք, թե ինչպես դրանք կարող են կիրառվել՝ օգտագործելով մեր խնդիրներից մեկի լուծման օրինակը:
Եկեք գնանք.
float & int, հաշվարկման սխալներ
Բացարձակապես ստանդարտ խնդիր, որը լուծվում է կաստաների միջոցով.
var alert_float = 5.0
var alert_int = 10
data|eval(lambda: float("value") > alert_float OR float("value") < float("alert_int"))
Օգտագործելով լռելյայն ()
Եթե պիտակը/դաշտը լրացված չէ, հաշվարկի սխալներ կառաջանան.
Լռելյայնորեն, միանալը կհեռացնի այն կետերը, որտեղ տվյալներ չկան (ներքին):
Fill('null')-ով կկատարվի արտաքին միացում, որից հետո պետք է կատարել default() և լրացնել դատարկ արժեքները.
var data = res1
|join(res2)
.as('res1', 'res2)
.fill('null')
|default()
.field('res1.value', 0.0)
.field('res2.value', 100.0)
Այստեղ դեռ մի նրբերանգ կա. Վերևի օրինակում, եթե շարքերից մեկը (res1 կամ res2) դատարկ է, ստացված շարքը (տվյալները) նույնպես դատարկ կլինի: Այս թեմայի վերաբերյալ մի քանի տոմսեր կան Github-ում (1633, 1871, 6967) – սպասում ենք շտկման ու մի փոքր տառապանք։
Օգտագործելով պայմանները հաշվարկներում (եթե լամբդա)
|eval(lambda: if("value" > 0, true, false)
Ժամանակահատվածի համար խողովակաշարից վերջին հինգ րոպեն
Օրինակ, դուք պետք է համեմատեք վերջին հինգ րոպեների արժեքները նախորդ շաբաթվա հետ: Դուք կարող եք վերցնել տվյալների երկու խմբաքանակ երկու առանձին խմբաքանակով կամ տվյալների մի մասը հանել ավելի մեծ ժամանակաշրջանից.
Վերջին հինգ րոպեների այլընտրանքը կլինի BarrierNode-ի օգտագործումը, որը կտրում է տվյալները նախքան նշված ժամանակը.
|barrier()
.period(5m)
Հաղորդագրության մեջ Go կաղապարների օգտագործման օրինակներ
Կաղապարները համապատասխանում են փաթեթի ձևաչափին text.կաղապարՍտորև ներկայացնում ենք մի քանի հաճախ հանդիպող գլուխկոտրուկներ:
եթե-ուրիշ
Մենք ամեն ինչ կարգի ենք բերում և մարդկանց հերթական անգամ տեքստով չենք հրահրում.
|alert()
...
.message(
'{{ if eq .Level "OK" }}It is ok now{{ else }}Chief, everything is broken{{end}}'
)
Հաղորդագրության մեջ տասնորդական կետից հետո երկու նիշ
Հաղորդագրության ընթեռնելիության բարելավում.
|alert()
...
.message(
'now value is {{ index .Fields "value" | printf "%0.2f" }}'
)
Հաղորդագրության մեջ փոփոխականների ընդլայնում
Մենք ավելի շատ տեղեկատվություն ենք ցուցադրում հաղորդագրության մեջ՝ «Ինչու է գոռում» հարցին պատասխանելու համար:
var warnAlert = 10
|alert()
...
.message(
'Today value less then '+string(warnAlert)+'%'
)
Զգուշացման եզակի նույնացուցիչ
Սա անհրաժեշտ բան է, երբ տվյալների մեջ կա մեկից ավելի խումբ, հակառակ դեպքում կստեղծվի միայն մեկ ահազանգ.
|alert()
...
.id('{{ index .Tags "myname" }}/{{ index .Tags "myfield" }}')
Պատվերով մշակող
Գործող սարքերի մեծ ցանկը ներառում է exec, որը թույլ է տալիս կատարել ձեր սցենարը անցած պարամետրերով (stdin)՝ ստեղծագործականություն և ոչ ավելին:
Մեր սովորույթներից մեկը փոքրիկ Python սկրիպտն է՝ Slack-ին ծանուցումներ ուղարկելու համար:
Սկզբում մենք ուզում էինք հաղորդագրության մեջ ուղարկել թույլտվությամբ պաշտպանված grafana նկար: Այնուհետև նույն խմբի նախորդ ահազանգի թեմայում գրեք OK, այլ ոչ որպես առանձին հաղորդագրություն: Մի փոքր ուշ - հաղորդագրությանը ավելացրեք վերջին X րոպեների ամենատարածված սխալը:
Առանձին թեմա է հաղորդակցությունը այլ ծառայությունների հետ և ցանկացած գործողություն, որը նախաձեռնվել է ահազանգով (միայն այն դեպքում, եթե ձեր մոնիտորինգը բավականաչափ լավ է աշխատում):
Գործադիրի նկարագրության օրինակ, որտեղ slack_handler.py-ը մեր ինքնագրված սցենարն է.
ժամանակի դրոշմակնիք influxdb-ում գրման հետադարձ կապի վրա
Օրինակ, մենք նախազգուշացնում ենք ժամում հարցումների գումարի համար (groupBy(1h)) և ցանկանում ենք արձանագրել ահազանգը, որը տեղի է ունեցել influxdb-ում (գրաֆանայի գրաֆիկի վրա խնդրի փաստը գեղեցիկ ցույց տալու համար):
influxDBOut()-ը կգրի ժամանակի արժեքը ահազանգից մինչև ժամանակի դրոշմը, համապատասխանաբար, գծապատկերի կետը գրվելու է ավելի վաղ/ուշ, քան ահազանգը ժամանել է:
Երբ պահանջվում է ճշգրտություն. մենք աշխատում ենք այս խնդրի շուրջ՝ զանգահարելով հատուկ մշակող, որը տվյալներ կգրի influxdb-ում՝ ընթացիկ ժամանակի դրոշմակնիքով:
docker, կառուցել և տեղակայում
Գործարկման ժամանակ capacitor-ը կարող է բեռնել առաջադրանքներ, ձևանմուշներ և մշակիչներ [load] բլոկի կոնֆիգուրայում նշված գրացուցակից:
Առաջադրանքը ճիշտ ստեղծելու համար ձեզ անհրաժեշտ են հետևյալ բաները.
Ֆայլի անուն – ընդլայնվել է սցենարի id/անունով
Տեսակ – հոսք/խմբաքանակ
dbrp – հիմնաբառ՝ ցույց տալու համար, թե որ տվյալների բազայում + քաղաքականությունում է աշխատում սկրիպտը (dbrp «մատակարար», «ավտոգեն»)
Եթե որոշ խմբաքանակային առաջադրանք չի պարունակում dbrp-ով տող, ամբողջ ծառայությունը կհրաժարվի սկսելուց և անկեղծորեն կգրի դրա մասին մատյանում:
Chronograf-ում, ընդհակառակը, այս տողը չպետք է գոյություն ունենա, այն չի ընդունվում ինտերֆեյսի միջոցով և առաջացնում է սխալ:
Հակել կոնտեյներ կառուցելիս. Dockerfile-ը դուրս է գալիս -1-ով, եթե կան //.+dbrp տողեր, ինչը թույլ կտա անմիջապես հասկանալ անհաջողության պատճառը build-ը հավաքելիս։
միանալ մեկին շատերին
Օրինակ առաջադրանք. դուք պետք է վերցնեք ծառայության աշխատանքի ժամանակի 95-րդ տոկոսը մեկ շաբաթվա ընթացքում, համեմատեք վերջին 10 րոպեի յուրաքանչյուր րոպեն այս արժեքի հետ:
Դուք չեք կարող կատարել մեկից շատերի միացում, վերջին/միջին/միջին միավորները մի խումբ կետերի վրա հանգույցը վերածում է հոսքի, «չի կարելի ավելացնել երեխայի անհամապատասխան եզրեր. փաթեթ -> հոսք» սխալը կվերադարձվի:
Փաթեթի արդյունքը, որպես փոփոխական լամբդա արտահայտության մեջ, նույնպես չի փոխարինվում:
Հնարավորություն կա պահպանել անհրաժեշտ թվերը առաջին խմբաքանակից ֆայլ udf-ի միջոցով և բեռնել այս ֆայլը sideload-ի միջոցով:
Ի՞նչ լուծեցինք սրանով։
Մենք ունենք մոտ 100 հյուրանոցի մատակարար, նրանցից յուրաքանչյուրը կարող է ունենալ մի քանի կապ, եկեք դա անվանենք ալիք։ Այս ալիքները մոտավորապես 300-ն են, ալիքներից յուրաքանչյուրը կարող է ընկնել: Բոլոր գրանցված ցուցանիշներից մենք կվերահսկենք սխալի մակարդակը (խնդրանքները և սխալները):
Ինչու՞ ոչ գրաֆանա:
Grafana-ում կազմաձևված սխալի մասին ահազանգերն ունեն մի քանի թերություններ: Ոմանք քննադատական են, ոմանց վրա կարող եք փակել ձեր աչքերը՝ կախված իրավիճակից:
Grafana-ն չգիտի ինչպես հաշվարկել չափումների + ծանուցման միջև, բայց մեզ պետք է դրույքաչափ (հարցումներ-սխալներ)/հարցումներ։
Սխալները տհաճ տեսք ունեն.
Եվ ավելի քիչ չարիք, երբ դիտվում է հաջող խնդրանքներով.
Լավ, մենք կարող ենք նախապես հաշվարկել ծառայության դրույքաչափը մինչև grafana, և որոշ դեպքերում դա կաշխատի: Բայց ոչ մեր մեջ, քանի որ... յուրաքանչյուր ալիքի համար իր սեփական հարաբերակցությունը համարվում է «նորմալ», և ազդանշաններն աշխատում են ստատիկ արժեքների համաձայն (մենք փնտրում ենք դրանք մեր աչքերով, փոխում ենք դրանք, եթե հաճախակի ահազանգեր են լինում):
Սրանք «նորմալ»-ի օրինակներ են տարբեր ալիքների համար.
Մենք անտեսում ենք նախորդ կետը և ենթադրում ենք, որ «նորմալ» պատկերը նման է բոլոր մատակարարների համար։ Հիմա ամեն ինչ կարգին է, և մենք կարո՞ղ ենք յոլա գնալ grafana-ում ահազանգերով:
Մենք կարող ենք, բայց մենք իսկապես չենք ուզում, քանի որ մենք պետք է ընտրենք տարբերակներից մեկը.
ա) յուրաքանչյուր ալիքի համար առանձին գծապատկերներ կազմեք (և ցավալիորեն ուղեկցեք դրանք)
բ) թողեք մեկ գծապատկեր բոլոր ալիքներով (և կորեք գունավոր գծերի և հարմարեցված ազդանշանների մեջ)
միանալ երկու սերիաների մի քանի ժամում, խմբավորելով ըստ ալիքների;
լրացրեք շարքը ըստ խմբի, եթե տվյալներ չկան.
համեմատել վերջին 10 րոպեի միջինը նախորդ տվյալների հետ.
մենք գոռում ենք, եթե ինչ-որ բան գտնենք;
մենք գրում ենք հաշվարկված դրույքաչափերը և ահազանգերը, որոնք տեղի են ունեցել influxdb-ում.
ուղարկել օգտակար հաղորդագրություն Slack-ին:
Իմ կարծիքով, մենք կարողացանք հնարավորինս գեղեցիկ կերպով հասնել այն ամենին, ինչ ցանկանում էինք ստանալ վերջում (և նույնիսկ մի փոքր ավելին՝ մաքսային մշակիչներով):
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)
Ո՞րն է եզրակացությունը։
Kapacitor-ը հիանալի կերպով կատարում է մոնիտորինգ-ազդարարումներ մի շարք խմբավորումների հետ, կատարել լրացուցիչ հաշվարկներ արդեն գրանցված չափումների հիման վրա, կատարել հատուկ գործողություններ և գործարկել սցենարներ (udf):
Մուտքի խոչընդոտը շատ բարձր չէ. փորձեք այն, եթե grafana-ն կամ այլ գործիքները լիովին չեն բավարարում ձեր ցանկությունները: