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() ์‚ฌ์šฉ

ํƒœ๊ทธ/ํ•„๋“œ๊ฐ€ ์ฑ„์›Œ์ง€์ง€ ์•Š์œผ๋ฉด ๊ณ„์‚ฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

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

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

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

ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋งˆ์ง€๋ง‰ 5๋ถ„ ๋™์•ˆ์˜ ๊ธฐ๊ฐ„

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

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

๋งˆ์ง€๋ง‰ 5๋ถ„์— ๋Œ€ํ•œ ๋Œ€์•ˆ์€ ์ง€์ •๋œ ์‹œ๊ฐ„ ์ „์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฐจ๋‹จํ•˜๋Š” 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์— ์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ์ž‘์€ ํŒŒ์ด์ฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ฒ˜์Œ์—๋Š” 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 ๋กœ๊ทธ ๋ ˆ๋ฒจ=์˜ค๋ฅ˜

httpOut ์˜ต์…˜

ํ˜„์žฌ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

|httpOut('something')

์‹œ์ฒญ(๋ฐ›๊ธฐ): ํ˜ธ์ŠคํŠธ ๋˜๋Š” IP:9092/kapacitor/v1/์ž‘์—…/์ž‘์—…_์ด๋ฆ„/๋ญ”๊ฐ€

๊ตฌํ˜„ ๊ณ„ํš

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

๋˜ ์–ด๋””์—์„œ ๊ธ์–ด๋ชจ์„ ์ˆ˜ ์žˆ๋‚˜์š”?

influxdb์˜ writeback ํƒ€์ž„์Šคํƒฌํ”„

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

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

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

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

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

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

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

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

๋ฐ˜๋ฉด์— ํฌ๋กœ๋…ธ๊ทธ๋ž˜ํ”ผ์—์„œ๋Š” ์ด๋Ÿฐ ์„ ์ด ์กด์žฌํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ํ—ˆ์šฉ๋˜์ง€ ์•Š์œผ๋ฉฐ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

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

์ผ๋Œ€๋‹ค๋กœ ์—ฐ๊ฒฐํ•˜๋‹ค

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

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

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

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

์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์œผ๋กœ ๋ฌด์—‡์„ ํ•ด๊ฒฐํ•˜๊ณ  ์žˆ์—ˆ๋Š”๊ฐ€?

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

์™œ Grafana๋Š” ์•„๋‹Œ๊ฐ€?

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

Grafana๋Š” ์ฐจ์› ๊ฐ„ ๊ณ„์‚ฐ ๋ฐ ์•Œ๋ฆผ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์ง€๋งŒ ๋น„์œจ(์š”์ฒญ-์˜ค๋ฅ˜)/์š”์ฒญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

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

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

์„ฑ๊ณต์ ์ธ ์ฟผ๋ฆฌ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋œ ์‚ฌ์•…ํ•ฉ๋‹ˆ๋‹ค.

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

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

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

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

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

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

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

์–ด๋–ป๊ฒŒ ํ–ˆ๋‚˜์š”?

๋‹ค์‹œ ํ•œ๋ฒˆ, ๋ฌธ์„œ์— ์ข‹์€ ์‹œ์ž‘ ์˜ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.๊ฒฐํ•ฉ๋œ ์‹œ๋ฆฌ์ฆˆ์— ๋Œ€ํ•œ ๋น„์œจ ๊ณ„์‚ฐ), ์‚ดํŽด๋ณด๊ฑฐ๋‚˜ ๋น„์Šทํ•œ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ธฐ์ดˆ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ตญ ์šฐ๋ฆฌ๊ฐ€ ํ•œ ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋ช‡ ์‹œ๊ฐ„ ์•ˆ์— ๋‘ ๊ฐœ์˜ ์—ํ”ผ์†Œ๋“œ๋ฅผ ํ•ฉ์น˜๊ณ  ์ฑ„๋„๋ณ„๋กœ ๊ทธ๋ฃนํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๊ทธ๋ฃน๋ณ„๋กœ ์‹œ๋ฆฌ์ฆˆ๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค.
  • ์ง€๋‚œ 10๋ถ„ ๋™์•ˆ์˜ ์ค‘๊ฐ„๊ฐ’์„ ์ด์ „ ๋ฐ์ดํ„ฐ์™€ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.
  • ๋ญ”๊ฐ€ ๋ฐœ๊ฒฌํ•˜๋ฉด ์†Œ๋ฆฌ์น˜์ž.
  • ๊ณ„์‚ฐ๋œ ์š”์œจ๊ณผ ๋ฐœ์ƒํ•œ ์•Œ๋ฆผ์„ influxdb์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
  • 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)๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

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

์ถœ์ฒ˜ : habr.com

DDoS ๋ณดํ˜ธ, VPS VDS ์„œ๋ฒ„๊ฐ€ ์žˆ๋Š” ์‚ฌ์ดํŠธ๋ฅผ ์œ„ํ•œ ์•ˆ์ •์ ์ธ ํ˜ธ์ŠคํŒ… ๊ตฌ์ž… ๐Ÿ”ฅ DDoS ๊ณต๊ฒฉ ๋ฐฉ์ง€ ๊ธฐ๋Šฅ์ด ํƒ‘์žฌ๋œ ์•ˆ์ •์ ์ธ ์›น์‚ฌ์ดํŠธ ํ˜ธ์ŠคํŒ…, VPS ๋ฐ VDS ์„œ๋ฒ„๋ฅผ ๊ตฌ๋งคํ•˜์„ธ์š” | ProHoster