ڪيپيسيٽر ۾ ميٽرڪ پروسيسنگ لاءِ ترڪيبون

گهڻو ڪري، اڄ ڪو به نه پڇي ٿو ته اهو ضروري آهي ته خدمت ميٽرڪ گڏ ڪرڻ ضروري آهي. ايندڙ منطقي قدم گڏ ڪيل ميٽرڪس لاءِ الرٽ قائم ڪرڻ آهي، جيڪو توهان جي لاءِ آسان چينلن ۾ ڊيٽا ۾ ڪنهن به انحراف بابت اطلاع ڏيندو (ميل، سليڪ، ٽيليگرام). آن لائن هوٽل بکنگ سروس ۾ Ostrovok.ru اسان جي خدمتن جا سڀئي ميٽرڪس InfluxDB ۾ داخل ڪيا ويا آهن ۽ گرافانا ۾ ڏيکاريا ويا آهن، ۽ بنيادي خبرداري پڻ اتي ترتيب ڏنل آهي. ڪمن لاءِ جيئن ”توهان کي ڪنهن شيءِ کي ڳڻڻ ۽ ان سان ڀيٽ ڪرڻ جي ضرورت آهي،“ اسان استعمال ڪريون ٿا 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"))

ڊفالٽ استعمال ڪندي ()

جيڪڏهن ٽيگ/فيلڊ نه ڀريو ويو آهي، حساب ۾ غلطيون ٿينديون:

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

شموليت ڀريو (اندرون بمقابله ٻاهريون)

ڊفالٽ طور، شامل ٿيڻ پوائنٽن کي رد ڪندو جتي ڊيٽا نه هوندي (اندروني).
ڀريو ('null') سان، هڪ ٻاهرئين شامل ٿيڻ تي عمل ڪيو ويندو، جنهن کان پوء توهان کي ڪرڻ جي ضرورت آهي default() ۽ خالي قدر ڀريو:

var data = res1
    |join(res2)
        .as('res1', 'res2)
        .fill('null')
    |default()
        .field('res1.value', 0.0)
        .field('res2.value', 100.0)

هتي اڃا تائين هڪ nuance آهي. مٿي ڏنل مثال ۾، جيڪڏهن سيريز مان هڪ (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.templateهيٺ ڏنل ڪجهه عام طور تي سامهون اچڻ وارا پزل آهن.

جيڪڏهن-ٻيو

اسان شين کي ترتيب ۾ رکون ٿا ۽ ماڻهن کي هڪ ڀيرو ٻيهر متن سان ٽڪر نه ٿا ڏين:

|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) سان - تخليقيت ۽ وڌيڪ ڪجهه به نه!

اسان جي ڪسٽم مان هڪ ننڍڙي پٿون اسڪرپٽ آهي سستي ڏانهن نوٽيفڪيشن موڪلڻ لاء.
پهرين ۾، اسان هڪ پيغام ۾ هڪ اختيار-محفوظ گرافانا تصوير موڪلڻ چاهيندا هئاسين. ان کان پوءِ ٿريڊ ۾ 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 ميزبان-يا-ip:9092 لاگس lvl = غلطي

httpOut سان اختيار

موجوده پائپ لائن ۾ ڊيٽا ڏيکاري ٿو:

|httpOut('something')

ڏسو (حاصل ڪريو): ميزبان-يا-ip:9092/kapacitor/v1/tasks/task_name/something

عملدرآمد خاڪو

  • ھر ڪم ھڪڙي عمل جي وڻ کي موٽائي ٿو مفيد انگن سان فارميٽ ۾ گرافيوز.
  • هڪ بلاڪ وٺو Dot.
  • ڏسندڙ ۾ پيسٽ ڪريو، مزو.

ٻيو ڪٿي توهان هڪ ريڪ حاصل ڪري سگهو ٿا؟

رائٽ بيڪ تي influxdb ۾ ٽائم اسٽيمپ

مثال طور، اسان في ڪلاڪ (groupBy(1h)) جي درخواستن جي مجموعن لاءِ هڪ الرٽ قائم ڪيو ۽ ان انتباہ کي رڪارڊ ڪرڻ چاهيون ٿا جيڪو influxdb ۾ آيو آهي (گرافانا ۾ گراف تي مسئلي جي حقيقت کي خوبصورتي سان ڏيکارڻ لاءِ).

influxDBOut() لکندو وقت جي قيمت الرٽ کان ٽائم اسٽيمپ تائين؛ ان جي مطابق، چارٽ تي پوائنٽ اڳ ۾ لکيل هوندو / بعد ۾ الرٽ اچڻ کان.

جڏهن درستي جي ضرورت آهي: اسان هن مسئلي جي حل ڪريون ٿا هڪ ڪسٽم هينڊلر کي ڪال ڪندي، جيڪو ڊيٽا کي لکندو influxdb تي موجوده ٽائم اسٽيمپ سان.

docker، تعمير ۽ لڳائڻ

شروع ٿيڻ تي، ڪيپيڪٽر [لوڊ] بلاڪ ۾ ترتيب ۾ ڏنل ڊاريڪٽري مان ڪم، ٽيمپليٽ ۽ هينڊلر لوڊ ڪري سگھن ٿا.

صحيح طور تي هڪ ڪم ٺاهڻ لاء، توهان کي هيٺين شين جي ضرورت آهي:

  1. فائل جو نالو - اسڪرپٽ id/نالو ۾ وڌايو ويو
  2. قسم - وهڪرو / بيچ
  3. dbrp - لفظ ظاهر ڪرڻ لاءِ ته ڪهڙي ڊيٽابيس + پاليسي اسڪرپٽ ۾ هلندي آهي (dbrp “سپلائر.” “آٽوجن”)

جيڪڏهن ڪجهه بيچ ٽاسڪ dbrp سان هڪ لائن تي مشتمل نه آهي، سڄي خدمت شروع ڪرڻ کان انڪار ڪندي ۽ ايمانداري سان لاگ ان جي باري ۾ لکندو.

ڪرونوگراف ۾، ان جي برعڪس، هي لڪير موجود نه هجڻ گهرجي؛ اهو انٽرفيس ذريعي قبول نه ڪيو ويو آهي ۽ هڪ غلطي پيدا ڪري ٿي.

هيڪ جڏهن ڪنٽينر ٺاهيندي: Dockerfile -1 سان نڪرندو آهي جيڪڏهن //.+dbrp سان لائينون آهن، جيڪي توهان کي فوري طور تي ناڪامي جي سبب کي سمجهڻ جي اجازت ڏين ٿيون جڏهن تعمير کي گڏ ڪندي.

هڪ کان گهڻن ۾ شامل ٿيو

مثالي ڪم: توهان کي هڪ هفتي لاءِ خدمت جي آپريٽنگ وقت جو 95 سيڪڙو حصو وٺڻو پوندو، آخري 10 جي هر منٽ کي هن قيمت سان ڀيٽيو.

توهان نه ٿا ڪري سگهو هڪ کان گھڻا شامل ٿيڻ، آخري/مطلب/ميڊين پوائنٽن جي هڪ گروپ مٿان نوڊ کي اسٽريم ۾ تبديل ڪري ٿو، غلطي ”ٻڌن جي بي ترتيب ڪنڊن کي شامل نه ٿو ڪري سگھجي: بيچ -> وهڪرو“ واپس ڪيو ويندو.

هڪ بيچ جو نتيجو، هڪ لامبڊا اظهار ۾ هڪ متغير جي طور تي، پڻ متبادل نه آهي.

اتي هڪ اختيار آهي ضروري نمبرن کي پهرين بيچ کان فائل ۾ udf ذريعي محفوظ ڪريو ۽ هن فائل کي سائڊ لوڊ ذريعي لوڊ ڪريو.

اسان ان سان ڇا حل ڪيو؟

اسان وٽ اٽڪل 100 ھوٽل سپلائر آھن، انھن مان ھر ھڪ جا ڪيترائي ڪنيڪشن ٿي سگھن ٿا، اچو ته ان کي ھڪ چينل سڏين. انهن مان تقريبن 300 چينل آهن، هر هڪ چينل بند ٿي سگهي ٿو. سڀني رڪارڊ ڪيل ميٽرڪ مان، اسان غلطي جي شرح جي نگراني ڪنداسين (درخواستون ۽ غلطيون).

گرافانا ڇو نه؟

گرافانا ۾ ترتيب ڏنل غلطي جي خبرداري جا ڪيترائي نقصان آھن. ڪجھ نازڪ آھن، ڪجھ توھان پنھنجيون اکيون بند ڪري سگھو ٿا، صورتحال تي منحصر آھي.

گرافانا کي خبر ناهي ته ماپن + خبرداري جي وچ ۾ ڪيئن ڳڻڻ، پر اسان کي شرح جي ضرورت آهي (درخواستون-غلطيون)/درخواستون.

غلطيون خراب نظر اچن ٿيون:

ڪيپيسيٽر ۾ ميٽرڪ پروسيسنگ لاءِ ترڪيبون

۽ گهٽ برائي جڏهن ڪامياب درخواستن سان ڏٺو وڃي:

ڪيپيسيٽر ۾ ميٽرڪ پروسيسنگ لاءِ ترڪيبون

ٺيڪ آهي، اسان گرافانا کان اڳ سروس ۾ شرح جي حساب سان ڪري سگهون ٿا، ۽ ڪجهه حالتن ۾ اهو ڪم ڪندو. پر اسان ۾ نه، ڇاڪاڻ ته ... هر چينل لاءِ ان جو پنهنجو تناسب ”عام“ سمجهيو ويندو آهي، ۽ الارٽ جامد قدرن جي مطابق ڪم ڪندا آهن (اسان انهن کي پنهنجي اکين سان ڳوليندا آهيون، انهن کي تبديل ڪندا آهيون جيڪڏهن بار بار الرٽ هجن).

اهي مختلف چينلن لاءِ ”عام“ جا مثال آهن:

ڪيپيسيٽر ۾ ميٽرڪ پروسيسنگ لاءِ ترڪيبون

ڪيپيسيٽر ۾ ميٽرڪ پروسيسنگ لاءِ ترڪيبون

اسان پوئين نقطي کي نظر انداز ڪريون ٿا ۽ فرض ڪريون ٿا ته "عام" تصوير سڀني سپلائرز لاءِ ساڳي آهي. هاڻي سڀ ڪجهه ٺيڪ آهي، ۽ اسان گرافانا ۾ الرٽ سان حاصل ڪري سگهون ٿا؟
اسان ڪري سگهون ٿا، پر اسان واقعي نٿا چاهيون، ڇو ته اسان کي اختيارن مان هڪ چونڊڻو پوندو:
الف) هر چينل لاءِ ڪيترائي گراف الڳ الڳ ٺاهيو (۽ ڏکوئيندڙ انهن سان گڏ)
ب) سڀني چينلن سان ھڪڙو چارٽ ڇڏي ڏيو (۽ رنگين لائنن ۽ ڪسٽمائيز الارٽس ۾ گم ٿي وڃو)

ڪيپيسيٽر ۾ ميٽرڪ پروسيسنگ لاءِ ترڪيبون

توهان اهو ڪيئن ڪيو؟

ٻيهر، دستاويز ۾ هڪ سٺو شروعاتي مثال آهي (شامل ڪيل سيريز ۾ قيمتن جي حساب سان) تي غور ڪري سگھجي ٿو يا ساڳئي مسئلن ۾ بنياد طور ورتو وڃي.

آخر ۾ اسان ڇا ڪيو:

  • ڪجھ ڪلاڪن ۾ ٻن سيريز ۾ شامل ٿيو، چينل طرفان گروپن؛
  • گروپ طرفان سيريز ڀريو جيڪڏهن ڪو ڊيٽا نه هو؛
  • پوئين ڊيٽا سان آخري 10 منٽن جي وچين جو مقابلو ڪريو؛
  • جيڪڏهن اسان کي ڪا شيءِ ملي ته رڙ ڪريون؛
  • اسان انفلوڪس ڊي بي ۾ ڪيل ڳڻپيوڪر شرح ۽ الرٽ لکون ٿا؛
  • 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) کي هلائڻ ۾ وڏو آھي.

داخلا جي رڪاوٽ تمام گهڻي نه آهي - ان جي ڪوشش ڪريو جيڪڏهن گرافانا يا ٻيا اوزار مڪمل طور تي توهان جي خواهش کي پورو نه ڪن.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو