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

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

Kapacitor-ում չափանիշներ մշակելու հնարքներ
Kapacitor-ը TICK stack-ի մի մասն է, որը կարող է մշակել 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()

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

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

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

Ըստ լռելյայնի, join-ը կմերժի այն կետերը, որտեղ տվյալներ չկան (ներքին):
Երբ 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 ձևանմուշների օգտագործման օրինակներ հաղորդագրության մեջ

Շաբլոնները համապատասխանում են փաթեթի ձևաչափին տեքստ.ձևանմուշ, ստորև ներկայացված են հաճախ հանդիպող որոշ խնդիրներ։

եթե-ուրիշ

Եկեք կարգի բերենք ամեն ինչ և անհարկի չգրգռենք մարդկանց տեքստով։

|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)՝ ստեղծագործականություն և ոչինչ ավելին։

Մեր սովորություններից մեկը Slack-ին ծանուցումներ ուղարկելու համար նախատեսված փոքրիկ Python սկրիպտ է։
Սկզբում մենք ուզում էինք Գրաֆանայից ուղարկված լուսանկարը ուղարկել լիազորագրով պաշտպանված հաղորդագրության մեջ։ Դրանից հետո՝ նույն խմբից ստացված նախորդ ծանուցման թեմայում գրեք «Լավ», այլ ոչ թե առանձին հաղորդագրության մեջ։ Մի փոքր ուշ՝ հաղորդագրությանը ավելացրեք վերջին 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): կոնդենսատոր -url հոսթ-կամ-ip:9092 գրանցումների մակարդակ = սխալ

Ընտրանք httpOut-ով

Ցուցադրում է ընթացիկ հոսքագծի տվյալները՝

|httpOut('something')

Դիտեք (ստանալ): հոսթ-կամ-ip:9092/կոնդենսատոր/v1/առաջադրանքներ/առաջադրանքի_անուն/ինչ-որ բան

Կատարման սխեմա

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

Ուրիշ որտե՞ղ կարելի է խաբվել։

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

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

influxDBOut() ֆունկցիան կգրի ժամանակի արժեքը ահազանգից մինչև ժամանակային դրոշմանիշը, ուստի գրաֆիկի վրա կետը կգրանցի ահազանգի ժամանումից ավելի վաղ/ուշ։

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

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

Մեկնարկելիս kapacitor-ը կարող է բեռնել առաջադրանքներ, ձևանմուշներ և մշակիչներ կոնֆիգուրացիայում նշված գրացուցակից՝ [load] բլոկում։

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

  1. Ֆայլի անուն - ընդարձակվում է մինչև id/script անունը
  2. Տեսակ – հոսք/խմբաքանակ
  3. dbrp – բանալի բառ, որը նշում է, թե որ տվյալների բազայում + քաղաքականության մեջ է աշխատում սկրիպտը (dbrp "մատակարար", "autogen")

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

Քրոնոգրաֆում, ընդհակառակը, այս տողը չպետք է լինի այնտեղ. այն չի ընդունվում ինտերֆեյսի միջոցով և վերադարձնում է սխալ։

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

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

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

Դուք չեք կարող կատարել մեկից շատ միացում, կետերի խմբի վրա last/mean/median-ը հանգույցը վերածում է հոսքի, կվերադարձվի «cannot add child mismatched edges: batch -> stream» սխալը։

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

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

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

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

Ինչո՞ւ ոչ Գրաֆանա։

Grafana-ում կարգավորված սխալի մասին ծանուցումներն ունեն մի քանի թերություններ։ Որոշները կարևոր են, որոշները կարող են անտեսվել՝ կախված իրավիճակից։

Գրաֆանան չի կարող միջչափային հաշվարկներ + ահազանգում կատարել, բայց մեզ անհրաժեշտ է հաճախականություն (հարցումներ-սխալներ)/հարցումներ։

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

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

Եվ ավելի քիչ չարիք, եթե նայեք հաջողված հարցումներին.

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

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

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

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

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

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

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

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

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

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

  • մի քանի ժամվա ընթացքում միացնել երկու դրվագ՝ խմբավորելով դրանք ըստ ալիքների։
  • մենք լրացնում ենք շարքը խմբերով, եթե տվյալներ չկան։
  • համեմատեք վերջին 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) գործարկելու համար։

Մուտքի շեմը շատ բարձր չէ. փորձեք այն, եթե Գրաֆանան կամ այլ գործիքներ լիովին չեն բավարարում ձեր ցանկությունները:

Source: www.habr.com

Գնեք հուսալի հոստինգ DDoS պաշտպանությամբ կայքերի, VPS VDS սերվերների համար 🔥 Գնեք հուսալի կայքերի հոսթինգ՝ DDoS պաշտպանությամբ, VPS VDS սերվերներով | ProHoster