Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх

Өнөөдөр үйлчилгээний хэмжигдэхүүнийг цуглуулах шаардлагатай гэж хэн ч асуудаггүй байх. Дараагийн логик алхам бол цуглуулсан хэмжигдэхүүнүүдэд анхааруулга тохируулах явдал бөгөөд энэ нь танд тохиромжтой сувгууд (мэйл, Slack, Telegram) дахь өгөгдөл дэх аливаа хазайлтын талаар мэдэгдэх болно. Онлайн зочид буудлын захиалгын үйлчилгээнд Ostrovok.ru Манай үйлчилгээний бүх хэмжигдэхүүнийг InfluxDB-д асгаж, Grafana-д харуулсан бөгөөд үндсэн анхааруулгыг тэнд тохируулсан болно. "Та ямар нэг зүйлийг тооцоолж, түүнтэй харьцуулах хэрэгтэй" гэх мэт ажлуудын хувьд бид Kapacitor ашигладаг.

Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх
Kapacitor нь InfluxDB-ийн хэмжүүрүүдийг боловсруулах боломжтой TICK стекийн нэг хэсэг юм. Энэ нь хэд хэдэн хэмжилтийг хооронд нь холбож (нэгдүүлэх), хүлээн авсан өгөгдлөөс хэрэгтэй зүйлийг тооцоолох, үр дүнг InfluxDB руу буцааж бичих, Slack/Telegram/мэйл рүү сэрэмжлүүлэг илгээх боломжтой.

Стек бүхэлдээ сэрүүн бөгөөд нарийвчилсан байна баримт бичиг, гэхдээ гарын авлагад тодорхой заагаагүй ашигтай зүйлүүд үргэлж байх болно. Энэ нийтлэлд би хэд хэдэн хэрэгтэй, тодорхой бус зөвлөгөөг цуглуулахаар шийдсэн (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 загваруудыг ашиглах жишээ

Загварууд нь багцын форматтай тохирч байна текст.загварДоорх нь байнга тааралддаг оньсого юм.

хэрэв үгүй ​​бол

Бид бүх зүйлийг цэгцэлж, хүмүүсийг дахин нэг удаа текстээр өдөөхгүй:

|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 скрипт юм.
Эхлээд бид зөвшөөрөлтэйгээр хамгаалагдсан графана зургийг мессежээр илгээхийг хүссэн. Дараа нь ижил бүлгийн өмнөх сэрэмжлүүлгийн утсанд 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/капацитор/v1/даалгавар/даалгаврын_нэр/ямар нэгэн зүйл

Гүйцэтгэлийн схем

  • Даалгавар бүр нь форматаар хэрэгтэй тоонуудтай гүйцэтгэх модыг буцаана графвиз.
  • Блок ав цэг.
  • Үүнийг үзэгч рүү буулгана уу, таашаал авах.

Та тармуурыг өөр хаанаас авч болох вэ?

буцаан бичихэд influxdb дахь цагийн тэмдэг

Жишээлбэл, бид нэг цагийн хүсэлтийн нийлбэрийн дохиог (groupBy(1h)) тохируулж, influxdb-д тохиолдсон сэрэмжлүүлгийг бүртгэхийг хүсч байна (графана дахь график дээрх асуудлын баримтыг сайхан харуулахын тулд).

influxDBOut() нь сэрэмжлүүлгээс цагийн тэмдэг хүртэлх цагийн утгыг бичих бөгөөд үүний дагуу диаграм дээрх цэг нь сэрэмжлүүлэг ирсэнээс өмнө/хожуу бичигдэнэ.

Нарийвчлал шаардлагатай үед: бид одоогийн цагийн тэмдэг бүхий influxdb руу өгөгдөл бичих тусгай зохицуулагчийг дуудаж энэ асуудлыг шийдэж байна.

докер, бүтээх, байршуулах

Ачаалах үед конденсатор нь [ачаалах] блок дахь тохиргоонд заасан лавлахаас даалгавар, загвар болон зохицуулагчийг ачаалж болно.

Даалгаврыг зөв үүсгэхийн тулд танд дараахь зүйлс хэрэгтэй болно.

  1. Файлын нэр – скриптийн ID/нэр болгон өргөжүүлсэн
  2. Төрөл - урсгал/багц
  3. dbrp – скрипт ямар өгөгдлийн сан + бодлого дээр ажиллаж байгааг харуулах түлхүүр үг (dbrp “нийлүүлэгч.” “автоген”)

Хэрэв зарим багц ажил нь dbrp-тэй мөрийг агуулаагүй бол бүхэл бүтэн үйлчилгээ эхлүүлэхээс татгалзаж, энэ тухай бүртгэлд чин сэтгэлээсээ бичих болно.

Хронограф дээр эсрэгээр энэ мөр байх ёсгүй, энэ нь интерфейсээр дамждаггүй бөгөөд алдаа үүсгэдэг.

Контейнер барих үед хакердах: //.+dbrp-тэй мөрүүд байвал Dockerfile -1-ээр гарах бөгөөд энэ нь угсралтыг угсрах үед бүтэлгүйтлийн шалтгааныг шууд ойлгох боломжийг олгоно.

олонд нэгдээрэй

Жишээ даалгавар: Та долоо хоногийн турш үйлчилгээний 95 дахь хувийг авч, сүүлийн 10 минутын минут бүрийг энэ утгатай харьцуулах хэрэгтэй.

Та нэгээс олон нэгдэл хийх боломжгүй, бүлэг цэгийн сүүлийн/дундаж/медиан нь зангилааг урсгал болгон хувиргавал "хүүхдийн таарахгүй ирмэгийг нэмж чадахгүй: багц -> урсгал" гэсэн алдаа гарна.

Ламбда илэрхийлэл дэх хувьсагчийн хувьд багцын үр дүн нь бас орлуулахгүй.

Эхний багцаас шаардлагатай тоонуудыг udf-ээр файл руу хадгалж, энэ файлыг sideload-аар ачаалах сонголт бий.

Үүнийг бид юу шийдсэн бэ?

Манайд 100 орчим зочид буудлын ханган нийлүүлэгч байдаг, тус бүр нь хэд хэдэн холболттой байж болно, үүнийг суваг гэж нэрлэе. Эдгээр суваг нь ойролцоогоор 300 байдаг бөгөөд суваг тус бүр нь унаж болно. Бүртгэгдсэн бүх хэмжигдэхүүнүүдээс бид алдааны түвшинг (хүсэлт, алдаа) хянах болно.

Яагаад графана болохгүй гэж?

Grafana-д тохируулсан алдааны дохиолол нь хэд хэдэн сул талуудтай. Зарим нь шүүмжлэлтэй ханддаг бол зарим нь нөхцөл байдлаас шалтгаалан нүдээ аниж болно.

Графана хэмжилт + дохиоллын хооронд хэрхэн тооцоолохыг мэдэхгүй ч бидэнд ханш (хүсэлт-алдаа)/хүсэлт хэрэгтэй.

Алдаанууд муухай харагдаж байна:

Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх

Амжилттай хүсэлтүүдээр харахад муу муухай нь бага байдаг:

Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх

За, бид графанаас өмнө үйлчилгээний тарифыг урьдчилан тооцоолж болох бөгөөд зарим тохиолдолд энэ нь ажиллах болно. Гэхдээ манайд биш, учир нь... Суваг бүрийн хувьд өөрийн харьцааг "хэвийн" гэж тооцдог бөгөөд дохиолол нь статик утгын дагуу ажилладаг (бид нүдээрээ хайж, дохиолол байнга гарч байвал тэдгээрийг өөрчилдөг).

Эдгээр нь янз бүрийн сувгийн "хэвийн" жишээ юм:

Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх

Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх

Бид өмнөх зүйлийг үл тоомсорлож, "хэвийн" дүр зургийг бүх ханган нийлүүлэгчдийн хувьд ижил төстэй гэж үздэг. Одоо бүх зүйл сайхан болсон, бид графана дахь сэрэмжлүүлгийг даван туулж чадах уу?
Бид чадна, гэхдээ бид үнэхээр хүсэхгүй байна, учир нь бид сонголтуудын аль нэгийг сонгох хэрэгтэй:
a) суваг тус бүрийг тусад нь олон график хийх (мөн тэдгээрийг дагалдуулна)
б) бүх сувгаар нэг диаграм үлдээх (мөн өнгөлөг шугамууд болон тохируулсан сэрэмжлүүлэгт төөрөх)

Kapacitor дахь хэмжигдэхүүнийг боловсруулах заль мэх

Та яаж үүнийг хийсэн бэ?

Дахин хэлэхэд, баримт бичигт сайн эхлэлийн жишээ байна (Холбогдсон цувралуудын хувь хэмжээг тооцоолж байна), үүнтэй төстэй асуудлуудыг судалж үзэх эсвэл үндэс болгон авч болно.

Эцэст нь бидний хийсэн зүйл:

  • хэдэн цагийн дотор хоёр цувралд нэгдэх, сувгаар нь бүлэглэх;
  • өгөгдөл байхгүй бол цувралыг бүлгээр бөглөх;
  • сүүлийн 10 минутын медианыг өмнөх өгөгдөлтэй харьцуулах;
  • хэрэв бид ямар нэг зүйл олвол хашгирах;
  • бид influxdb-д тохиолдсон тооцоолсон хувь хэмжээ, сэрэмжлүүлгийг бичих;
  • сулрахад хэрэгтэй мессеж илгээнэ үү.

Миний бодлоор бид эцэст нь авахыг хүссэн бүх зүйлдээ (мөн захиалгат зохицуулагчийн тусламжтайгаар) аль болох сайхан хүрч чадсан.

Та 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

сэтгэгдэл нэмэх