Kapacitor์—์„œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์š”๋ น

์•„๋งˆ๋„ ์˜ค๋Š˜๋‚  ์„œ๋น„์Šค ์ง€ํ‘œ๋ฅผ ์ˆ˜์ง‘ํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋ฅผ ๋ฌป๋Š” ์‚ฌ๋žŒ์€ ์•„๋ฌด๋„ ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ๋…ผ๋ฆฌ์  ๋‹จ๊ณ„๋Š” ์ˆ˜์ง‘๋œ ์ง€ํ‘œ์— ๋Œ€ํ•ด ํŽธ๋ฆฌํ•œ ์ฑ„๋„(๋ฉ”์ผ, Slack, Telegram)์˜ ๋ฐ์ดํ„ฐ ํŽธ์ฐจ์— ๋Œ€ํ•ด ์•Œ๋ฆฌ๋Š” ๊ฒฝ๊ณ ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜จ๋ผ์ธ ํ˜ธํ…” ์˜ˆ์•ฝ ์„œ๋น„์Šค์—์„œ 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)

์กฐ์ธ ์ฑ„์šฐ๊ธฐ(๋‚ด๋ถ€ ๋Œ€ ์™ธ๋ถ€)

๊ธฐ๋ณธ์ ์œผ๋กœ ์กฐ์ธ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ์ง€์ (๋‚ด๋ถ€)์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
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)

ํ•ด๋‹น ๊ธฐ๊ฐ„ ๋™์•ˆ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๋งˆ์ง€๋ง‰ XNUMX๋ถ„

์˜ˆ๋ฅผ ๋“ค์–ด ์ง€๋‚œ XNUMX๋ถ„ ๋™์•ˆ์˜ ๊ฐ’์„ ์ง€๋‚œ์ฃผ์™€ ๋น„๊ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ๋ณ„๋„ ๋ฐฐ์น˜๋กœ ๋‘ ๊ฐœ์˜ ๋ฐ์ดํ„ฐ ๋ฐฐ์น˜๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ๋” ํฐ ๊ธฐ๊ฐ„์—์„œ ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 |where(lambda: duration((unixNano(now()) - unixNano("time"))/1000, 1u) < 5m)

๋งˆ์ง€๋ง‰ XNUMX๋ถ„ ๋™์•ˆ์˜ ๋Œ€์•ˆ์€ ์ง€์ •๋œ ์‹œ๊ฐ„ ์ด์ „์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฐจ๋‹จํ•˜๋Š” 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" }}')

๋งž์ถคํ˜• ํ•ธ๋“ค๋Ÿฌ

์ฒ˜๋ฆฌ๊ธฐ์˜ ํฐ ๋ชฉ๋ก์—๋Š” ์ „๋‹ฌ๋œ ๋งค๊ฐœ๋ณ€์ˆ˜(stdin)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” exec๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐฝ์˜์„ฑ ๊ทธ ์ด์ƒ์€ ์•„๋‹™๋‹ˆ๋‹ค!

์šฐ๋ฆฌ์˜ ๊ด€์Šต ์ค‘ ํ•˜๋‚˜๋Š” Slack์— ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ์ž‘์€ Python ์Šคํฌ๋ฆฝํŠธ์ž…๋‹ˆ๋‹ค.
์ฒ˜์Œ์—๋Š” ์Šน์ธ์œผ๋กœ ๋ณดํ˜ธ๋œ grafana ์‚ฌ์ง„์„ ๋ฉ”์‹œ์ง€๋กœ ๋ณด๋‚ด๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ณ„๋„์˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์•„๋‹Œ ๋™์ผํ•œ ๊ทธ๋ฃน์˜ ์ด์ „ ๊ฒฝ๊ณ ์— ๋Œ€ํ•œ ์Šค๋ ˆ๋“œ์— 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/์ž‘์—…/์ž‘์—… ์ด๋ฆ„/๋ญ”๊ฐ€

์‹คํ–‰ ๊ณ„ํš

  • ๊ฐ ์ž‘์—…์€ ๋‹ค์Œ ํ˜•์‹์œผ๋กœ ์œ ์šฉํ•œ ์ˆซ์ž๊ฐ€ ํฌํ•จ๋œ ์‹คํ–‰ ํŠธ๋ฆฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜ํ”„๋น„์ฆˆ.
  • ๋ธ”๋ก์„ ๊ฐ€์ ธ ๊ฐ€๋ผ. ์ .
  • ๋ทฐ์–ด์— ๋ถ™์—ฌ๋„ฃ๊ณ , ์ฆ๊ธฐ๋‹ค.

๊ฐˆํ€ด๋Š” ๋˜ ์–ด๋””์„œ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

์“ฐ๊ธฐ ์ €์žฅ ์‹œ influxdb์˜ ํƒ€์ž„์Šคํƒฌํ”„

์˜ˆ๋ฅผ ๋“ค์–ด, ์‹œ๊ฐ„๋‹น ์š”์ฒญ ํ•ฉ๊ณ„(groupBy(1h))์— ๋Œ€ํ•œ ์•Œ๋ฆผ์„ ์„ค์ •ํ•˜๊ณ  influxdb์— ๋ฐœ์ƒํ•œ ์•Œ๋ฆผ์„ ๊ธฐ๋กํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค(๋ฌธ์ œ์˜ ์‚ฌ์‹ค์„ grafana์˜ ๊ทธ๋ž˜ํ”„์— ์•„๋ฆ„๋‹ต๊ฒŒ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด).

influxDBOut()์€ ๊ฒฝ๊ณ ์˜ ์‹œ๊ฐ„ ๊ฐ’์„ ํƒ€์ž„์Šคํƒฌํ”„์— ๊ธฐ๋กํ•˜๋ฏ€๋กœ ์ฐจํŠธ์˜ ์ง€์ ์€ ๊ฒฝ๊ณ ๊ฐ€ ๋„์ฐฉํ•œ ๊ฒƒ๋ณด๋‹ค ์ด์ „/๋Šฆ๊ฒŒ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.

์ •ํ™•์„ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ: ํ˜„์žฌ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ influxdb์— ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๋„์ปค, ๋นŒ๋“œ ๋ฐ ๋ฐฐํฌ

์‹œ์ž‘ ์‹œ kapacitor๋Š” [load] ๋ธ”๋ก์˜ ๊ตฌ์„ฑ์— ์ง€์ •๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ์ž‘์—…, ํ…œํ”Œ๋ฆฟ ๋ฐ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž‘์—…์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ƒ์„ฑํ•˜๋ ค๋ฉด ๋‹ค์Œ ์‚ฌํ•ญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. ํŒŒ์ผ ์ด๋ฆ„ โ€“ ์Šคํฌ๋ฆฝํŠธ ID/์ด๋ฆ„์œผ๋กœ ํ™•์žฅ๋จ
  2. ์œ ํ˜• โ€“ ์ŠคํŠธ๋ฆผ/๋ฐฐ์น˜
  3. dbrp โ€“ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค + ์ •์ฑ…์„ ๋‚˜ํƒ€๋‚ด๋Š” ํ‚ค์›Œ๋“œ(dbrp โ€œsupplier.โ€โ€œautogenโ€)

์ผ๋ถ€ ๋ฐฐ์น˜ ์ž‘์—…์— dbrp ์ค„์ด ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์ „์ฒด ์„œ๋น„์Šค๊ฐ€ ์‹œ์ž‘์„ ๊ฑฐ๋ถ€ํ•˜๊ณ  ์ด์— ๋Œ€ํ•ด ์†”์งํ•˜๊ฒŒ ๋กœ๊ทธ์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€๋กœ chronograf์—์„œ๋Š” ์ด ์ค„์ด ์กด์žฌํ•ด์„œ๋Š” ์•ˆ ๋˜๋ฉฐ, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ํ—ˆ์šฉ๋˜์ง€ ์•Š๊ณ  ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ปจํ…Œ์ด๋„ˆ ๋นŒ๋“œ ์‹œ ํ•ดํ‚น: //.+dbrp๊ฐ€ ํฌํ•จ๋œ ํ–‰์ด ์žˆ๋Š” ๊ฒฝ์šฐ Dockerfile์€ -1๋กœ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ ๋นŒ๋“œ๋ฅผ ์–ด์…ˆ๋ธ”ํ•  ๋•Œ ์‹คํŒจ ์ด์œ ๋ฅผ ์ฆ‰์‹œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋Œ€๋‹ค์— ํ•ฉ๋ฅ˜ํ•˜๋‹ค

์˜ˆ์ œ ์ž‘์—…: ์ผ์ฃผ์ผ ๋™์•ˆ ์„œ๋น„์Šค ์šด์˜ ์‹œ๊ฐ„์˜ 95๋ฒˆ์งธ ๋ฐฑ๋ถ„์œ„์ˆ˜๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋งˆ์ง€๋ง‰ 10๋ถ„์˜ ๊ฐ ๋ถ„์œ„์ˆ˜๋ฅผ ์ด ๊ฐ’๊ณผ ๋น„๊ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋Œ€๋‹ค ์กฐ์ธ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํฌ์ธํŠธ ๊ทธ๋ฃน์— ๋Œ€ํ•œ ๋งˆ์ง€๋ง‰/ํ‰๊ท /์ค‘์•™๊ฐ’์€ ๋…ธ๋“œ๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. "์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ํ•˜์œ„ ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ๋ฐฐ์น˜ -> ์ŠคํŠธ๋ฆผ" ์˜ค๋ฅ˜๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

๋žŒ๋‹ค ์‹์˜ ๋ณ€์ˆ˜์ธ ์ผ๊ด„ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋„ ๋Œ€์ฒด๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋ฐฐ์น˜์—์„œ ํ•„์š”ํ•œ ์ˆซ์ž๋ฅผ udf๋ฅผ ํ†ตํ•ด ํŒŒ์ผ์— ์ €์žฅํ•˜๊ณ  ์‚ฌ์ด๋“œ๋กœ๋“œ๋ฅผ ํ†ตํ•ด ์ด ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋Š” ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์œผ๋กœ ๋ฌด์—‡์„ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๊นŒ?

์šฐ๋ฆฌ๋Š” ์•ฝ 100๊ฐœ์˜ ํ˜ธํ…” ๊ณต๊ธ‰์—…์ฒด๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๊ฐ๊ฐ์€ ์—ฌ๋Ÿฌ ์—ฐ๊ฒฐ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ฑ„๋„์ด๋ผ๊ณ  ๋ถ€๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฑ„๋„์€ ์•ฝ 300๊ฐœ๊ฐ€ ์žˆ์œผ๋ฉฐ, ๊ฐ ์ฑ„๋„์ด ๋Š์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ก๋œ ๋ชจ๋“  ์ง€ํ‘œ ์ค‘์—์„œ ์˜ค๋ฅ˜์œจ(์š”์ฒญ ๋ฐ ์˜ค๋ฅ˜)์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ผํŒŒ๋‚˜๋Š” ์™œ ์•ˆ๋˜๋‚˜์š”?

Grafana์— ๊ตฌ์„ฑ๋œ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ผ๋ถ€๋Š” ์ค‘์š”ํ•˜๊ณ  ์ผ๋ถ€๋Š” ๋ˆˆ์„ ๊ฐ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Grafana๋Š” ์ธก์ • + ์•Œ๋ฆผ ๊ฐ„ ๊ณ„์‚ฐ ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด์ง€๋งŒ ๋น„์œจ(์š”์ฒญ-์˜ค๋ฅ˜)/์š”์ฒญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋ฅ˜๊ฐ€ ์‹ฌํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.

Kapacitor์—์„œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์š”๋ น

์„ฑ๊ณต์ ์ธ ์š”์ฒญ์œผ๋กœ ๋ณผ ๋•Œ ๋œ ์•…ํ•ฉ๋‹ˆ๋‹ค.

Kapacitor์—์„œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์š”๋ น

์ข‹์Šต๋‹ˆ๋‹ค. Grafana ์ด์ „์— ์„œ๋น„์Šค ์š”๊ธˆ์„ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ด๊ฒƒ์ด ์ž‘๋™ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ๋Š” ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—... ๊ฐ ์ฑ„๋„๋งˆ๋‹ค ์ž์ฒด ๋น„์œจ์ด "์ •์ƒ"์œผ๋กœ ๊ฐ„์ฃผ๋˜๋ฉฐ ๊ฒฝ๊ณ ๋Š” ์ •์  ๊ฐ’์— ๋”ฐ๋ผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค(๋ˆˆ์œผ๋กœ ์ฐพ๊ณ  ๊ฒฝ๊ณ ๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋ฉด ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค).

๋‹ค์Œ์€ ๋‹ค์–‘ํ•œ ์ฑ„๋„์— ๋Œ€ํ•œ "์ •์ƒ"์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค.

Kapacitor์—์„œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์š”๋ น

Kapacitor์—์„œ ๋ฉ”ํŠธ๋ฆญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์š”๋ น

์šฐ๋ฆฌ๋Š” ์ด์ „ ์š”์ ์„ ๋ฌด์‹œํ•˜๊ณ  "์ •์ƒ์ ์ธ" ๊ทธ๋ฆผ์ด ๋ชจ๋“  ๊ณต๊ธ‰์—…์ฒด์— ์œ ์‚ฌํ•˜๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ๋ชจ๋“  ๊ฒƒ์ด ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. Grafana์—์„œ ๊ฒฝ๊ณ ๋ฅผ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋‹ค์Œ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
a) ๊ฐ ์ฑ„๋„์— ๋Œ€ํ•ด ๊ฐœ๋ณ„์ ์œผ๋กœ ๋งŽ์€ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค(๊ทธ๋ฆฌ๊ณ  ๊ณ ํ†ต์Šค๋Ÿฝ๊ฒŒ ๋™๋ฐ˜ํ•ฉ๋‹ˆ๋‹ค).
b) ๋ชจ๋“  ์ฑ„๋„์ด ํฌํ•จ๋œ ํ•˜๋‚˜์˜ ์ฐจํŠธ๋ฅผ ๋‚จ๊น๋‹ˆ๋‹ค. (๊ทธ๋ฆฌ๊ณ  ๋‹ค์ฑ„๋กœ์šด ์„ ๊ณผ ๋งž์ถคํ˜• ์•Œ๋ฆผ์— ๋น ์ ธ๋“ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.)

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)

๊ทธ๋ฆฌ๊ณ  ๊ฒฐ๋ก ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

Kapacitor๋Š” ์—ฌ๋Ÿฌ ๊ทธ๋ฃนํ™”๋ฅผ ํ†ตํ•ด ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฒฝ๊ณ ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , ์ด๋ฏธ ๊ธฐ๋ก๋œ ์ธก์ •ํ•ญ๋ชฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ถ”๊ฐ€ ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์‚ฌ์šฉ์ž ์ง€์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์Šคํฌ๋ฆฝํŠธ(udf)๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ํƒ์›”ํ•ฉ๋‹ˆ๋‹ค.

์ง„์ž… ์žฅ๋ฒฝ์€ ๊ทธ๋‹ค์ง€ ๋†’์ง€ ์•Š์Šต๋‹ˆ๋‹ค. grafana ๋˜๋Š” ๊ธฐํƒ€ ๋„๊ตฌ๊ฐ€ ๊ท€ํ•˜์˜ ์š•๊ตฌ๋ฅผ ์™„์ „ํžˆ ์ถฉ์กฑ์‹œํ‚ค์ง€ ๋ชปํ•œ๋‹ค๋ฉด ์‹œ๋„ํ•ด ๋ณด์‹ญ์‹œ์˜ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€