Capacitor-ում չափումների մշակման հնարքներ

Ամենայն հավանականությամբ, այսօր ոչ ոք չի հարցնում, թե ինչու է անհրաժեշտ հավաքագրել սպասարկման չափանիշները: Հաջորդ տրամաբանական քայլը հավաքագրված չափումների համար ահազանգի ստեղծումն է, որը կտեղեկացնի ձեզ հարմար ալիքներում (փոստ, Slack, Telegram) տվյալների ցանկացած շեղումների մասին: Հյուրանոցների առցանց ամրագրման ծառայությունում Ostrovok.ru Մեր ծառայությունների բոլոր ցուցանիշները լցվում են InfluxDB և ցուցադրվում Grafana-ում, և հիմնական ծանուցումը նույնպես կազմաձևված է այնտեղ: «Դուք պետք է ինչ-որ բան հաշվարկեք և համեմատեք դրա հետ» նման առաջադրանքների համար մենք օգտագործում ենք Kapacitor:

Capacitor-ում չափումների մշակման հնարքներ
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"))

Օգտագործելով լռելյայն ()

Եթե ​​պիտակը/դաշտը լրացված չէ, հաշվարկի սխալներ կառաջանան.

|default()
        .tag('status', 'empty')
        .field('value', 0)

լրացնել միացում (ներքին ընդդեմ արտաքին)

Լռելյայնորեն, միանալը կհեռացնի այն կետերը, որտեղ տվյալներ չկան (ներքին):
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)

Ժամանակահատվածի համար խողովակաշարից վերջին հինգ րոպեն

Օրինակ, դուք պետք է համեմատեք վերջին հինգ րոպեների արժեքները նախորդ շաբաթվա հետ: Դուք կարող եք վերցնել տվյալների երկու խմբաքանակ երկու առանձին խմբաքանակով կամ տվյալների մի մասը հանել ավելի մեծ ժամանակաշրջանից.

 |where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)

Վերջին հինգ րոպեների այլընտրանքը կլինի 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-ը մեր ինքնագրված սցենարն է.

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"]

Ինչպե՞ս կարգաբերել:

Տարբերակ մատյան ելքով

|log()
      .level("error")
      .prefix("something")

Դիտեք (cli): kapacitor -url host-or-ip:9092 logs lvl=սխալ

Տարբերակ httpOut-ով

Ցույց է տալիս ընթացիկ խողովակաշարի տվյալները.

|httpOut('something')

Դիտեք (ստացեք): host-or-ip:9092/kapacitor/v1/tasks/task_name/something

Կատարման դիագրամ

  • Յուրաքանչյուր առաջադրանք վերադարձնում է կատարման ծառ՝ ձևաչափով օգտակար թվերով գրաֆվիզ.
  • Վերցրեք մի բլոկ օժիտ.
  • Տեղադրեք այն դիտողի մեջ, վայելել.

Էլ որտեղի՞ց կարելի է փոցխ ստանալ:

ժամանակի դրոշմակնիք influxdb-ում գրման հետադարձ կապի վրա

Օրինակ, մենք նախազգուշացնում ենք ժամում հարցումների գումարի համար (groupBy(1h)) և ցանկանում ենք արձանագրել ահազանգը, որը տեղի է ունեցել influxdb-ում (գրաֆանայի գրաֆիկի վրա խնդրի փաստը գեղեցիկ ցույց տալու համար):

influxDBOut()-ը կգրի ժամանակի արժեքը ահազանգից մինչև ժամանակի դրոշմը, համապատասխանաբար, գծապատկերի կետը գրվելու է ավելի վաղ/ուշ, քան ահազանգը ժամանել է:

Երբ պահանջվում է ճշգրտություն. մենք աշխատում ենք այս խնդրի շուրջ՝ զանգահարելով հատուկ մշակող, որը տվյալներ կգրի influxdb-ում՝ ընթացիկ ժամանակի դրոշմակնիքով:

docker, կառուցել և տեղակայում

Գործարկման ժամանակ capacitor-ը կարող է բեռնել առաջադրանքներ, ձևանմուշներ և մշակիչներ [load] բլոկի կոնֆիգուրայում նշված գրացուցակից:

Առաջադրանքը ճիշտ ստեղծելու համար ձեզ անհրաժեշտ են հետևյալ բաները.

  1. Ֆայլի անուն – ընդլայնվել է սցենարի id/անունով
  2. Տեսակ – հոսք/խմբաքանակ
  3. dbrp – հիմնաբառ՝ ցույց տալու համար, թե որ տվյալների բազայում + քաղաքականությունում է աշխատում սկրիպտը (dbrp «մատակարար», «ավտոգեն»)

Եթե ​​որոշ խմբաքանակային առաջադրանք չի պարունակում dbrp-ով տող, ամբողջ ծառայությունը կհրաժարվի սկսելուց և անկեղծորեն կգրի դրա մասին մատյանում:

Chronograf-ում, ընդհակառակը, այս տողը չպետք է գոյություն ունենա, այն չի ընդունվում ինտերֆեյսի միջոցով և առաջացնում է սխալ:

Հակել կոնտեյներ կառուցելիս. Dockerfile-ը դուրս է գալիս -1-ով, եթե կան //.+dbrp տողեր, ինչը թույլ կտա անմիջապես հասկանալ անհաջողության պատճառը build-ը հավաքելիս։

միանալ մեկին շատերին

Օրինակ առաջադրանք. դուք պետք է վերցնեք ծառայության աշխատանքի ժամանակի 95-րդ տոկոսը մեկ շաբաթվա ընթացքում, համեմատեք վերջին 10 րոպեի յուրաքանչյուր րոպեն այս արժեքի հետ:

Դուք չեք կարող կատարել մեկից շատերի միացում, վերջին/միջին/միջին միավորները մի խումբ կետերի վրա հանգույցը վերածում է հոսքի, «չի կարելի ավելացնել երեխայի անհամապատասխան եզրեր. փաթեթ -> հոսք» սխալը կվերադարձվի:

Փաթեթի արդյունքը, որպես փոփոխական լամբդա արտահայտության մեջ, նույնպես չի փոխարինվում:

Հնարավորություն կա պահպանել անհրաժեշտ թվերը առաջին խմբաքանակից ֆայլ udf-ի միջոցով և բեռնել այս ֆայլը sideload-ի միջոցով:

Ի՞նչ լուծեցինք սրանով։

Մենք ունենք մոտ 100 հյուրանոցի մատակարար, նրանցից յուրաքանչյուրը կարող է ունենալ մի քանի կապ, եկեք դա անվանենք ալիք։ Այս ալիքները մոտավորապես 300-ն են, ալիքներից յուրաքանչյուրը կարող է ընկնել: Բոլոր գրանցված ցուցանիշներից մենք կվերահսկենք սխալի մակարդակը (խնդրանքները և սխալները):

Ինչու՞ ոչ գրաֆանա:

Grafana-ում կազմաձևված սխալի մասին ահազանգերն ունեն մի քանի թերություններ: Ոմանք քննադատական ​​են, ոմանց վրա կարող եք փակել ձեր աչքերը՝ կախված իրավիճակից:

Grafana-ն չգիտի ինչպես հաշվարկել չափումների + ծանուցման միջև, բայց մեզ պետք է դրույքաչափ (հարցումներ-սխալներ)/հարցումներ։

Սխալները տհաճ տեսք ունեն.

Capacitor-ում չափումների մշակման հնարքներ

Եվ ավելի քիչ չարիք, երբ դիտվում է հաջող խնդրանքներով.

Capacitor-ում չափումների մշակման հնարքներ

Լավ, մենք կարող ենք նախապես հաշվարկել ծառայության դրույքաչափը մինչև grafana, և որոշ դեպքերում դա կաշխատի: Բայց ոչ մեր մեջ, քանի որ... յուրաքանչյուր ալիքի համար իր սեփական հարաբերակցությունը համարվում է «նորմալ», և ազդանշաններն աշխատում են ստատիկ արժեքների համաձայն (մենք փնտրում ենք դրանք մեր աչքերով, փոխում ենք դրանք, եթե հաճախակի ահազանգեր են լինում):

Սրանք «նորմալ»-ի օրինակներ են տարբեր ալիքների համար.

Capacitor-ում չափումների մշակման հնարքներ

Capacitor-ում չափումների մշակման հնարքներ

Մենք անտեսում ենք նախորդ կետը և ենթադրում ենք, որ «նորմալ» պատկերը նման է բոլոր մատակարարների համար։ Հիմա ամեն ինչ կարգին է, և մենք կարո՞ղ ենք յոլա գնալ grafana-ում ահազանգերով:
Մենք կարող ենք, բայց մենք իսկապես չենք ուզում, քանի որ մենք պետք է ընտրենք տարբերակներից մեկը.
ա) յուրաքանչյուր ալիքի համար առանձին գծապատկերներ կազմեք (և ցավալիորեն ուղեկցեք դրանք)
բ) թողեք մեկ գծապատկեր բոլոր ալիքներով (և կորեք գունավոր գծերի և հարմարեցված ազդանշանների մեջ)

Capacitor-ում չափումների մշակման հնարքներ

Ինչպե՞ս դա արեցիր:

Կրկին, փաստաթղթերում լավ մեկնարկային օրինակ կա (Միացված շարքերի տոկոսադրույքների հաշվարկ), կարելի է դիտարկել կամ հիմք ընդունել նմանատիպ խնդիրների դեպքում:

Ինչ արեցինք վերջում.

  • միանալ երկու սերիաների մի քանի ժամում, խմբավորելով ըստ ալիքների;
  • լրացրեք շարքը ըստ խմբի, եթե տվյալներ չկան.
  • համեմատել վերջին 10 րոպեի միջինը նախորդ տվյալների հետ.
  • մենք գոռում ենք, եթե ինչ-որ բան գտնենք;
  • մենք գրում ենք հաշվարկված դրույքաչափերը և ահազանգերը, որոնք տեղի են ունեցել influxdb-ում.
  • ուղարկել օգտակար հաղորդագրություն Slack-ին:

Իմ կարծիքով, մենք կարողացանք հնարավորինս գեղեցիկ կերպով հասնել այն ամենին, ինչ ցանկանում էինք ստանալ վերջում (և նույնիսկ մի փոքր ավելին՝ մաքսային մշակիչներով):

Դուք կարող եք դիտել github.com-ը կոդի օրինակ и նվազագույն միացում (գրաֆվիզ) ստացված սցենարը:

Ստացված կոդի օրինակ.

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-ն կամ այլ գործիքները լիովին չեն բավարարում ձեր ցանկությունները:

Source: www.habr.com

Добавить комментарий