సర్వీస్ మెట్రిక్స్ను సేకరించడం ఎందుకు అవసరమో అని బహుశా ఇప్పుడు ఎవరూ ఆలోచించడం లేదు. తదుపరి తార్కిక చర్య ఏమిటంటే, సేకరించిన మెట్రిక్స్ కోసం అలర్ట్లను సెటప్ చేయడం. ఇవి మీకు నచ్చిన ఛానెళ్ల (ఈమెయిల్, స్లాక్, టెలిగ్రామ్) ద్వారా డేటాలో ఏవైనా వ్యత్యాసాలు ఉంటే మీకు తెలియజేస్తాయి. ఒక ఆన్లైన్ హోటల్ బుకింగ్ సర్వీస్లో మా సేవల నుండి వచ్చే అన్ని మెట్రిక్లు InfluxDB లోకి స్ట్రీమ్ చేయబడి Grafana లో ప్రదర్శించబడతాయి, అక్కడ ప్రాథమిక హెచ్చరికలు కూడా కాన్ఫిగర్ చేయబడ్డాయి. "దేనినైనా లెక్కించి, దానితో పోల్చాలి" వంటి పనుల కోసం, మేము Kapacitor ను ఉపయోగిస్తాము.

Kapacitor అనేది TICK స్టాక్లో ఒక భాగం, ఇది InfluxDB నుండి మెట్రిక్లను ప్రాసెస్ చేయగలదు. ఇది బహుళ డైమెన్షన్లను జాయిన్ చేయగలదు, ఫలిత డేటా నుండి ఉపయోగకరమైన డేటాను లెక్కించగలదు, ఫలితాన్ని తిరిగి InfluxDBకి రాయగలదు మరియు Slack, Telegram, లేదా ఇమెయిల్కు అలర్ట్లను పంపగలదు.
మొత్తం స్టాక్ చల్లగా మరియు వివరంగా ఉంది అయితే, మాన్యువల్స్లో స్పష్టంగా ప్రస్తావించని ఉపయోగకరమైన విషయాలు ఎల్లప్పుడూ ఉంటాయి. ఈ వ్యాసంలో, నేను అలాంటి ఉపయోగకరమైన, అంతగా సుస్పష్టం కాని అనేక చిట్కాలను సేకరించాలని నిర్ణయించుకున్నాను (ప్రాథమిక TICKscipt సింటాక్స్ వివరించబడింది). ) మరియు మన సమస్యలలో ఒకదానిని పరిష్కరించే ఉదాహరణను ఉపయోగించి వాటిని ఎలా అనువర్తించవచ్చో చూపండి.
లెట్ యొక్క వెళ్ళి!
ఫ్లోట్ & ఇంట్, గణన లోపాలు
కాస్టింగ్ ద్వారా పరిష్కరించబడిన పూర్తిగా ప్రామాణికమైన సమస్య:
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)
జాయిన్ను పూరించండి (లోపలి vs. బయటి)
డిఫాల్ట్గా, జాయిన్ డేటా లేని పాయింట్లను (లోపలి) విస్మరిస్తుంది.
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) ఒకటి ఖాళీగా ఉంటే, ఫలిత సిరీస్ (డేటా) కూడా ఖాళీగా ఉంటుంది. ఈ అంశంపై అనేక గిట్హబ్ ఇష్యూలు ఉన్నాయి (, , ) – మేము పరిష్కారాల కోసం ఎదురుచూస్తూ కొద్దిగా ఇబ్బంది పడుతున్నాము.
గణనలలో షరతులను ఉపయోగించడం (లాంబ్డాలో ఉంటే)
|eval(lambda: if("value" > 0, true, false)
ఈ కాలానికి పైప్లైన్ నుండి చివరి ఐదు నిమిషాలు
ఉదాహరణకు, మీరు గత ఐదు నిమిషాల విలువలను గత వారంతో పోల్చాలి. మీరు రెండు వేర్వేరు బ్యాచ్లలో రెండు డేటా సెట్లను తీసుకోవచ్చు లేదా పెద్ద కాల వ్యవధి నుండి డేటాలోని కొంత భాగాన్ని సంగ్రహించవచ్చు:
|where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)
చివరి ఐదు నిమిషాల కోసం ఒక ప్రత్యామ్నాయం ఏమిటంటే, BarrierNode నోడ్ను ఉపయోగించడం, ఇది నిర్దిష్ట సమయానికి ముందే డేటాను నిలిపివేస్తుంది:
|barrier()
.period(5m)
మెసేజ్లో గో టెంప్లేట్లను ఉపయోగించడానికి ఉదాహరణలు
టెంప్లేట్లు ప్యాకేజీలోని ఫార్మాట్కు అనుగుణంగా ఉంటాయి కింద తరచుగా ఎదురయ్యే కొన్ని సమస్యలు ఇవ్వబడ్డాయి.
ఉంటే-లేకపోతే
విషయాలను చక్కబెట్టుకుందాం మరియు అనవసరమైన రాతలతో ఇతరులను రెచ్చగొట్టవద్దు:
|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) మీ స్క్రిప్ట్ను అమలు చేయడానికి మిమ్మల్ని అనుమతిస్తుంది – స్వచ్ఛమైన సృజనాత్మకత!
మా కస్టమ్ స్క్రిప్ట్లలో ఒకటి, స్లాక్కు నోటిఫికేషన్లను పంపే ఒక చిన్న పైథాన్ స్క్రిప్ట్.
మొదట, మేము గ్రాఫానా నుండి ఒక చిత్రాన్ని, అధికారికతతో రక్షితమైన సందేశంలో పంపాలనుకున్నాము. ఆ తర్వాత, ఒక ప్రత్యేక సందేశంలో కాకుండా, అదే గ్రూప్ నుండి వచ్చిన మునుపటి హెచ్చరిక యొక్క థ్రెడ్లోనే "సరే" అని వ్రాయాలనుకున్నాము. కొద్దిసేపటి తర్వాత, గత 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 :9092 లాగ్లు lvl=లోపం
httpOut తో ఎంపిక
ప్రస్తుత పైప్లైన్లోని డేటాను చూపుతుంది:
|httpOut('something')
చూడండి (పొందండి): :9092/kapacitor/v1/tasks/task_name/something
అమలు పథకం
- ప్రతి టాస్క్ ఈ ఫార్మాట్లో ఉపయోగకరమైన సంఖ్యలతో కూడిన ఎగ్జిక్యూషన్ ట్రీని అందిస్తుంది .
- మేము ఒక బ్లాక్ తీసుకుంటాము .
- వ్యూయర్లో అతికించండి, .
ఇంకెక్కడ ఇంతలా దెబ్బలు తినగలరు?
రైట్బ్యాక్ సమయంలో ఇన్ఫ్లక్స్డిబిలో టైమ్స్టాంప్
ఉదాహరణకు, మేము గంటకు అభ్యర్థనల మొత్తం కోసం (groupBy(1h)) ఒక హెచ్చరికను ఏర్పాటు చేసాము మరియు గ్రాఫానాలోని గ్రాఫ్లో సమస్య యొక్క వాస్తవాన్ని చక్కగా చూపించడానికి ఆ హెచ్చరికను ఇన్ఫ్లక్స్డిబిలో రికార్డ్ చేయాలనుకుంటున్నాము.
influxDBOut() అలర్ట్ నుండి సమయ విలువను టైమ్స్టాంప్కు రాస్తుంది, కాబట్టి చార్ట్లోని పాయింట్ అలర్ట్ వచ్చిన దానికంటే ముందుగా/తరువాత రాయబడుతుంది.
ఖచ్చితత్వం అవసరమైనప్పుడు, ప్రస్తుత టైమ్స్టాంప్తో ఇన్ఫ్లక్స్డిబికి డేటాను వ్రాసే ఒక కస్టమ్ హ్యాండ్లర్ను పిలవడం ద్వారా మేము ఈ సమస్యను అధిగమిస్తాము.
డాకర్, బిల్డ్ మరియు డిప్లాయ్
ప్రారంభించేటప్పుడు, kapacitor కాన్ఫిగ్లోని [load] బ్లాక్లో పేర్కొన్న డైరెక్టరీ నుండి టాస్క్లు, టెంప్లేట్లు మరియు హ్యాండ్లర్లను లోడ్ చేయగలదు.
ఒక పనిని సరిగ్గా సృష్టించడానికి, మీకు ఈ క్రింది విషయాలు అవసరం:
- ఫైల్ పేరు - ఐడి/స్క్రిప్ట్ పేరుగా విస్తరిస్తుంది
- రకం – స్ట్రీమ్/బ్యాచ్
- dbrp – స్క్రిప్ట్ ఏ డేటాబేస్ + పాలసీలో రన్ అవుతుందో పేర్కొనడానికి ఉపయోగించే కీవర్డ్ (dbrp "supplier"."autogen")
ఏదైనా బ్యాచ్ టాస్క్లో dbrp అనే లైన్ లేకపోతే, మొత్తం సర్వీస్ ప్రారంభం కావడానికి నిరాకరిస్తుంది మరియు ఆ విషయాన్ని లాగ్లో స్పష్టంగా నమోదు చేస్తుంది.
దీనికి విరుద్ధంగా, క్రోనోగ్రాఫ్లో ఈ లైన్ ఉండకూడదు; ఇది ఇంటర్ఫేస్ ద్వారా ఆమోదించబడదు మరియు ఒక ఎర్రర్ను తిరిగి ఇస్తుంది.
కంటైనర్ బిల్డ్ హ్యాక్: Dockerfileలో //.+dbrp ఉన్న లైన్లు ఉంటే, అది -1తో నిష్క్రమిస్తుంది, దీనివల్ల మీరు బిల్డ్ వైఫల్యానికి గల కారణాన్ని వెంటనే అర్థం చేసుకోవచ్చు.
ఒకటి నుండి అనేక వాటికి చేరండి
ఉదాహరణ పని: ఒక వారానికి సర్వీస్ అప్టైమ్ యొక్క 95వ పర్సంటైల్ను తీసుకొని, చివరి 10 నిమిషాలలోని ప్రతి నిమిషాన్ని ఈ విలువతో పోల్చండి.
మీరు వన్-టు-మెనీ జాయిన్ చేయలేరు; పాయింట్ల సమూహంపై లాస్ట్/మీన్/మీడియన్ నోడ్ను స్ట్రీమ్గా మారుస్తుంది, మరియు "cannot add child mismatched edges: batch -> stream" అనే ఎర్రర్ వస్తుంది.
లాంబ్డా ఎక్స్ప్రెషన్లో ఒక వేరియబుల్గా ఉన్న బ్యాచ్ ఫలితం కూడా ప్రతిక్షేపించబడదు.
మొదటి బ్యాచ్ నుండి అవసరమైన సంఖ్యలను udf ద్వారా ఒక ఫైల్లో సేవ్ చేసి, ఈ ఫైల్ను సైడ్లోడ్ ద్వారా లోడ్ చేసుకునే అవకాశం ఉంది.
దీనితో మనం ఏమి పరిష్కరిస్తున్నాము?
మాకు సుమారు 100 హోటల్ ప్రొవైడర్లు ఉన్నారు, వీరిలో ప్రతి ఒక్కరికీ బహుళ కనెక్షన్లు ఉండవచ్చు, వీటిని మనం ఛానెల్లు అని పిలుద్దాం. ఇటువంటి ఛానెల్లు దాదాపు 300 ఉన్నాయి, మరియు వాటిలో ఏ ఒక్కటైనా విఫలం కావచ్చు. నమోదు చేయబడిన అన్ని మెట్రిక్లలో, మేము ఎర్రర్ రేట్ను (అభ్యర్థనలు మరియు లోపాలు) పర్యవేక్షిస్తాము.
గ్రాఫానా ఎందుకు కాదు?
గ్రాఫానాలో కాన్ఫిగర్ చేయబడిన ఎర్రర్ అలర్ట్లకు అనేక లోపాలు ఉన్నాయి. కొన్ని కీలకమైనవి కాగా, పరిస్థితిని బట్టి మరికొన్నింటిని విస్మరించవచ్చు.
గ్రాఫానా ఇంటర్-డైమెన్షనల్ గణనలు మరియు హెచ్చరికలు చేయలేదు, కానీ మాకు రేటు (అభ్యర్థనలు-లోపాలు)/అభ్యర్థనలు అవసరం.
తప్పులు చాలా ఘోరంగా కనిపిస్తున్నాయి:

మరియు విజయవంతమైన అభ్యర్థనల దృక్కోణంతో చూస్తే, దీని చెడు అంత తక్కువ కాదు:

సరే, మనం గ్రాఫానాకు ముందు సర్వీస్లో రేటును ముందుగానే లెక్కించవచ్చు, మరియు కొన్ని సందర్భాల్లో అది ఫర్వాలేదు. కానీ మా విషయంలో అలా కాదు, ఎందుకంటే ప్రతి ఛానెల్కు దాని స్వంత "సాధారణ" నిష్పత్తి ఉంటుంది, మరియు హెచ్చరికలు స్థిరమైన విలువలపై ఆధారపడి ఉంటాయి (మేము వాటిని గమనిస్తూ ఉంటాము, అవి తరచుగా హెచ్చరిస్తుంటే వాటిని సర్దుబాటు చేస్తాము).
వివిధ ఛానెల్లకు "సాధారణమైన" వాటికి ఇవి ఉదాహరణలు:


మునుపటి విషయాన్ని విస్మరించి, సరఫరాదారులందరికీ ఒకే విధమైన "సాధారణ" పరిస్థితి ఉందని అనుకుందాం. అలాగైతే, ఇప్పుడు అంతా బాగానే ఉంది, మరియు మనం గ్రాఫానాలోని హెచ్చరికలతో సరిపెట్టుకోవచ్చా?
మేము చేయగలం, కానీ మాకు నిజంగా ఇష్టం లేదు, ఎందుకంటే మేము ఉన్న ఎంపికలలో ఒకదాన్ని ఎంచుకోవాలి:
ఎ) ప్రతి ఛానెల్కు విడివిడిగా చాలా గ్రాఫ్లను తయారు చేయడం (మరియు వాటిని కష్టపడి నిర్వహించడం)
బి) అన్ని ఛానెల్లతో ఒకే చార్ట్ను వదిలివేయండి (మరియు రంగురంగుల గీతలు మరియు అనుకూలీకరించిన హెచ్చరికలలో మునిగిపోండి)

మీరు దాన్ని ఎలా చేసారు?
మళ్ళీ, డాక్యుమెంటేషన్లో ఒక మంచి ప్రారంభ ఉదాహరణ ఉంది (), మీరు దానిని పరిశీలించవచ్చు లేదా ఇలాంటి సమస్యలలో దానిని ఆధారంగా ఉపయోగించుకోవచ్చు.
చివరికి మేము చేసింది:
- ఛానెల్ల వారీగా సమూహపరచి, కొన్ని గంటల్లో రెండు ఎపిసోడ్లను కలపండి;
- డేటా లేకపోతే మనం శ్రేణిని సమూహాల వారీగా పూరిస్తాము;
- మేము చివరి 10 నిమిషాల మధ్యస్థాన్ని మునుపటి డేటాతో పోలుస్తాము;
- ఏదైనా దొరికితే కేక వేద్దాం;
- మేము లెక్కించిన రేట్లు మరియు సంభవించిన హెచ్చరికలను ఇన్ఫ్లక్స్డిబికి వ్రాస్తాము;
- స్లాక్కు ఉపయోగకరమైన సందేశాన్ని పంపండి.
నా అభిప్రాయం ప్రకారం, చివరికి మనం కోరుకున్నవన్నీ సాధ్యమైనంత అందంగా సాధించగలిగాము (కస్టమ్ హ్యాండ్లర్ల సహాయంతో ఇంకాస్త ఎక్కువగా కూడా).
మీరు 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) అమలు చేయడంలో చాలా గొప్పది.
దీనిని ప్రారంభించడం అంత కష్టం కాదు - గ్రాఫానా లేదా ఇతర సాధనాలు మీ అవసరాలను పూర్తిగా తీర్చకపోతే, దీనిని ప్రయత్నించండి.
మూలం: www.habr.com
