모니터링 도구 비교 분석 2025
Prometheus vs Datadog vs New Relic vs Grafana - 모니터링 플랫폼 선택 가이드
📊 모니터링 도구 비교 분석 2025
애플리케이션과 인프라 모니터링을 위한 최적의 관측성(Observability) 플랫폼 선택 가이드
📊 개요
비교 대상
- Prometheus + Grafana: 오픈소스 모니터링 스택
- Datadog: 클라우드 기반 통합 모니터링
- New Relic: Full-stack 관측성 플랫폼
- Elastic Stack (ELK): 로그 중심 모니터링
- AWS CloudWatch: AWS 네이티브 모니터링
- Dynatrace: AI 기반 APM
평가 기준
- 메트릭 수집 능력
- 로그 분석
- APM (Application Performance Monitoring)
- 분산 추적
- 알림 기능
- 비용
- 확장성
📈 상세 비교표
핵심 특성 비교
| 특성 | Prometheus | Datadog | New Relic | ELK Stack | | --------------- | ---------- | ---------- | ---------- | ------------- | | 호스팅 | 자체 | SaaS | SaaS | 자체/클라우드 | | 가격 | 무료 | 높음 | 높음 | 무료/유료 | | 설정 복잡도 | 높음 | 낮음 | 중간 | 높음 | | 확장성 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | | 통합 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | | 실시간성 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
기능 비교
| 기능 | Prometheus | Datadog | New Relic | ELK Stack | | --------------- | ---------- | ---------- | ---------- | ---------- | | 메트릭 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | | 로그 | ❌ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | APM | ❌ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | | 분산 추적 | ❌ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | | 인프라 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | | 사용자 경험 | ❌ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ❌ |
가격 비교 (월 기준)
| 규모 | Prometheus | Datadog | New Relic | ELK Cloud | | -------------- | ---------- | ------- | --------- | --------- | | 10 호스트 | $0 | $150 | $750 | $95 | | 50 호스트 | $0 | $750 | $3,750 | $475 | | 100 호스트 | $0 | $1,500 | $7,500 | $950 | | 500 호스트 | $0 | $7,500 | $37,500 | $4,750 |
운영 비용 별도
💼 사용 사례별 구현
🏢 대규모 마이크로서비스
추천: Prometheus + Grafana + Jaeger
# prometheus.yml - Prometheus 설정
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: "production"
region: "ap-northeast-2"
# 알림 매니저
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
# 규칙 파일
rule_files:
- "alerts/*.yml"
- "recording/*.yml"
# 서비스 디스커버리
scrape_configs:
# Kubernetes 파드 자동 발견
- job_name: "kubernetes-pods"
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels:
[__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
# Node Exporter
- job_name: "node"
static_configs:
- targets: ["node-exporter:9100"]
# 커스텀 애플리케이션 메트릭
- job_name: "app-metrics"
metrics_path: "/metrics"
static_configs:
- targets: ["app-service:8080"]
# alerts.yml - 알림 규칙
groups:
- name: service_alerts
interval: 30s
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
> 0.05
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "High error rate on {{ $labels.service }}"
description: "{{ $labels.service }} has {{ $value | humanizePercentage }} error rate"
- alert: HighMemoryUsage
expr: |
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))
* 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is {{ $value | humanize }}%"
- alert: PodCrashLooping
expr: |
rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"
// Go 애플리케이션 계측
package main
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests.",
Buckets: prometheus.DefBuckets,
}, []string{"path", "method", "status"})
httpRequests = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
}, []string{"path", "method", "status"})
activeConnections = promauto.NewGauge(prometheus.GaugeOpts{
Name: "http_active_connections",
Help: "Number of active HTTP connections.",
})
)
// 미들웨어
func prometheusMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timer := prometheus.NewTimer(httpDuration.WithLabelValues(
r.URL.Path, r.Method, ""))
activeConnections.Inc()
defer activeConnections.Dec()
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(rw, r)
timer.ObserveDuration()
httpRequests.WithLabelValues(
r.URL.Path, r.Method, strconv.Itoa(rw.statusCode),
).Inc()
})
}
// Grafana 대시보드 (JSON)
{
"dashboard": {
"title": "Application Metrics",
"panels": [
{
"title": "Request Rate",
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (service)"
}
]
},
{
"title": "Error Rate",
"targets": [
{
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) by (service)"
}
]
},
{
"title": "Response Time (P95)",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))"
}
]
}
]
}
}
📱 SaaS 애플리케이션
추천: Datadog
# Datadog APM 통합
from ddtrace import patch_all, tracer
from datadog import initialize, statsd
import logging
# 자동 계측
patch_all()
# Datadog 초기화
initialize(
api_key="YOUR_API_KEY",
app_key="YOUR_APP_KEY",
host_name="production-web-01"
)
# 커스텀 메트릭
class MetricsMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
path = environ.get('PATH_INFO', '/')
method = environ.get('REQUEST_METHOD', 'GET')
# 요청 시작
statsd.increment('web.requests', tags=[f'path:{path}', f'method:{method}'])
with tracer.trace('web.request', service='webapp', resource=path):
# 커스텀 태그
span = tracer.current_span()
if span:
span.set_tag('http.method', method)
span.set_tag('http.url', path)
span.set_tag('customer.tier', get_customer_tier())
start_time = time.time()
def custom_start_response(status, headers):
duration = (time.time() - start_time) * 1000
status_code = int(status.split()[0])
# 메트릭 전송
statsd.histogram('web.request.duration', duration,
tags=[f'path:{path}', f'method:{method}', f'status:{status_code}'])
if status_code >= 500:
statsd.increment('web.errors',
tags=[f'path:{path}', f'status:{status_code}'])
if span:
span.set_tag('http.status_code', status_code)
return start_response(status, headers)
return self.app(environ, custom_start_response)
# 비즈니스 메트릭
@tracer.wrap('business.logic')
def process_order(order_id):
try:
# 주문 처리 로직
order = get_order(order_id)
# 커스텀 메트릭
statsd.increment('orders.processed',
tags=[f'region:{order.region}', f'product:{order.product}'])
statsd.histogram('order.amount', order.total_amount,
tags=[f'currency:{order.currency}'])
# 로그 연관
logger.info(f"Processing order {order_id}",
extra={
'dd.trace_id': tracer.current_span().trace_id,
'order_id': order_id,
'amount': order.total_amount
})
process_payment(order)
update_inventory(order)
send_notification(order)
return order
except Exception as e:
statsd.increment('orders.failed',
tags=[f'error:{type(e).__name__}'])
raise
# Datadog 대시보드 설정 (Terraform)
resource "datadog_dashboard" "app_dashboard" {
title = "Application Dashboard"
widget {
timeseries_definition {
title = "Request Rate by Endpoint"
request {
q = "sum:web.requests{*} by {path}.as_rate()"
display_type = "line"
}
}
}
widget {
query_value_definition {
title = "Error Rate"
request {
q = "sum:web.errors{*}.as_rate() / sum:web.requests{*}.as_rate() * 100"
aggregator = "avg"
}
custom_unit = "%"
precision = 2
}
}
widget {
heatmap_definition {
title = "Response Time Distribution"
request {
q = "avg:web.request.duration{*} by {path}"
}
}
}
}
🔍 로그 중심 모니터링
추천: ELK Stack
# Filebeat 설정
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- add_docker_metadata:
host: "unix:///var/run/docker.sock"
- decode_json_fields:
fields: ["message"]
target: "json"
overwrite_keys: true
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
processors:
- dissect:
tokenizer: '%{clientip} - - [%{timestamp}] "%{method} %{uri} HTTP/%{version}" %{status} %{size} "%{referrer}" "%{agent}"'
field: "message"
target_prefix: "nginx"
output.elasticsearch:
hosts: ["elasticsearch:9200"]
indices:
- index: "filebeat-nginx-%{+yyyy.MM.dd}"
when.contains:
container.name: "nginx"
- index: "filebeat-app-%{+yyyy.MM.dd}"
when.contains:
container.name: "app"
# Logstash 파이프라인
input {
beats {
port => 5044
}
}
filter {
if [service] == "api" {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{DATA:logger} - %{GREEDYDATA:msg}"
}
}
if [level] == "ERROR" {
mutate {
add_tag => [ "error", "alert" ]
}
}
# 응답시간 추출
if [msg] =~ /Request completed/ {
grok {
match => {
"msg" => "Request completed: %{WORD:method} %{URIPATH:path} - %{NUMBER:duration:float}ms"
}
}
mutate {
add_field => { "metric_type" => "response_time" }
}
}
}
# GeoIP 변환
if [clientip] {
geoip {
source => "clientip"
target => "geoip"
}
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "logs-%{[@metadata][beat]}-%{+YYYY.MM.dd}"
}
# 에러 알림
if "error" in [tags] {
email {
to => "devops@company.com"
subject => "Error Alert: %{service}"
body => "Error detected:\n\nService: %{service}\nLevel: %{level}\nMessage: %{msg}\nHost: %{host}"
}
}
}
# Kibana 대시보드 설정
PUT _ingest/pipeline/parse_logs
{
"processors": [
{
"grok": {
"field": "message",
"patterns": ["%{COMBINEDAPACHELOG}"]
}
},
{
"date": {
"field": "timestamp",
"target_field": "@timestamp",
"formats": ["dd/MMM/yyyy:HH:mm:ss Z"]
}
},
{
"convert": {
"field": "response",
"type": "integer"
}
}
]
}
☁️ 풀스택 관측성
추천: New Relic
// New Relic APM 설정
require("newrelic");
const newrelic = require("newrelic");
const express = require("express");
const app = express();
// 커스텀 계측
app.use((req, res, next) => {
// 트랜잭션 이름 설정
newrelic.setTransactionName(`${req.method} ${req.route?.path || req.path}`);
// 커스텀 속성
newrelic.addCustomAttribute("customer_id", req.user?.id);
newrelic.addCustomAttribute("api_version", req.headers["api-version"]);
next();
});
// 비즈니스 메트릭
async function processPayment(order) {
return newrelic.startSegment("payment:process", true, async () => {
const startTime = Date.now();
try {
const result = await paymentGateway.charge({
amount: order.total,
currency: order.currency,
customer: order.customerId,
});
// 커스텀 이벤트
newrelic.recordCustomEvent("PaymentProcessed", {
orderId: order.id,
amount: order.total,
currency: order.currency,
gateway: "stripe",
duration: Date.now() - startTime,
success: true,
});
// 커스텀 메트릭
newrelic.recordMetric("Custom/Payment/Amount", order.total);
newrelic.recordMetric("Custom/Payment/Duration", Date.now() - startTime);
return result;
} catch (error) {
// 에러 추적
newrelic.noticeError(error, {
orderId: order.id,
amount: order.total,
gateway: "stripe",
});
newrelic.recordCustomEvent("PaymentFailed", {
orderId: order.id,
error: error.message,
errorCode: error.code,
});
throw error;
}
});
}
// Browser 모니터링
app.get("/", (req, res) => {
const browserTimingHeader = newrelic.getBrowserTimingHeader({
hasToRemoveScriptWrapper: true,
});
res.send(`
<html>
<head>
${browserTimingHeader}
<script>
// 커스텀 페이지 액션
newrelic.addPageAction('customAction', {
userId: '${req.user?.id}',
feature: 'homepage'
});
</script>
</head>
<body>
<!-- content -->
</body>
</html>
`);
});
// Synthetic 모니터링 스크립트
const syntheticScript = `
// New Relic Synthetics
$browser.get('https://app.example.com').then(function() {
return $browser.findElement($driver.By.id('login-button')).click();
}).then(function() {
return $browser.findElement($driver.By.id('username')).sendKeys($secure.USERNAME);
}).then(function() {
return $browser.findElement($driver.By.id('password')).sendKeys($secure.PASSWORD);
}).then(function() {
return $browser.findElement($driver.By.id('submit')).click();
}).then(function() {
return $browser.wait(function() {
return $browser.findElement($driver.By.className('dashboard')).isDisplayed();
}, 10000);
});
`;
🏢 실제 기업 사례
Prometheus 사용
- SoundCloud: 발명한 회사
- DigitalOcean: 인프라 모니터링
- 카카오: Kubernetes 모니터링
- 쿠팡: 마이크로서비스
Datadog 사용
- Airbnb: 전체 인프라
- Samsung: 글로벌 서비스
- Peloton: 실시간 모니터링
- 토스: 금융 서비스
New Relic 사용
- MLB: 스포츠 스트리밍
- Domino's: 주문 시스템
- Under Armour: 이커머스
- 배민: 배달 플랫폼
ELK Stack 사용
- Netflix: 로그 분석
- LinkedIn: 보안 모니터링
- 네이버: 검색 로그
- 라인: 메시징 로그
🔧 통합 및 확장
에코시스템 비교
| 도구 | 통합 수 | 주요 통합 | | -------------- | ------- | ------------------------- | | Prometheus | 100+ | Kubernetes, Docker, Cloud | | Datadog | 450+ | 모든 주요 서비스 | | New Relic | 300+ | APM, 브라우저, 모바일 | | ELK | 200+ | Beats, Logstash 플러그인 |
확장성 한계
- Prometheus: 단일 서버 한계 (페더레이션 필요)
- Datadog: 비용 증가
- New Relic: 데이터 보관 제한
- ELK: 운영 복잡도
💰 TCO 분석
50대 서버 기준 (연간)
| 항목 | Prometheus | Datadog | New Relic | ELK | | ------------- | ---------- | -------- | --------- | -------- | | 라이선스 | $0 | $9,000 | $45,000 | $0 | | 인프라 | $5,000 | $0 | $0 | $10,000 | | 운영 인력 | 1명 | 0.2명 | 0.2명 | 1명 | | 총 비용 | ~$65,000 | ~$21,000 | ~$57,000 | ~$70,000 |
🎯 선택 가이드
Prometheus + Grafana 선택 시
✅ 오픈소스 선호
✅ Kubernetes 환경
✅ 메트릭 중심
✅ 비용 민감
❌ 로그/APM 필요
❌ 운영 인력 부족
Datadog 선택 시
✅ 통합 플랫폼 원함
✅ 빠른 구축
✅ 다양한 통합
✅ 글로벌 서비스
❌ 높은 비용
❌ 데이터 주권
New Relic 선택 시
✅ APM 중심
✅ 사용자 경험 중요
✅ 풀스택 관측성
✅ 코드 레벨 분석
❌ 높은 비용
❌ 인프라 모니터링만
ELK Stack 선택 시
✅ 로그 분석 중심
✅ 유연한 검색
✅ 보안 분석
✅ 비용 통제
❌ 실시간 메트릭
❌ APM 기능
📚 추가 리소스
공식 문서
모니터링 모범 사례
- USE Method (Utilization, Saturation, Errors)
- RED Method (Rate, Errors, Duration)
- Four Golden Signals (Google SRE)
- 분산 추적 구현
한국 커뮤니티
- Prometheus 한국 사용자 모임
- Elastic 한국 사용자 그룹
- DevOps Korea
- SRE Korea
💡 핵심 조언: 모니터링은 "도구"가 아닌 "문화"입니다. 비싼 도구를 쓴다고 좋은 모니터링이 되는 것이 아닙니다. 작은 규모라면 Prometheus + Grafana로 시작하고, 성장하면서 Datadog이나 New Relic을 고려하세요. 중요한 것은 무엇을 측정하고 어떻게 대응할지 아는 것입니다.