[AEWS] #4์ฃผ์ฐจ ํ๋ก๋ฉํ ์ฐ์ค๋งคํธ๋ฆญ๊ณผ PromQL (4)
Observability (๊ด์ธก ๊ฐ๋ฅ์ฑ)
์ต์ ๋ฒ๋น๋ฆฌํฐ(๊ด์ธก๊ฐ๋ฅ์ฑ)์ ์์คํ ๋ด๋ถ ์ํ๋ฅผ ์ธ๋ถ์์ ์ดํดํ๊ณ ๋ถ์ํ ์ ์๋ ๋ฅ๋ ฅ์ ์๋ฏธํ๋ฉฐ, ์๋น์ค๊ฐ ์ด๋ค ์ํ์ธ์ง, ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง, ์ด๋์ ๋ฐ์ํ๋์ง๋ฅผ ํ์ ํ ์ ์๋๋ก ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ ๋ถ์ํ๋ค.์๋ฐฑ~์์ฒ ๊ฐ์ ์๋น์ค๊ฐ ๋ถ์ฐ ํ๊ฒฝ์์ ๋์ํ๋ฏ๋ก ๋ ๊น์ด ์๋ ๋ถ์์ด ํ์ํ์ฌ ์ต์ ๋ฒ๋น๋ฆฌํฐ์ ์ค์์ฑ์ด ๋์ฑ ๋์์ง๊ณ ์๋ค.
๊ธฐ์กด์ ๋ชจ๋ํฐ๋ง ๊ฐ๋ ๊ณผ ๋น๊ตํด๋ณด์์ ๋, ๋ชจ๋ํฐ๋ง์ ์์คํ ์ด ์ ์์ธ์ง ํ์ธํ๋ ๋๊ตฌ๋ก์จ ์ง๊ธ CPU ์ฌ์ฉ๋์ด ๋์๊ฐ์ ๋ํ ์ง๋ฌธ์ ๋ตํ๋ค๋ฉด, ์ต์ ๋ฒ๋น๋ฆฌํฐ๋ ์์คํ ์ด ์ ๊ทธ๋ ๊ฒ ๋์ํ๋์ง ๋ถ์ํ๋ ๊ฐ๋ ์ผ๋ก CPU ์ฌ์ฉ๋์ด ๋์์ง ์์ธ์ ๋ฌด์์ธ๊ฐ๋ฅผ ํ์ ํ๋ค.
์ฆ ์ต์ ๋ฒ๋น๋ฆฌํฐ๋ ์ฌ๋ฌ ์์ค์ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ์ฌ ์์คํ ๋์์ ์๊ด ๊ด๊ณ๋ฅผ ํ์ ํ๊ณ ์ง์์ ์ผ๋ก ๋ถ์ํ ์ ์๋ ๊ฒ์ ๋ชฉํ๋ก ๋๋ค.
ํ๋ก๋ฉํ ์ฐ์ค๋
ํ๋ก๋ฉํ ์ฐ์ค๋ ๊ณ ๋ ์ ํ์ ๋์ค๋ ์ ์ด๋ฆ์ธ๋ฐ ๋ถ์ ๊ฐ์ ธ๋ค ์ค ์๋ผ๋ ์๋ฏธ๊ฐ ์ง์์ ๊ฐ์ ธ๋ค ์ฃผ๋ ๋๊ตฌ๋ก์จ ๋ณด์ด์ง ์๋ ์์คํ ๋ด๋ถ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ๋ค ์ค๋ค๋ ์๋ฏธ๋ฅผ ํฌํจํ ๊ฒ ๊ฐ๋ค. ๋ค๋ฅธ ์คํ์์ค๋ค๋ ์ ํ์์ ์ด๋ฆ์ ๋ง์ด ์ฐจ์ฉํ๋ ๊ฒ ๊ฐ๋ค.
ํ๋ก๋ฉํ ์ฐ์ค๋ ์๊ณ์ด(time-series) ๋ฐ์ดํฐ ๊ธฐ๋ฐ์ ๋ชจ๋ํฐ๋ง ๋ฐ ๊ฒฝ๊ณ (alerting) ์์คํ ์ผ๋ก ์๋ฒ, ์ ํ๋ฆฌ์ผ์ด์ , ๋คํธ์ํฌ ์ฅ๋น ๋ฑ์์ ์๊ฐ์ ๋ฐ๋ผ ๋ณํ๋ ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ ์ ์ฅํ ํ, ๋ถ์ํ๊ณ ์๋์ ๋ณด๋ผ ์ ์๋ ๋๊ตฌ์ด๋ค.
์ฒ์์ SoundCloud์์ ๋ด๋ถ ๋ชจ๋ํฐ๋ง์ ์ํด ๊ฐ๋ฐ๋์๋๋ฐ ์ง๊ธ์ CNCF(Cloud Native Computing Foundation) ํ๋ก์ ํธ๋ก ์ฑ์ฅํด์ ์ฟ ๋ฒ๋คํฐ์ค(Kubernetes)๋ฅผ ๋น๋กฏํ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ํ๊ฒฝ์์ ๋ง์ด ์ฌ์ฉ๋๊ณ ์๋ค.
ํ๋ก๋ฉํ ์ฐ์ค ๋งคํธ๋ฆญ
๋งคํธ๋ฆญ์ ํค-๋ฐธ๋ฅ ๊ฐ์ผ๋ก ์ ์ฅ๋์ด ์์ผ๋ฉฐ ํ๊ธฐ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
<metric name>{<label name>=<label value>, ...}
ex) api_http_requests_total{method="POST", handler="/messages"}
๋งคํธ๋ฆญ ๋ค์, ๋ผ๋ฒจ, ํ์์คํฌํ๊ฐ ํค๋ก ์ ์ฅ๋๊ณ ๋ฐธ๋ฅ๋ก ๊ทธ ์์ ์์ ๋ฐ์ดํฐ ๊ฐ์ด ์ ์ฅ๋๋ ๋ฐฉ์์ด๋ค.
๋งคํธ๋ฆญ ํ์
์นด์ดํฐ
์ฆ๊ฐํ๋ ๊ฐ์ ์ ์ฅํ๋ ๋ฉํธ๋ฆญ ํ์
- ์ฃผ๋ก ์ด๋ฒคํธ ๋ฐ์ ํ์๋ฅผ ์ธก์ ํ ๋ ์ฌ์ฉ
- ์ฌ์ฉ ์์
- HTTP ์์ฒญ ์ (http_requests_total)
- ํ๋ก์ธ์ค ํฌ๋์ ํ์ (process_crashes_total)
- ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธํ ํ์ (user_logins_total)
๊ฒ์ด์ง
์ฆ๊ฐ / ๊ฐ์ํ๋ ๊ฐ์ ์ ์ฅํ๋ ๋ฉํธ๋ฆญ ํ์
- ํ์ฌ ์ํ๋ฅผ ์ธก์ ํ๋ ๋ฉํธ๋ฆญ.
- ๊ฐ์ด ์ฆ๊ฐํ๊ฑฐ๋ ๊ฐ์ํ ์ ์์.
- ๋ฆฌ์์ค ์ฌ์ฉ๋์ ๋ชจ๋ํฐ๋งํ ๋ ์์ฃผ ์ฌ์ฉ๋จ.
- ์ฌ์ฉ ์์
- ํ์ฌ ์ฌ์ฉ ์ค์ธ ๋ฉ๋ชจ๋ฆฌ (memory_usage_bytes)
- CPU ์ฌ์ฉ๋ (cpu_usage_percentage)
- ํ์ฌ ํ์ฑํ๋ ์ฌ์ฉ์ ์ (active_users)
ํ์คํ ๊ทธ๋จ
๋ฐ์ดํฐ์ ๋ถํฌ๋ฅผ ์ธก์ ํ๋ ๋ฉํธ๋ฆญ ํ์
- ๊ฐ์ ๋ถํฌ๋ฅผ ๋ฒํท(bucket)์ผ๋ก ๋๋์ด ์ ์ฅ
- ํน์ ๊ฐ ์ดํ์ธ ์์ฒญ์ ๊ฐ์๋ฅผ ์ธก์ ํ ์ ์์
- ํ๊ท ๊ฐ(average), 95th, 99th ํผ์ผํ์ผ(percentile) ๊ฐ์ ํต๊ณ๋ฅผ ๊ณ์ฐํ ๋ ์ฌ์ฉ
- ์ฌ์ฉ ์์
- HTTP ์์ฒญ ์ง์ฐ ์๊ฐ (http_request_duration_seconds)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์๊ฐ (db_query_duration_seconds)
- ์ ํ๋ฆฌ์ผ์ด์ ์๋ต ์๋ (app_response_time_seconds)
์๋จธ๋ฆฌ
Histogram๊ณผ ๋น์ทํ์ง๋ง, ์ง์ ๊ฐ์ ์ ์ฅํ๋ ๋ฉํธ๋ฆญ ํ์
- Histogram๊ณผ ๋ฌ๋ฆฌ, ๋ฏธ๋ฆฌ ๊ณ์ฐ๋ ํผ์ผํ์ผ(percentile) ๊ฐ์ ์ ์ฅ
- ๊ฐ๋ณ ์์ฒญ๋ค์ ํ๊ท (mean), ์ต์(min), ์ต๋(max), ํผ์ผํ์ผ(percentile) ๋ฑ์ ๊ณ์ฐ
- ํผ์ผํ์ผ ๊ฐ์ด ์ด๋ฏธ ๊ณ์ฐ๋์ด ์์ผ๋ฏ๋ก ๋ฐ์ดํฐ๋์ด ๋ง์์ง๋ฉด ๋ถ๋ด์ด ํด ์ ์์.
- ์ฌ์ฉ ์์
- API ์๋ต ์๋ (api_response_time_seconds)
- ์ฌ์ฉ์ ์ ๋ ฅ ๋๊ธฐ ์๊ฐ (user_input_delay_seconds)
Job, instance
์ก์ ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ๋ฉํธ๋ฆญ์ ์์งํ๋ ๋์ ๊ทธ๋ฃน์ผ๋ก, ํ๋ ์ด์์ ์ธ์คํด์ค๋ฅผ ํฌํจํ๋ ๋ฉํธ๋ฆญ ์์ง ๋จ์์ด๋ค.
scrape_configs:
- job_name: "node_exporter" # Job ์ด๋ฆ (์ฌ๋ฌ ๊ฐ์ ์ธ์คํด์ค ํฌํจ ๊ฐ๋ฅ)
static_configs:
- targets: ["192.168.1.10:9100", "192.168.1.11:9100"]
์ธ์คํด์ค๋ ์ค์ ๋ฉํธ๋ฆญ์ ์ ๊ณตํ๋ ๊ฐ๋ณ์ ์ธ ์๋ํฌ์ธํธ๋ก ์ก ์์์ ๊ฐ๋ณ์ ์ผ๋ก ๋ชจ๋ํฐ๋งํ๋ ์๋น์ค์ ์ค์ ์๋ฒ์ด๋ค.
scrape_configs:
- job_name: "web_server"
static_configs:
- targets: ["192.168.1.20:8080", "192.168.1.21:8080"]
์๊ณ์ด ๋ฐ์ดํฐ(Time-Series Data)
์๊ฐ์ ๋ฐ๋ผ ๋ณํํ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๋ถ์ํ๋ ๋ฐฉ์์ผ๋ก ํน์ ์๊ฐ ๊ฐ๊ฒฉ์ ๋๊ณ ์ฐ์์ ์ผ๋ก ๊ด์ธก๋ ๊ฐ์ ์๋ฏธํ๋ค.
- CPU ์ฌ์ฉ๋ (10์ 30๋ถ - 50%, 10์ 31๋ถ - 52%)
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ (10์ 30๋ถ - 4GB, 10์ 31๋ถ - 4.2GB)
- ๋คํธ์ํฌ ํธ๋ํฝ (10์ 30๋ถ - 1Gbps, 10์ 31๋ถ - 1.2Gbps)
์ฅ์
- ์๊ฐ ๊ธฐ๋ฐ ๋ถ์์ด ๊ฐ๋ฅ -> ์ง๋ 24์๊ฐ ๋์ CPU ์ฌ์ฉ๋์ด ์ด๋ป๊ฒ ๋ณํ๋๊ฐ?
- ์ถ์ธ ์์ธก ๊ฐ๋ฅ -> ํธ๋ํฝ ์ฆ๊ฐ ํจํด์ ๋ณด๋ฉด 3์ผ ํ์ ์๋ฒ๊ฐ ํฐ์ง ๊ฐ๋ฅ์ฑ
- ์ค์๊ฐ ๋ชจ๋ํฐ๋ง & ์๋ -> ์๋ต ์๊ฐ์ด 1์ด๋ฅผ ๋์ผ๋ฉด ์๋ ์ ์ก
๋จ์
- 1์ด๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ฉด ์์ฒญ๋ ์์ ๋ฐ์ดํฐ๊ฐ ์์ด๊ฒ ๋๋ค.
- ์ผ๋ฐ์ ์ธ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค(SQL)๋ก๋ ๊ด๋ฆฌ๊ฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์ TSDB๊ฐ ํ์ํ๋ค.
์ต์คํฌํฐ
ํ๋ก๋ฉํ ์ฐ์ค ์ต์คํฌํฐ(Exporter)๋ ๋ฉํธ๋ฆญ์ ์ ๊ณตํ์ง ์๋ ์์คํ ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์์งํ ์ ์๋๋ก ๋ณํํ๋ ์ญํ ์ ํ๋๋ฐ, ๋ฐ์ดํฐ๋ฅผ ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์ดํดํ ์ ์๋ ํ์์ผ๋ก ๋ณํํ๋ค.
์ฆ ์ต์คํฌํฐ๋ ์ธ๋ถ ์์คํ ์์ ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ , HTTP ์๋ํฌ์ธํธ(/metrics) ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๋ ธ์ถํ์ฌ, ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก(Pull ๋ฐฉ์) ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ๊ฐ๋๋ก ํ๋ค.
๋๋ ์ฃผ๋ก ๋ ธ๋ ์ต์คํฌํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌ๋ ์ค ์์คํ ์ CPU, ๋ฉ๋ชจ๋ฆฌ, ๋์คํฌ, ๋คํธ์ํฌ ์ํ ์์งํ๋ ์ฉ๋๋ก ํ์ฉํ์์๋ค.
CPU ์ฌ์ฉ๋์ ์ธก์ ํ๋ ์ต์คํฌํฐ ์์๋ ์๋์ ๊ฐ๋ค.
pip install prometheus_client
from prometheus_client import start_http_server, Gauge
import psutil
import time
# ๊ฒ์ด์ง ํ์
์ ๋งคํธ๋ฆญ ํ์
cpu_usage = Gauge("custom_cpu_usage", "Current CPU usage percentage")
def collect_metrics():
while True:
cpu_usage.set(psutil.cpu_percent()) # ํ์ฌ CPU ์ฌ์ฉ๋์ ๋ฉํธ๋ฆญ์ผ๋ก ์ค์
time.sleep(5) # 5์ด๋ง๋ค ์
๋ฐ์ดํธ
if __name__ == "__main__":
start_http_server(8000) # ์ต์คํฌํฐ HTTP ์๋ฒ
collect_metrics() # ๋ฉํธ๋ฆญ ์์ง
์ด๋ ๊ฒ ์ปค์คํ ์ผ๋ก ์์ฑํ ์ต์คํฌํฐ๋ฅผ ํ๋ก๋ฉํ ์ฐ์ค๊ฐ ์์งํ๋๋ก ์ค์ ํ๋ ค๋ฉด, prometheus.yaml ํ์ผ์ ์์ฑํ ์ปค์คํ ์ต์คํฌํฐ๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
scrape_configs:
- job_name: "custom_exporter"
static_configs:
- targets: ["localhost:8000"]
PromQL
PromQL(Prometheus Query Language)์ ํ๋ก๋ฉํ
์ฐ์ค์์ ๋ฉํธ๋ฆญ์ ๊ฒ์ํ๊ณ ๋ถ์ํ๋ ๋ฐ ์ฌ์ฉํ๋ ์ฟผ๋ฆฌ ์ธ์ด๋ก
๋ฐ์ดํฐ ํํฐ๋ง, ์ง๊ณ(Aggregation), ๋ณํ ๋ฑ์ ์ํํ ์ ์์ผ๋ฉฐ, Grafana ๋์๋ณด๋์์ ์๊ฐํํ ๋๋ ์ฌ์ฉ๋๋ค.
SQL๊ณผ ๋น๊ตํด์ ๋ณด๋ฉด ์ข ๋ ๋น ๋ฅด๊ฒ ์ฌ์ฉ์ด ๊ฐ๋ฅํด์ ์ ๋ฆฌํ์๋ค.
๊ธฐ๋ฅ | SQL | PromQL |
๋ฐ์ดํฐ ์กฐํ | SELECT column FROM table; | <metric_name> |
์กฐ๊ฑด ํํฐ๋ง | SELECT column FROM table WHERE column = 'value' | <metric_name>{label="value"} |
์ฌ๋ฌ ์กฐ๊ฑด ํํฐ | SELECT column FROM table WHERE col1 = 'val1' AND col2 = 'val2' | <metric_name>{label1="val1", label2="val2"} |
๋ฐ์ดํฐ ๊ทธ๋ฃนํ | SELECT column, SUM(value) FROM table GROUP BY column; | sum(<metric_name>) by (label) |
์ง๊ณ | SELECT SUM(column) FROM table; | sum(<metric_name>) |
์ง๊ณ + ๊ทธ๋ฃนํ | SELECT col, SUM(value) FROM table GROUP BY col; | sum(<metric_name>) by (label) |
์ ๋ ฌ | SELECT column FROM table ORDER BY column DESC LIMIT N; | topk(N, <metric_name>) |
์๊ฐ ๊ธฐ๋ฐ ํํฐ๋ง | SELECT column FROM table WHERE time > NOW() - INTERVAL '5 minutes'; | <metric_name>[5m] |
๋ฐ์ดํฐ ์ฆ๊ฐ๋ | SELECT (column2 - column1) FROM table WHERE time BETWEEN NOW() - INTERVAL '5 minutes' AND NOW(); | increase(<metric_name>[5m]) |
์ด๋น ๋ณํ์จ | SELECT (column2 - column1) / time_diff FROM table WHERE time BETWEEN NOW() - INTERVAL '5 minutes' AND NOW(); | rate(<metric_name>[5m]) |
ํผ์ผํ์ผ ๊ณ์ฐ | SELECT PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY column) FROM table; | histogram_quantile(0.95, <metric_name>) |
promql์ ๋ณด๋ฉด์ ํนํ ํท๊ฐ๋ ธ๋.. rate, irate, increase์ ๋ํด์ ์ข ๋ ์์ธํ ์ ๋ฆฌํด๋ณด์๋ค.
3๊ฐ์ง ํจ์ ๋ชจ๋ ์นด์ดํฐ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ค.
rate (์ด๋น ํ๊ท ์ฆ๊ฐ์จ)
rate(nginx_http_requests_total[10m]) ํจ์๋ ์ต๊ทผ 10๋ถ ๋์์ nginx HTTP ์์ฒญ ์ด์ ์นด์ดํฐ(nginx_http_requests_total)๊ฐ ์ด๋น ์ผ๋ง๋ ์ฆ๊ฐํ๋์ง ํ๊ท ๊ฐ์ ๊ณ์ฐํ๋ค.
[10m]๋ ์ต๊ทผ 10๋ถ ๋์์ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ ๋ฒ์ ๋ฒกํฐ๋ฅผ ์์ฑํ๋๋ฐ ์ด ๋ฒกํฐ์๋ ์ง์ ๋ ์๊ณ์ด์ ์ฌ๋ฌ ์์ ์ ๊ฐ๋ค์ด ํฌํจ๋๋ค.
๋ฒ์ ๋ด์ ์ฒซ ๋ฒ์งธ์ ๋ง์ง๋ง ์ํ ๊ฐ์ ํ์ธํ์ฌ (๋ง์ง๋ง ๊ฐ - ์ฒซ ๋ฒ์งธ ๊ฐ)์ ๊ตฌํ๊ณ , ์ด ๊ฐ์ด ์ ์ฒด ์๊ฐ(10๋ถ, ์ฆ 600์ด) ๋์ ์ผ๋ง๋ ์ฆ๊ฐํ๋์ง๋ฅผ ๊ณ์ฐํ๋ค.
irate (์ด๋น ์๊ฐ ์ฆ๊ฐ์จ)
๊ฐ์ฅ ์ต๊ทผ ๋ ๊ฐ์ ๋ฐ์ดํฐ ํฌ์ธํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋น ์ฆ๊ฐ์จ์ ๊ณ์ฐํ๋ค.
๋ง์ฝ ์๋์ ๊ฐ์ด ๊ฐ์ด ์ ์ฅ๋์ด ์์๋ค๋ฉด
- 10:04:00 1500
- 10:05:00 1700
์ด๋น ์์ฒญ ์ฆ๊ฐ์จ์ (1700 - 1500) / 60์ด = 3.33 req/sec ์ด ๋๋ค.
irate๋ก ๊ฐ์์ค๋ฌ์ด ๊ธ์ฆ์ด๋ ๊ฐ์ ๊ฐ์ ๋ณผ ๋ ์ ์ฉํ๋ค.
increase (์ง์ ๋ ๊ธฐ๊ฐ ๋์์ ์ด ์ฆ๊ฐ๋)
์ง์ ๋ ์๊ฐ ๋ฒ์ ๋์ ์ ์ฒด ์ฆ๊ฐ๋(์ดํฉ)์ด๋ฉฐ ํด๋น ์๊ฐ๋์ ์ผ๋ง๋ ๋ง์ ์์ฒญ์ด ๋ฐ์ํ๋๋ ๋ถ์์ด ๊ฐ๋ฅํ๋ฉฐ, ์ฅ๊ธฐ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์์งํ ๋ ์ ํฉํ๋ค.