Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම

බොහෝ දුරට ඉඩ ඇති පරිදි, සේවා ප්‍රමිතික එකතු කිරීම අවශ්‍ය වන්නේ මන්දැයි අද කිසිවෙකු අසන්නේ නැත. මීළඟ තාර්කික පියවර වනුයේ ඔබට පහසු නාලිකාවල (තැපැල්, ස්ලැක්, ටෙලිග්‍රාම්) දත්තවල කිසියම් අපගමනය ගැන දැනුම් දෙන එකතු කරන ලද ප්‍රමිතික සඳහා ඇඟවීමක් සැකසීමයි. මාර්ගගත හෝටල් වෙන්කරවා ගැනීමේ සේවාව තුළ Ostrovok.ru අපගේ සේවාවන්හි සියලුම ප්‍රමිතික InfluxDB වෙත වත් කර Grafana හි ප්‍රදර්ශනය කෙරෙන අතර මූලික ඇඟවීම් ද එහි වින්‍යාස කර ඇත. "ඔබට යමක් ගණනය කර එය සමඟ සැසඳිය යුතුය" වැනි කාර්යයන් සඳහා අපි Kapacitor භාවිතා කරමු.

Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම
Kapacitor යනු InfluxDB වෙතින් ප්‍රමිතික සැකසිය හැකි TICK තොගයේ කොටසකි. එයට මිනුම් කිහිපයක් එකට සම්බන්ධ කළ හැකිය (එකතු වීම), ලැබුණු දත්ත වලින් ප්‍රයෝජනවත් දෙයක් ගණනය කිරීම, ප්‍රතිඵලය නැවත 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') පිටත එකතු කිරීමක් සිදු කරනු ඇත, ඉන්පසු ඔබට පෙරනිමිය() කර හිස් අගයන් පුරවන්න:

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) සමඟ ඔබේ ස්ක්‍රිප්ට් ක්‍රියාත්මක කිරීමට ඔබට ඉඩ සලසයි - නිර්මාණශීලීත්වය සහ තවත් කිසිවක් නැත!

අපගේ චාරිත්‍රවලින් එකක් වන්නේ ස්ලැක් වෙත දැනුම්දීම් යැවීම සඳහා කුඩා පයිතන් ස්ක්‍රිප්ට් එකකි.
මුලදී, අපට අවශ්‍ය වූයේ අවසර-ආරක්ෂිත ග්‍රැෆානා පින්තූරයක් පණිවිඩයක් යැවීමට ය. ඊට පස්සේ, ත්‍රෙඩ් එකේ 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/යමක්

ක්රියාත්මක කිරීමේ රූප සටහන

තව කොහෙන්ද පෝරුවක් ගන්න පුළුවන්?

ආපසු ලිවීමේදී influxdb හි කාල මුද්‍රාව

උදාහරණයක් ලෙස, අපි පැයකට ඉල්ලීම් එකතුව (groupBy(1h)) සඳහා ඇඟවීමක් සකසන අතර influxdb හි සිදු වූ අනතුරු ඇඟවීම පටිගත කිරීමට අවශ්‍යයි (grafana හි ප්‍රස්ථාරයේ ගැටලුවේ සත්‍යය අලංකාර ලෙස පෙන්වීමට).

influxDBOut() විසින් අනතුරු ඇඟවීමේ සිට කාල මුද්‍රාව දක්වා කාල අගය ලියයි; ඒ අනුව, ප්‍රස්ථාරයේ ලක්ෂ්‍යය අනතුරු ඇඟවීමට වඩා කලින්/පසු ලියා ඇත.

නිරවද්‍යතාව අවශ්‍ය වූ විට: අපි අභිරුචි හසුරුවන්නෙකු ඇමතීමෙන් මෙම ගැටලුව විසඳා ගනිමු, එය වත්මන් කාල මුද්‍රාව සමඟ influxdb වෙත දත්ත ලියනු ඇත.

ඩොකර්, ගොඩනැගීම සහ යෙදවීම

ආරම්භයේදී, kapacitor හට [load] block හි වින්‍යාසයෙහි දක්වා ඇති නාමාවලියෙන් කාර්යයන්, සැකිලි සහ හසුරුවන්නන් පූරණය කළ හැක.

කාර්යයක් නිවැරදිව නිර්මාණය කිරීම සඳහා, ඔබට පහත සඳහන් දෑ අවශ්‍ය වේ:

  1. ගොනු නාමය - ස්ක්‍රිප්ට් හැඳුනුම්පත/නම බවට පුළුල් කරන ලදී
  2. වර්ගය - ධාරාව / කණ්ඩායම
  3. dbrp - ස්ක්‍රිප්ට් ක්‍රියාත්මක වන්නේ කුමන දත්ත සමුදාය + ප්‍රතිපත්තියෙහිද යන්න දැක්වීමට මූල පදය (dbrp "සැපයුම්කරු" "autogen")

සමහර කණ්ඩායම් කාර්යය dbrp සමඟ රේඛාවක් අඩංගු නොවේ නම්, සම්පූර්ණ සේවාව ආරම්භ කිරීම ප්රතික්ෂේප කරනු ඇති අතර අවංකව ලොගයේ ඒ ගැන ලියන්න.

chronograf හි, ඊට පටහැනිව, මෙම රේඛාව නොතිබිය යුතුය; එය අතුරු මුහුණත හරහා පිළි නොගන්නා අතර දෝෂයක් ජනනය කරයි.

කන්ටේනරයක් තැනීමේදී හැක් කරන්න: //.+dbrp සමඟ රේඛා තිබේ නම් -1 සමඟින් Dockerfile පිටවෙයි, එය ගොඩනැගීම එකලස් කිරීමේදී අසාර්ථක වීමට හේතුව වහාම තේරුම් ගැනීමට ඔබට ඉඩ සලසයි.

එකට එකතු වෙන්න

උදාහරණ කාර්යය: ඔබ සතියක් සඳහා සේවාවේ මෙහෙයුම් කාලයෙන් 95 වැනි ප්‍රතිශතයක් ගත යුතුය, අවසාන 10 හි සෑම මිනිත්තුවක්ම මෙම අගය සමඟ සසඳන්න.

ඔබට එකට-බොහෝ එකතු කිරීමක් කළ නොහැක, ලක්ෂ්‍ය සමූහයක් හරහා අවසාන/මධ්‍ය/මධ්‍ය නෝඩය ප්‍රවාහයක් බවට පත් කරයි, “ළමා නොගැලපෙන දාර එකතු කළ නොහැක: කණ්ඩායම -> ප්‍රවාහය” දෝෂය ආපසු ලබා දෙනු ඇත.

ලැම්ඩා ප්‍රකාශනයක විචල්‍යයක් ලෙස කණ්ඩායමක ප්‍රතිඵලය ද ආදේශ නොකෙරේ.

අවශ්‍ය අංක පළමු කාණ්ඩයේ සිට ගොනුවකට udf හරහා සුරැකීමට සහ මෙම ගොනුව සයිඩ්ලෝඩ් හරහා පූරණය කිරීමට විකල්පයක් ඇත.

අපි මේකෙන් විසඳුවේ මොකක්ද?

අපට හෝටල් සැපයුම්කරුවන් 100 ක් පමණ ඇත, ඔවුන් සෑම කෙනෙකුටම සම්බන්ධතා කිහිපයක් තිබිය හැකිය, අපි එය නාලිකාවක් ලෙස කියමු. මෙම නාලිකා වලින් ආසන්න වශයෙන් 300 ක් ඇත, එක් එක් නාලිකා කඩා වැටිය හැක. සියලුම වාර්තාගත මිනුම් වලින්, අපි දෝෂ අනුපාතය (ඉල්ලීම් සහ දෝෂ) නිරීක්ෂණය කරන්නෙමු.

ග්‍රැෆානා නොවන්නේ ඇයි?

Grafana හි වින්‍යාස කර ඇති දෝෂ ඇඟවීම් වල අවාසි කිහිපයක් ඇත. සමහර ඒවා තීරනාත්මක ය, සමහරක් ඔබට ඔබේ ඇස් වසා ගත හැකිය, තත්වය අනුව.

මිනුම් + අනතුරු ඇඟවීම් අතර ගණනය කරන්නේ කෙසේදැයි Grafana දන්නේ නැත, නමුත් අපට අනුපාතයක් (ඉල්ලීම්-දෝෂ)/ඉල්ලීම් අවශ්‍ය වේ.

දෝෂ නරක ලෙස පෙනේ:

Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම

සාර්ථක ඉල්ලීම් සමඟ බලන විට අඩු නරක:

Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම

හරි, අපට ග්‍රැෆනාට පෙර සේවාවේ ගාස්තුව කලින් ගණනය කළ හැකි අතර සමහර අවස්ථාවල මෙය ක්‍රියා කරයි. ඒත් අපේ එකේ නෙවෙයි මොකද... සෑම නාලිකාවක් සඳහාම තමන්ගේම අනුපාතය “සාමාන්‍ය” ලෙස සලකනු ලබන අතර, ඇඟවීම් ස්ථිතික අගයන් අනුව ක්‍රියා කරයි (අපි ඒවා අපගේ ඇස්වලින් සොයමු, නිතර ඇඟවීම් තිබේ නම් ඒවා වෙනස් කරන්න).

මේවා විවිධ නාලිකා සඳහා "සාමාන්‍ය" සඳහා උදාහරණ වේ:

Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම

Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම

අපි පෙර කරුණ නොසලකා හරින අතර සියලු සැපයුම්කරුවන් සඳහා "සාමාන්ය" පින්තූරය සමාන බව උපකල්පනය කරමු. දැන් සියල්ල හොඳින්, සහ අපට ග්‍රැෆානා හි ඇඟවීම් සමඟින් යා හැකිද?
අපට හැකිය, නමුත් අපට ඇත්තටම අවශ්‍ය නැත, මන්ද අපට විකල්ප වලින් එකක් තෝරා ගත යුතුය:
අ) එක් එක් නාලිකාව සඳහා වෙන වෙනම ප්‍රස්ථාර ගොඩක් සාදන්න (සහ වේදනාකාරී ලෙස ඒවා සමඟ යන්න)
ආ) සියලුම නාලිකා සමඟ එක් ප්‍රස්ථාරයක් තබන්න (සහ වර්ණවත් රේඛා සහ අභිරුචිකරණය කළ ඇඟවීම් තුළ අතරමං වන්න)

Kapacitor හි ප්‍රමිතික සැකසීම සඳහා උපක්‍රම

ඔබ කොහොමද එය කළේ?

නැවතත්, ලේඛනගත කිරීමෙහි හොඳ ආරම්භක උදාහරණයක් තිබේ (සම්බන්ධ වූ ශ්‍රේණි හරහා ගාස්තු ගණනය කිරීම), සමාන ගැටළු සඳහා එබී බැලීමට හෝ පදනමක් ලෙස ගත හැකිය.

අපි අවසානයේ කළ දේ:

  • පැය කිහිපයකින් මාලාවන් දෙකකට සම්බන්ධ වන්න, නාලිකා අනුව කණ්ඩායම් කිරීම;
  • දත්ත නොමැති නම් කණ්ඩායම අනුව මාලාව පුරවන්න;
  • පෙර දත්ත සමඟ අවසන් මිනිත්තු 10 මධ්යන්ය සංසන්දනය කරන්න;
  • අපි යමක් සොයාගත්තොත් කෑ ගහනවා;
  • අපි influxdb හි සිදු වූ ගණනය කළ අනුපාත සහ ඇඟවීම් ලියන්නෙමු;
  • slack වෙත ප්‍රයෝජනවත් පණිවිඩයක් යවන්න.

මගේ මතය අනුව, අපට අවසානයේ ලබා ගැනීමට අවශ්‍ය සියල්ල (සහ අභිරුචි හසුරුවන්නන් සමඟ ඊටත් වඩා ටිකක්) හැකි තරම් අලංකාර ලෙස සාක්ෂාත් කර ගැනීමට අපට හැකි විය.

ඔබට github.com බලන්න පුළුවන් කේත උදාහරණය и අවම පරිපථය (graphviz) ප්‍රතිඵලය වූ පිටපත.

ලැබෙන කේතයේ උදාහරණයක්:

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)

නිගමනය කුමක්ද?

කණ්ඩායම් සමූහයක් සමඟ අධීක්ෂණ-ඇඟවීම් සිදු කිරීම, දැනටමත් වාර්තා කර ඇති ප්‍රමිතික මත පදනම්ව අමතර ගණනය කිරීම් සිදු කිරීම, අභිරුචි ක්‍රියා සිදු කිරීම සහ ස්ක්‍රිප්ට් ධාවනය කිරීම (udf) සඳහා Kapacitor විශිෂ්ටයි.

ඇතුල් වීමට ඇති බාධකය ඉතා ඉහළ නොවේ - ග්‍රැෆානා හෝ වෙනත් මෙවලම් ඔබේ ආශාවන් සම්පූර්ණයෙන්ම තෘප්තිමත් නොකරන්නේ නම් එය උත්සාහ කරන්න.

මූලාශ්රය: www.habr.com

DDoS ආරක්ෂාව, VPS VDS සේවාදායකයන් සහිත අඩවි සඳහා විශ්වාසදායක සත්කාරකත්වය මිලදී ගන්න 🔥 DDoS ආරක්ෂාව, VPS VDS සේවාදායකයන් සහිත විශ්වාසදායක වෙබ් අඩවි සත්කාරකත්වය මිලදී ගන්න | ProHoster