Theme:

서비스가 잘 돌아가고 있는지 확인하는 건 당연히 해야 하는 일이다. 근데 "모니터링하고 있어요"라고 말하는 것과, 실제로 장애 원인을 5분 안에 찾아내는 건 완전히 다른 얘기다. 이 글에서는 모니터링과 옵저버빌리티의 차이부터 Prometheus, Grafana, ELK Stack, 분산 추적까지 핵심 내용을 정리했다.


모니터링 vs 옵저버빌리티

이 두 단어를 혼용하는 경우가 많은데, 엄밀히 따지면 관점 자체가 달라요.

모니터링 (Monitoring)

모니터링은 "미리 정해둔 지표를 감시하는 것" 이에요. CPU 사용률이 90% 넘으면 알람, 응답 시간이 3초 넘으면 알람. 이런 식으로 "알고 있는 문제"에 대해 감시하는 거예요.

  • 대시보드에 지표를 띄워놓고 지켜봅니다
  • 임계치를 설정해서 알람을 받습니다
  • 이미 알고 있는 장애 패턴 에 대응하기 좋습니다

옵저버빌리티 (Observability)

옵저버빌리티는 "시스템의 외부 출력을 보고 내부 상태를 추론할 수 있는 능력" 입니다. 제어 이론에서 온 개념인데, 핵심은 "처음 보는 문제도 원인을 파악할 수 있느냐" 에 있어요.

모니터링이 "알려진 unknowns"에 대한 대응이라면, 옵저버빌리티는 "알려지지 않은 unknowns"까지 다뤄요. 예를 들어 특정 사용자만 느린 현상이 생겼는데 CPU도 정상, 메모리도 정상이라면? 모니터링만으로는 원인을 못 찾습니다. 메트릭, 로그, 트레이스를 조합해서 "왜 이 사용자만 느린지"를 추적해야 하는데, 이게 옵저버빌리티의 영역이에요.

PLAINTEXT
모니터링: "CPU 90% 넘었다!" → 알람 발생
옵저버빌리티: "왜 이 API만 느리지?" → 메트릭 + 로그 + 트레이스 조합 → 원인 파악

핵심만 정리하면 모니터링은 옵저버빌리티의 부분집합 이고, 옵저버빌리티를 확보하려면 다양한 텔레메트리 데이터를 수집하고 상관 분석할 수 있어야 합니다.


옵저버빌리티의 3가지 축

옵저버빌리티를 구성하는 세 가지 축은 메트릭(Metrics), ** 로그(Logs)**, ** 트레이스(Traces)**예요. 이걸 "텔레메트리의 세 기둥(Three Pillars of Observability)"이라고도 부릅니다.

메트릭 (Metrics)

시간에 따라 변하는 ** 숫자 데이터 **예요. 집계와 비교에 최적화되어 있고, 저장 비용이 상대적으로 낮습니다.

  • CPU 사용률, 메모리 사용량, 디스크 I/O
  • HTTP 요청 수, 에러율, 응답 시간(p50, p95, p99)
  • JVM 힙 사용량, GC 횟수, 스레드 수

메트릭의 핵심 특징은 ** 차원(dimension)**입니다. 단순히 "요청 수"가 아니라, method=GET, path=/api/users, status=200 같은 레이블을 붙여서 다차원으로 조회할 수 있어요. 이걸 ** 다차원 시계열 데이터 **라 부릅니다.

PLAINTEXT
http_requests_total{method="GET", path="/api/users", status="200"} 1523
http_requests_total{method="POST", path="/api/users", status="201"} 87
http_requests_total{method="GET", path="/api/users", status="500"} 3

로그 (Logs)

특정 시점에 발생한 ** 이벤트에 대한 텍스트 기록 **이에요. 가장 직관적이고 디버깅할 때 제일 먼저 보게 되는 데이터인데, 양이 많고 구조화가 안 되어 있으면 분석이 어렵습니다.

  • 애플리케이션 로그: ERROR - NullPointerException at UserService.java:42
  • 액세스 로그: 192.168.1.1 - GET /api/users 200 32ms
  • 시스템 로그: OOM Killer invoked, killing process 1234

최근에는 JSON 형태의 ** 구조화된 로그(Structured Logging)**가 표준처럼 쓰이고 있어요. 평문 로그는 사람이 읽기엔 좋지만 기계가 파싱하기 어렵기 때문입니다.

JSON
{
  "timestamp": "2026-03-15T10:30:00Z",
  "level": "ERROR",
  "service": "user-service",
  "traceId": "abc123",
  "message": "Failed to fetch user",
  "userId": 42,
  "error": "ConnectionTimeout"
}

트레이스 (Traces)

하나의 요청이 여러 서비스를 거치면서 ** 어디서 얼마나 시간이 걸렸는지** 추적하는 데이터예요. MSA 환경에서 특히 중요합니다. 단일 서버라면 로그만 봐도 되지만, 요청 하나가 API Gateway → User Service → Order Service → Payment Service를 거치는 구조에서는 로그만으로 병목을 찾기가 정말 어려워요.

PLAINTEXT
[Trace ID: abc-123]
├─ API Gateway          2ms
├─ User Service        15ms
├─ Order Service       45ms  ← 여기가 느리다
│  ├─ DB Query         30ms  ← 원인: 느린 쿼리
│  └─ Cache Lookup      5ms
└─ Payment Service     10ms
Total: 72ms

트레이스는 Trace ID 와 Span ID 로 구성됩니다. Trace ID는 전체 요청을 하나로 묶는 식별자이고, Span은 각 서비스에서의 작업 단위예요. 각 Span은 부모 Span을 참조해서 트리 구조를 이룹니다.


Prometheus

Prometheus는 SoundCloud에서 만들고 CNCF에서 관리하는 오픈소스 모니터링 시스템 입니다. K8s와 함께 CNCF에서 두 번째로 졸업한 프로젝트이기도 해요. 시계열 메트릭 수집에 특화되어 있고, 클라우드 네이티브 환경에서 사실상 표준으로 자리 잡았습니다.

Pull 기반 수집

대부분의 모니터링 시스템이 **Push 방식 **(에이전트가 메트릭을 서버로 보냄)인 반면, Prometheus는 Pull 방식 이에요. Prometheus 서버가 주기적으로 각 타겟의 /metrics 엔드포인트에 HTTP 요청을 보내서 메트릭을 긁어옵니다.

PLAINTEXT
┌─────────────┐     scrape      ┌──────────────┐
│  Prometheus │ ───────────────→ │  App /metrics│
│   Server    │  (HTTP GET)     │              │
└─────────────┘                 └──────────────┘

       │  scrape

┌──────────────┐
│  Node        │
│  Exporter    │
└──────────────┘

Pull 방식의 장점이 뭐냐면,

  • 모니터링 대상이 Prometheus의 존재를 몰라도 됩니다. 그냥 /metrics 엔드포인트만 열어두면 끝이에요
  • 타겟이 죽으면 scrape가 실패하니까, 업/다운 상태를 자동으로 감지 할 수 있어요
  • 방화벽 설정이 단순합니다. Prometheus에서 나가는 방향만 열면 돼요

물론 Push가 필요한 경우도 있어요. 배치 잡처럼 수명이 짧은 프로세스는 Prometheus가 scrape하기 전에 끝나버릴 수 있으니까, 이때는 Pushgateway 를 중간에 두고 메트릭을 밀어넣습니다.

시계열 DB (TSDB)

Prometheus는 자체 시계열 데이터베이스를 내장하고 있어요. 시계열 데이터의 특성(시간순 쓰기, 최근 데이터 위주 조회, 오래된 데이터는 집계)에 맞춰 최적화되어 있습니다.

기본적으로 로컬 디스크에 저장하고, 보존 기간은 기본 15일이에요. 장기 보존이 필요하면 Thanos 나 Cortex 같은 별도 솔루션을 붙여서 원격 스토리지에 저장합니다.

PromQL

Prometheus 전용 쿼리 언어예요. SQL과는 문법이 완전히 다른데, 시계열 데이터를 다루는 데 특화되어 있어서 익숙해지면 꽤 강력합니다.

PROMQL
# 5분간 HTTP 요청의 초당 비율
rate(http_requests_total[5m])

# 서비스별 에러율
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/ sum(rate(http_requests_total[5m])) by (service)

# 응답 시간 p95
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

PromQL 문법을 전부 외울 필요는 없지만, rate(), sum(), histogram_quantile() 같은 핵심 함수가 뭘 하는지 정도는 알아두는 게 좋습니다.

메트릭 타입

Prometheus가 지원하는 메트릭 타입은 4가지예요.

타입설명예시
Counter단조 증가하는 누적값요청 수, 에러 수
Gauge올라가기도 내려가기도 하는 값CPU 사용률, 메모리
Histogram값의 분포를 버킷으로 관측응답 시간 분포
Summary클라이언트에서 분위수 계산응답 시간 p50/p99

Histogram과 Summary의 차이는 자주 헷갈리는 부분이에요. Histogram은 서버 사이드에서 집계 가능하고(여러 인스턴스의 데이터를 합칠 수 있음), Summary는 클라이언트에서 이미 계산해버려서 집계가 안 됩니다. 그래서 보통 Histogram을 권장 해요.

Alert Manager

Prometheus 자체는 메트릭 수집과 저장만 하고, 알람은 Alert Manager가 담당 합니다. Prometheus에서 알람 규칙을 정의하면, 조건이 충족됐을 때 Alert Manager로 알림을 보내고, Alert Manager가 Slack, PagerDuty, 이메일 등으로 라우팅해요.

YAML
# prometheus alert rule 예시
groups:
  - name: example
    rules:
      - alert: HighErrorRate
        expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "에러율이 5%를 초과했습니다"

for: 5m은 "5분 동안 지속되면 발생"이라는 뜻이에요. 순간적인 스파이크에 알람이 울리면 피로감만 쌓이니까, 이 값을 적절히 조절하는 게 중요합니다.


Grafana

Grafana는 시각화 도구 예요. 그 자체로는 데이터를 수집하거나 저장하지 않습니다. 다양한 데이터소스에 연결해서 대시보드를 만드는 역할이에요.

데이터소스 다양성

Grafana의 가장 큰 강점은 데이터소스를 가리지 않는다는 거예요.

  • Prometheus (가장 많이 쓰는 조합)
  • Elasticsearch
  • InfluxDB, Loki, Tempo
  • MySQL, PostgreSQL
  • CloudWatch, Datadog
  • 그 외 수십 가지 플러그인

이게 왜 강점이냐면, 메트릭은 Prometheus에서, 로그는 Loki에서, 트레이스는 Tempo에서 가져와서 하나의 대시보드에 같이 볼 수 있기 때문 이에요. 장애 상황에서 여러 도구를 왔다 갔다 할 필요가 없습니다.

실무 대시보드 구성

실무에서 Grafana 대시보드는 보통 이런 계층으로 구성해요.

PLAINTEXT
1단계: 전체 서비스 개요 (Overview)
  ├─ 서비스별 에러율, 요청량, 응답 시간
  ├─ 인프라 리소스 현황 (CPU, Memory, Disk)
  └─ 전체 SLO 달성 현황

2단계: 개별 서비스 상세
  ├─ 엔드포인트별 응답 시간 (p50, p95, p99)
  ├─ 에러 로그 패턴
  └─ 의존성 서비스 호출 현황

3단계: 인프라 상세
  ├─ Node Exporter 메트릭 (CPU, Memory, Network, Disk)
  ├─ K8s Pod 상태, 리소스 사용량
  └─ DB 커넥션 풀, 슬로우 쿼리

대시보드를 만들 때 흔히 하는 실수가 있어요. 지표를 너무 많이 넣는 것. 한 화면에 패널이 30개씩 있으면 아무도 안 봅니다. **RED 메서드 **(Rate, Errors, Duration)나 **USE 메서드 **(Utilization, Saturation, Errors) 같은 프레임워크를 기준으로 핵심 지표만 넣는 게 좋아요.


ELK Stack

ELK는 Elasticsearch + Logstash + Kibana 의 조합이에요. 로그 수집, 저장, 시각화를 하나의 스택으로 해결하는 솔루션이고, Elastic 사에서 만들었습니다.

각 구성 요소

Elasticsearch 는 분산 검색 엔진입니다. Apache Lucene 기반이고, JSON 문서를 저장하고 전문 검색(Full-text search)을 할 수 있어요. 로그를 단순히 저장하는 게 아니라 인덱싱해서 빠르게 검색 할 수 있다는 게 핵심이에요. "최근 1시간 동안 user-service에서 발생한 NullPointerException" 같은 검색이 초 단위로 가능합니다.

Logstash 는 데이터 수집 파이프라인이에요. 다양한 소스에서 데이터를 받아서, 필터링/변환하고, 목적지로 보내는 ETL 역할을 합니다.

PLAINTEXT
Input (수집)          Filter (가공)         Output (전송)
─────────────       ──────────────       ──────────────
파일, Kafka,    →   grok, mutate,    →   Elasticsearch,
syslog, beats       date, geoip         S3, Kafka

Kibana 는 Elasticsearch 전용 시각화 도구예요. 로그 검색, 대시보드, 알림 설정 등을 UI로 제공합니다. Grafana와 역할이 비슷하지만, Elasticsearch와의 궁합이 당연히 가장 좋아요.

로그 수집 파이프라인

실무에서 로그 수집 파이프라인은 보통 이런 흐름을 따릅니다.

PLAINTEXT
App → Filebeat → Kafka(버퍼) → Logstash → Elasticsearch → Kibana

왜 중간에 Kafka를 넣느냐면, Logstash나 Elasticsearch에 장애가 발생해도 로그가 유실되지 않게 버퍼 역할을 하기 위해서예요. 트래픽이 갑자기 몰릴 때 Elasticsearch가 인덱싱 속도를 못 따라가면 Kafka에 쌓아뒀다가 천천히 처리할 수도 있습니다.


Filebeat

Logstash가 강력하긴 한데, 문제는 ** 무겁다 **는 거예요. JVM 기반이라 메모리를 많이 먹습니다. 각 서버마다 Logstash를 설치하는 건 리소스 낭비가 심해요.

그래서 나온 게 Filebeat 예요. Go로 작성된 경량 로그 수집기로, 파일 변경을 감지해서 로그를 수집하고 전송하는 일만 합니다. 각 서버에는 Filebeat를 설치하고, 중앙에 Logstash를 두는 구조가 일반적이에요.

PLAINTEXT
서버 1: App → Filebeat ──┐
서버 2: App → Filebeat ──┼──→ Logstash → Elasticsearch
서버 3: App → Filebeat ──┘

EFK vs ELK

Logstash 대신 Fluentd 를 쓰는 조합을 EFK라고 부릅니다. Fluentd도 CNCF 졸업 프로젝트인데, K8s 환경에서는 EFK가 더 많이 쓰여요.

비교 항목LogstashFluentd
언어JRuby (JVM)C + Ruby
메모리많이 먹음 (수백 MB)적게 먹음 (수십 MB)
플러그인Elastic 중심다양한 출력 지원
K8s 통합별도 설정 필요DaemonSet으로 깔끔
버퍼링메모리 기반메모리 + 파일

K8s 환경에서 Fluentd가 인기 있는 이유는, DaemonSet으로 각 노드에 하나씩 배포하면 해당 노드의 모든 Pod 로그를 자동으로 수집하기 때문이에요. 별도 설정 없이도 K8s 메타데이터(Pod 이름, 네임스페이스 등)를 자동으로 붙여줍니다.


분산 추적 (Distributed Tracing)

MSA 환경에서 하나의 API 호출이 내부적으로 10개 서비스를 거칠 수 있어요. 이때 "어디서 병목이 생겼는지"를 추적하는 게 분산 추적입니다.

Trace ID와 Span ID

PLAINTEXT
사용자 요청 → API Gateway → Auth Service → User Service → DB

                 └→ Trace ID: "abc-123" (전체 요청 식별)

각 서비스에서의 처리 = Span
  Span 1: API Gateway (span_id: "s1", parent: null)
  Span 2: Auth Service (span_id: "s2", parent: "s1")
  Span 3: User Service (span_id: "s3", parent: "s1")
  Span 4: DB Query    (span_id: "s4", parent: "s3")

Trace ID는 요청이 시작될 때 생성되고, 서비스 간 호출할 때 HTTP 헤더(예: X-B3-TraceId)나 gRPC 메타데이터로 전파돼요. 이걸 Context Propagation 이라고 합니다.

주요 도구

Jaeger 는 Uber에서 만든 분산 추적 시스템이에요. CNCF 졸업 프로젝트이고, K8s 환경에서 많이 쓰입니다. UI가 깔끔해서 트레이스를 시각적으로 분석하기 편해요.

Zipkin 은 Twitter에서 만들었어요. Google Dapper 논문을 기반으로 한 초기 분산 추적 도구입니다. Jaeger보다 먼저 나왔고, 자바 생태계에서 특히 호환성이 좋아요.

OpenTelemetry (OTel) 는 요즘 가장 주목할 만한 프로젝트예요. CNCF에서 밀고 있는 벤더 중립적인 텔레메트리 표준 인데, 이전에 따로 있던 OpenTracing과 OpenCensus를 합친 것입니다. 메트릭, 로그, 트레이스를 하나의 SDK로 수집하고, 백엔드는 Jaeger든 Zipkin이든 Datadog이든 자유롭게 바꿀 수 있어요.

PLAINTEXT
App (OTel SDK) → OTel Collector → Jaeger (트레이스)
                                → Prometheus (메트릭)
                                → Loki (로그)

OpenTelemetry를 쓰면 특정 벤더에 종속되지 않으니까, 나중에 모니터링 도구를 바꿔도 애플리케이션 코드를 수정할 필요가 없어요. 이게 OTel의 핵심 가치입니다.


Spring Boot Actuator와 Prometheus 연동

Spring Boot로 개발하고 있다면 모니터링 연동이 상당히 쉬운 편이에요.

Actuator

Spring Boot Actuator는 애플리케이션의 상태 정보와 메트릭을 HTTP 엔드포인트로 노출 하는 모듈이에요.

YAML
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health, info, metrics, prometheus
  endpoint:
    health:
      show-details: always

/actuator/health로 헬스 체크를 하고, /actuator/metrics로 각종 메트릭을 확인할 수 있어요. 근데 이 메트릭은 Actuator 자체 포맷이라 Prometheus가 바로 읽지 못합니다.

Micrometer

여기서 Micrometer 가 등장합니다. Micrometer는 "메트릭의 SLF4J"라고 불리는데, 다양한 모니터링 시스템에 대한 추상화 레이어 예요. SLF4J가 로깅 구현체(Logback, Log4j)를 추상화하듯이, Micrometer는 모니터링 구현체(Prometheus, Datadog, CloudWatch)를 추상화합니다.

JAVA
// 커스텀 메트릭 등록 예시
@Component
public class OrderMetrics {
    private final Counter orderCounter;
    private final Timer orderTimer;

    public OrderMetrics(MeterRegistry registry) {
        this.orderCounter = Counter.builder("orders.created")
            .tag("type", "online")
            .description("주문 생성 횟수")
            .register(registry);

        this.orderTimer = Timer.builder("orders.processing.time")
            .description("주문 처리 시간")
            .register(registry);
    }
}

Prometheus 연동

micrometer-registry-prometheus 의존성을 추가하면 /actuator/prometheus 엔드포인트가 열리고, Prometheus가 이해하는 포맷으로 메트릭이 노출돼요.

PLAINTEXT
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="G1 Eden Space"} 2.5165824E7
jvm_memory_used_bytes{area="heap",id="G1 Old Gen"} 1.8874368E7

Prometheus scrape_configs에 해당 애플리케이션을 등록하면 끝입니다. 이 구조를 그림으로 그리면 이래요.

PLAINTEXT
Spring Boot App
  └─ Actuator + Micrometer
       └─ /actuator/prometheus  ←── Prometheus (scrape)
                                        └─── Grafana (시각화)

SLI / SLO / SLA

모니터링 지표를 아무리 많이 수집해도, "우리 서비스가 잘 돌아가고 있다"를 판단하는 기준이 없으면 의미가 없어요. 그 기준을 정하는 프레임워크가 SLI/SLO/SLA입니다. Google SRE 팀이 체계화한 개념이에요.

SLI (Service Level Indicator)

서비스 수준을 측정하는 구체적인 지표 예요. 숫자로 표현되어야 합니다.

  • 가용성: 전체 요청 중 성공한 요청의 비율
  • 지연 시간: 요청의 p99 응답 시간
  • 처리량: 초당 처리할 수 있는 요청 수
  • 에러율: 전체 요청 중 5xx 응답 비율

SLO (Service Level Objective)

SLI에 대한 목표값 이에요. "가용성 99.9%", "p99 응답 시간 500ms 이하" 같은 것입니다.

PLAINTEXT
SLI: 가용성 → SLO: 99.95%
SLI: p99 레이턴시 → SLO: 300ms 이하

SLO를 정할 때 중요한 건, 100%를 목표로 잡으면 안 된다는 거예요. 99.99%와 100%의 차이를 달성하려면 비용이 기하급수적으로 올라갑니다. 그리고 SLO에는 에러 예산(Error Budget) 이라는 개념이 따라와요. 99.9% SLO라면 한 달에 약 43분의 다운타임이 허용된다는 뜻이에요. 이 에러 예산이 남아있으면 적극적으로 배포하고, 소진되면 안정성에 집중하는 식으로 운영합니다.

SLA (Service Level Agreement)

SLO에 법적/계약적 구속력 을 붙인 거예요. "가용성 99.9%를 보장하고, 이를 어기면 크레딧을 돌려드립니다" 같은 게 SLA입니다. AWS, GCP 같은 클라우드 서비스들이 제공하는 가용성 SLA가 대표적인 예시예요.

SLA는 항상 SLO보다 느슨하게 잡아요. 내부적으로 99.99%를 목표로 하되, 고객에게는 99.9%를 약속하는 식입니다. SLO를 못 지키면 팀 내부 문제지만, SLA를 못 지키면 돈이 나갑니다.


알람 전략

모니터링 시스템을 잘 구축해놓고도, 알람 전략을 잘못 짜면 의미가 없어져요. 알람이 너무 많으면 알람 피로(Alert Fatigue) 가 오고, 결국 중요한 알람도 무시하게 됩니다.

알람 피로를 줄이려면

  • **증상 기반 알람 **: 원인이 아니라 증상에 알람을 걸어요. "디스크 I/O가 높다"가 아니라 "사용자 응답 시간이 느려졌다"에 알람을 겁니다
  • ** 적절한 임계치 **: 순간적인 스파이크에 반응하지 않도록 for 기간을 둡니다
  • ** 심각도 분류 **: critical(즉시 대응), warning(업무 시간 대응), info(참고용)
  • ** 그룹핑과 디듀플리케이션 **: 같은 원인의 알람 100개가 동시에 오면 하나로 묶어요
  • ** 런북(Runbook) 연결 **: 알람에 "이 알람이 왔을 때 뭘 해야 하는지" 문서를 링크합니다

의미 있는 알람 설계

핵심은 "이 알람이 울렸을 때 누군가 행동을 취해야 하는가?" 를 기준으로 설계하는 거예요. 행동이 필요 없는 알람은 대시보드에서 모니터링하면 충분합니다.

PLAINTEXT
좋은 알람:
  "주문 서비스 에러율이 5분간 5%를 초과했습니다"
  → 원인 파악 후 롤백 또는 핫픽스 필요

나쁜 알람:
  "CPU 사용률이 80%를 넘었습니다"
  → 80%가 정상일 수도 있다. 서비스에 영향이 있는지가 핵심이다

주의할 점

APM이란?

APM(Application Performance Monitoring)은 애플리케이션 레벨의 성능을 모니터링하는 도구예요. 메트릭, 트레이스, 프로파일링을 통합해서 제공합니다. 대표적으로 Datadog APM, New Relic, Elastic APM이 있어요. Prometheus + Grafana가 인프라 모니터링에 가깝다면, APM은 애플리케이션 코드 레벨의 성능 분석에 초점을 맞춥니다. 느린 SQL 쿼리, N+1 문제, 메서드별 실행 시간 같은 걸 잡아내요.

로그 레벨 전략

로그 레벨은 TRACE < DEBUG < INFO < WARN < ERROR < FATAL 순이에요. 실무에서 중요한 건 "어떤 상황에 어떤 레벨을 쓸 것인가"를 팀에서 합의하는 것입니다.

  • ERROR: 요청 실패, 데이터 정합성 문제 등 즉시 확인이 필요한 것
  • WARN: 당장 문제는 아니지만 주의가 필요한 것 (재시도 성공, 폴백 동작 등)
  • INFO: 비즈니스 흐름 추적 (주문 생성, 결제 완료 등)
  • DEBUG: 개발 중 디버깅용. 운영에서는 보통 꺼둔다

운영 환경에서 DEBUG를 켜면 로그 양이 폭발하니까, 특정 모듈만 일시적으로 DEBUG로 올릴 수 있는 ** 동적 로그 레벨 변경** 기능이 중요해요. Spring Boot에서는 Actuator의 /actuator/loggers 엔드포인트로 런타임에 변경할 수 있습니다.

Prometheus vs Datadog

Prometheus와 Datadog은 뭐가 다를까요?

비교PrometheusDatadog
비용오픈소스 (무료)SaaS (호스트당 과금)
운영직접 운영해야 함관리형 서비스
기능메트릭 특화메트릭 + 로그 + 트레이스 + APM 통합
장기 저장별도 구성 필요 (Thanos 등)기본 제공
커스터마이징자유도 높음제한적
K8s 통합네이티브에이전트 설치 필요

핵심만 정리하면, 비용 여유가 있고 운영 부담을 줄이고 싶으면 Datadog, 비용을 아끼고 커스터마이징이 필요하면 Prometheus예요. 실제로 스타트업 초기에는 Prometheus + Grafana로 시작하고, 규모가 커지면 Datadog을 도입하는 경우도 많습니다.

카디널리티 폭발 (Cardinality Explosion)

메트릭에 레이블을 붙일 때 조심해야 하는 부분이에요. 레이블 값의 조합이 시계열 하나를 만드는데, 레이블 값이 너무 다양하면 시계열 수가 폭발적으로 증가합니다.

PLAINTEXT
# 나쁜 예: user_id를 레이블로 쓰면 사용자 수만큼 시계열이 생긴다
http_requests_total{user_id="12345"} → 사용자 100만 명 = 시계열 100만 개

# 좋은 예: 카디널리티가 낮은 레이블만 사용
http_requests_total{method="GET", status="200", service="user-api"}

Prometheus TSDB가 메모리에 최근 시계열을 들고 있기 때문에, 카디널리티가 폭발하면 Prometheus 자체가 OOM으로 죽을 수 있어요. user_id, request_id, session_id 같은 고유값은 절대 레이블로 쓰면 안 됩니다. 이런 건 로그에 남기는 게 맞아요.


파생 개념

이 글에서 다룬 내용과 연결되는 주제들이에요.

댓글 로딩 중...