Grafana 입문 8편 — 운영 함정 + 사고 케이스 깊이

2026-05-18Grafana 입문에서 운영까지

Grafana 입문 8편. 운영 함정 + 사고 케이스 깊이 — Cardinality 폭발 의 디버그 (TSDB · series 수 모니터 · drop label), Cost 폭증 (Cloud · S3 · Mimir 의 비용 함정), Plugin 의 보안 사고 (CVE · supply chain), DDoS · 광범위 사고 의 alert 폭주, Configuration drift 의 회복, Backup · DR 의 시험과 실패 case, Multi-region 의 latency · clock skew, Compliance · Audit 사고 (GDPR · PIPA), Vendor lock-in 의 회피. 운영 1년 이상 의 자리.

📚 Grafana 입문에서 운영까지 · 8편 — 운영 함정 + 사고 케이스 깊이

이 글은 Grafana 입문에서 운영까지 시리즈 8편이에요. 1~7편이 기능의 이해 였다면 8편은 운영 1년 이상이 쌓아 올린 무거운 사고 자리예요.

이번 글의 범위

운영자에게 처음엔 안 보이고 서서히 발견되며 한 번 발견하면 잊혀지지 않는 사고들의 모음이에요.

영역 사고
Data Cardinality 폭발 · 비용 폭증
Security Plugin CVE · Audit 누락
Reliability Alert 폭주 · Config drift · DR 실패
Scale Multi-region · Clock skew
Governance Compliance · Vendor lock-in

Cardinality 폭발 — 가장 큰 함정

Cardinality 의 의미 (재정리)

Cardinality(label 조합으로 만들어지는 고유 time series 개수)는 메트릭 시스템의 메모리·비용을 좌우하는 핵심 지표예요.

Time Series 의 개수 = label combination 의 unique 수

예 — http_requests_total:
  {method, status, region, service, path}

  method: 5 종 (GET, POST, PUT, DELETE, PATCH)
  status: 30 종 (200, 201, 400, 401, ...)
  region: 3 종 (us, eu, asia)
  service: 20 종
  path: ???

  → 5 × 30 × 3 × 20 × <path> = 9000 × <path>

  path 가 100 종 → 900,000 series
  path 가 dynamic (/products/12345 같은) → 무한

Cardinality 폭발 의 증상

- Prometheus / Mimir 의 메모리 폭증
- Query 의 timeout
- Loki 의 stream 수 증가 → injest 느림
- Tempo 의 indexing 느림
- Grafana dashboard 의 로딩 매우 느림

디버그 — Series 수 확인

# Prometheus / Mimir 의 series 수
prometheus_tsdb_head_series

# Top 10 high-cardinality metric
topk(10,
  count by (__name__) ({__name__=~".+"})
)

# 특정 metric 의 label 별 cardinality
count(count by (label_name) (metric_name))

# 모든 label 의 cardinality
topk(10,
  count by (label_name) (
    {__name__="metric_name"}
  )
)

Cardinality 회피

1. Label 의식
   동적 값 (user_id · request_id · UUID) = label X
   normalized 한 값 ("/products/:id") 만 label

2. Drop label (Prometheus relabel)
   relabel_configs:
     - source_labels: [__name__]
       regex: 'noisy_metric_.*'
       action: drop

3. Metric Aggregation
   raw metric → Recording Rule 의 aggregated metric
   원본 series 는 short retention

4. Histogram 의 bucket 의식
   너무 많은 bucket = cardinality 폭증
   필요 bucket 만 (10 ~ 15 권장)

사고 케이스 — Path Cardinality

사고: e-commerce 의 product page 의 path 가 그대로 label
  /products/12345 · /products/12346 · ...

10만 상품 → 10만 unique path label
→ http_requests_total 의 series 수 폭증
→ Prometheus OOM

해결:
  Application 의 routing 의식:
    GET /products/:id  → handler name = "GetProduct"

  Instrumentation 의 path 정규화:
    span.set_attribute("http.route", "/products/:id")
    label 박을 때 path 아닌 route 사용

Cost 폭증

Grafana Cloud Cost

Free Tier 의 한도 초과 시:
  - Metrics: 10k → 20k = +$8/월
  - Logs: 50GB → 100GB = +$25/월
  - 갑자기 트래픽 폭증 = 큰 청구

Mimir 의 Cost

저장:
  - 1 active series ≈ 8 bytes (메모리) × N hours retention
  - 1억 series × 7일 = 큰 비용

  - S3 chunk storage: $0.023 / GB / 월
  - 1 TB 의 metric data = $23 / 월

Compute:
  - Mimir 의 인스턴스 (Ingester · Querier · Compactor)
  - 메모리 의 크기 = active series × bytes

S3 비용 함정

사고 case:
  - Loki 의 chunk 가 S3 에 저장
  - 너무 많은 작은 chunk → S3 의 *PUT request 비용* 폭증
  - GET 의 비용 도 누적

해결:
  - chunk 크기 의 충분 (1.5 MB · 2 hour 권장)
  - S3 의 Intelligent Tiering
  - 오래된 chunk 의 Glacier 의 자동 transition

Query 비용 (BigQuery 와 비교)

BQ 가 datasource 시:
  - Grafana panel 의 한 query = BQ scan
  - Dashboard 의 자동 refresh = 매번 scan
  - 30초 마다 refresh × 10 panel = 30분에 600 query

  → 작은 dashboard 도 큰 비용

해결:
  - Materialized view (6편)
  - Query result cache (Grafana 의 query caching)
  - Default refresh = 5분 (자주 refresh X)

비용 모니터 자동

# 매일 자동 cost 의 audit
def daily_cost_audit():
    # Grafana Cloud
    cloud_usage = get_grafana_cloud_usage()

    # AWS
    s3_cost = get_aws_cost_explorer("s3")
    ec2_cost = get_aws_cost_explorer("ec2")

    # 비용 보고
    daily_total = cloud_usage['cost'] + s3_cost + ec2_cost

    if daily_total > DAILY_BUDGET:
        slack_alert(
            f"⚠️ Daily observability cost: ${daily_total:.2f} "
            f"> budget ${DAILY_BUDGET:.2f}"
        )

    # 항상 Slack 보고
    post_to_slack("#cost-monitor", format_cost_report(...))

Plugin 의 보안 사고

CVE 의 위험

CVE(공개된 보안 취약점 일련번호)는 plugin 채택 기준의 1순위 지표예요.

Grafana Plugin 의 CVE 사례:
  - Grafana 9 의 path traversal CVE
  - Image renderer 의 SSRF
  - Authentication bypass
  - XSS · SQL injection

영향:
  - 인증 우회 → 모든 dashboard 접근
  - SSRF → 내부 네트워크 의 정보 누출
  - Cookie · token 의 탈취

Plugin 의 의식

공식 Grafana plugin:
  - Grafana Labs 의 maintain
  - CVE 패치 빠름
  - 보안 audit 받음

Community plugin:
  - 누구나 등록 가능
  - maintain 끊김 가능
  - CVE 가 발견 안 됨

Enterprise plugin:
  - Grafana Labs 의 maintain
  - SLA 있음

회피 패턴

1. Plugin upgrade 의 자동 알림
   - GitHub watch 또는 RSS

2. 주기적 CVE review
   - 매월 모든 plugin 의 CVE 확인
   - CVE 발견 → 즉시 patch

3. Plugin 의 origin 의식
   - Community plugin = 신중
   - 가능 시 alternative · 직접 개발

4. Production 의 plugin 제한
   - 표준 plugin 만 허용
   - 새 plugin = security review 후

Supply Chain Attack

사례:
  - npm package 의 hijack
  - Plugin 의 malicious update
  - "신뢰 의 사슬" 의 약한 link

회피:
  - Plugin signing 의 검증
  - 알려진 hash 의 검증
  - Production 의 air-gapped 옵션

Alert 폭주 — 광범위 사고

사고 case: AWS Region Down

AWS us-east-1 의 outage:
  → 1000 instance 모두 down
  → 1000 alert 동시 fire
  → Slack 의 1000 메시지
  → PagerDuty 의 1000 page
  → oncall 의 무력감

원인 분석 늦음 → 진짜 사고 의 대응 늦음

회피 — Inhibition 설계

Inhibition(상위 alert가 켜지면 하위 alert를 자동으로 잠재우는 규칙)이 폭주를 잡는 표준이에요.

# Inhibition 의 표준
inhibit_rules:
  # Region 전체 down → 자식 alert mask
  - source_match:
      alertname: AwsRegionDown
    target_match_re:
      alertname: ".*"
    equal: [region]

  # Cluster down → 그 cluster 의 모든 alert mask
  - source_match:
      alertname: ClusterDown
    target_match_re:
      alertname: ".*"
    equal: [cluster]

  # Service down → 그 service 의 latency · error alert mask
  - source_match:
      alertname: ServiceDown
    target_match_re:
      alertname: "Service(Latency|ErrorRate)"
    equal: [service]

회피 — Aggregation Alert

# 단일 instance alert X
# Service 전체 의 aggregate alert O

# 나쁨: 각 instance 의 alert (100 instance = 100 alert)
- alert: InstanceHighCpu
  expr: cpu_usage > 80
  by: (instance)

# 좋음: service 전체 의 percentage alert (1 alert)
- alert: ServiceHighCpuRatio
  expr: |
    (
      count(cpu_usage > 80) by (service) /
      count(cpu_usage) by (service)
    ) > 0.5

Configuration Drift

Drift 의 의미

Drift(의도된 설정과 실제 시스템 상태의 어긋남)는 운영의 잠재적 가장 큰 미스터리원이에요.

의도된 config:
  - Git 의 Terraform · Provisioning 의 file
  - "이게 우리 의 설정"

실제 시스템:
  - UI 의 수동 변경
  - 긴급 시 의 임시 수정
  - 사람 마다 다른 변경

→ Git ≠ 실제 → drift

Drift 의 위험

- 재해 복구 시 git 의 config 으로 복원 → 실제 와 다름
- Audit 시 의 의도 안 한 변경 발견
- 사고 시 의 "왜 이 alert 박혔지?" 의 미스터리
- 사람 의 의식 분산

회피 — editable: false

# Provisioning config
datasources:
  - name: Prometheus
    editable: false       ← UI 에서 수정 시도 = error

dashboards:
  - name: 'company-dashboards'
    editable: false

회피 — Drift Detection

# 주기적 git diff (CI/CD job)
def detect_drift():
    # Grafana 의 현재 dashboard 추출
    actual_dashboards = grafana_api.list_dashboards()

    # Git 의 dashboard
    git_dashboards = load_dashboards_from_git()

    # 비교
    diff = compare(actual_dashboards, git_dashboards)

    if diff.has_changes:
        slack_alert(
            f"⚠️ Drift detected: {len(diff.changes)} 변경\n"
            f"{format_diff(diff)}"
        )

        # 자동 복원 (선택)
        if AUTO_REVERT:
            grafana_api.apply(git_dashboards)

매시간 cron으로 drift를 자동 감지해요.

Backup · DR 의 실패

DR(Disaster Recovery, 재해 복구)은 backup만으로 끝나지 않아요. 시험까지가 한 세트예요.

Backup 만 하고 시험 안 함

일반 case:
  - 매일 backup 자동 (S3 의 daily)
  - 1년 동안 backup 안 시험
  - 사고 때 복원 시도 → 실패 (backup 손상 · format 변경 · 권한 부족)

→ Backup 있어도 의미 없음

DR 의 주기적 시험

매 분기:
  □ Backup 의 download
  □ Test environment 에 restore
  □ Dashboard 의 query 검증
  □ Alert rule 의 검증
  □ User · Team 권한 검증
  □ 시험 결과 docs

매년:
  □ 실 사고 시뮬레이션 (1 시간 의 RTO)
  □ Tabletop exercise (모의 사고)
  □ Runbook 의 review

사고 case: 잘못된 Backup

사고:
  - Backup script 가 dashboard 만 백업
  - Alert · Notification Policy · Contact Point 백업 X
  - 1 년 후 사고 시 복원 → alert 모두 손실
  - 새벽 의 alert 안 옴 → 사용자 영향 의 늦은 감지

해결:
  - 전체 export 의 backup (Terraform plan 기반)
  - 또는 Grafana 의 Migration API 활용

RTO · RPO

RTO (Recovery Time Objective):
  사고 후 시스템 복원 까지 의 시간 목표
  예: 4 시간 (4h 안에 모든 monitoring 복원)

RPO (Recovery Point Objective):
  허용 가능 한 데이터 손실 시간
  예: 24 시간 (마지막 백업 후 24h 의 변경 손실 허용)

설정 의 design:
  RPO 가 짧으면 → backup 의 빈도 높여야 (비용 큼)
  RTO 가 짧으면 → hot standby · multi-region

Multi-region · Clock Skew

Multi-region 의 latency

회사 가 us-east-1 · asia-northeast3 의 cluster:
  - 각 region 의 Prometheus 의 metric
  - 한 Grafana 의 view

문제:
  Grafana 가 us-east-1 → Prometheus asia-northeast3 query:
    network latency 200ms
    너무 많은 panel = 누적 latency

해결:
  - 각 region 의 Prometheus + 통합 Mimir (글로벌)
  - 또는 region 별 Grafana + Cross-region link
  - Grafana Cloud 의 multi-region 활용

Clock Skew

Clock Skew(노드 간 시각이 어긋난 상태)는 trace의 음수 duration 같은 기괴한 증상으로 드러나요.

사고 case:
  - 100 instance 의 NTP 동기화 X
  - 일부 instance 의 시각 = 30 초 어긋남
  - Trace 의 timeline 의 음수 duration
  - Metric 의 시각 불일치

해결:
  - 모든 instance 의 NTP (chrony · ntpd)
  - Kubernetes 의 NodeLocalDNS · 자동 sync
  - Monitoring: node_time_seconds 의 회사 표준 시각 과 비교

Cross-region Replication 의 함정

사고:
  - Mimir 의 multi-region replication
  - us-east-1 의 데이터 → asia-northeast3 로 자동 replication
  - 네트워크 비용 폭증 + 데이터 일관성 문제

해결:
  - Replication 의 의도 명시
  - 데이터 의 sharding (region 별 namespace)
  - "필요한 데이터 만" replication

Compliance · Audit 사고

GDPR · PIPA 의 의식

GDPR(EU 개인정보 보호 규정) · PIPA(한국 개인정보 보호법)은 관측 데이터에도 그대로 적용돼요. PII(개인식별정보, Personally Identifiable Information)가 로그·메트릭에 박혀 들어가는 게 가장 흔한 위반이에요.

관측 데이터 의 PII:
  - log 안 의 email · 전화 · 신용카드
  - metric label 의 user_id (직접 식별)
  - trace 의 user.email attribute

→ 이런 데이터 가 Grafana 에 저장 = 컴플라이언스 위반

회피 — Pre-ingestion Redaction

# Alloy 의 redaction stage
loki.process "redact" {
  forward_to = [loki.write.default.receiver]

  stage.regex {
    expression = `(?P<email>[\w.+-]+@[\w-]+\.[\w.-]+)`
  }
  stage.replace {
    expression = `(?P<email>[\w.+-]+@[\w-]+\.[\w.-]+)`
    replace    = "REDACTED_EMAIL"
  }

  # 신용카드
  stage.replace {
    expression = `\b\d{4}-\d{4}-\d{4}-\d{4}\b`
    replace    = "REDACTED_CC"
  }
}

사고 case: 의도 안 한 PII

사고:
  - Application 의 새 endpoint 의 PII 가 path 에 박힘
  - /reset-password?email=alice@company.com
  - Loki 의 1년 retention → 모든 사용자 의 email
  - 컴플라이언스 audit → 위반

해결:
  - Application 의 audit (PII 의 URL 사용 금지)
  - Loki 의 정기 scan (regex 의 매칭)
  - 발견 시 즉시 redaction stage 추가 + 데이터 삭제

Audit Log 의 의미

Enterprise 의 audit log:
  - 누가 dashboard 의 sensitive data 봤나
  - 누가 권한 변경 했나
  - 누가 alert 의 silence 박았나

→ 사고 시 의 추적 자료
→ 컴플라이언스 의 의무 (GDPR 의 "right to be informed")

Vendor Lock-in 의 회피

Lock-in 의 의식

Grafana 의 lock-in 위험:
  - Grafana 의 dashboard JSON format
  - PromQL · LogQL · TraceQL 의 query language
  - Enterprise plugin 의 vendor-specific

→ 5 년 후 의 vendor 변경 시 의 비용

회피 - 표준 의식

Open standard 의 우선:
  - OpenTelemetry (모든 vendor 호환)
  - Prometheus exposition format (de-facto 표준)
  - SQL · OpenSearch · ClickHouse 의 표준 query

Grafana 의 alternative:
  - Datadog (벤더)
  - New Relic (벤더)
  - Splunk (벤더 + 더 비싸)
  - PostHog (오픈소스 product analytics)
  - 자체 dashboard

Migration Path

표준 의 의식 의 대가:
  - Vendor 의 unique 기능 의 거부 (단순한 정의 만 사용)
  - Vendor 마다 의 약간 의 변환 필요

장기적:
  - Grafana → Datadog · 반대 의 6 개월 의 migration 가능
  - Lock-in 의 약함

운영 1 년 의 KPI

운영 의 metric

관측 시스템 자체 의 health:

1. Availability
   - Grafana 의 uptime (99.9% 목표)
   - Datasource 의 healthy
   - Alert 의 delivery latency

2. Cost
   - 월간 비용 의 trend
   - 데이터 unit 의 비용 ($ / GB · $ / 1M sample)

3. Adoption
   - 활성 사용자 수
   - dashboard 의 사용 빈도
   - alert 의 useful 비율

4. Incident Response
   - Mean Time To Detection (MTTD)
   - Mean Time To Recovery (MTTR)
   - Alert false positive rate

5. Data Quality
   - Cardinality 의 trend
   - 데이터 누락 의 빈도
   - Backfill 의 빈도

매월 review

□ 비용 trend (예상 vs 실제)
□ 활성 사용자 · dashboard 의 사용
□ Top 10 noisy alert
□ Most viewed dashboard
□ Cardinality 의 trend
□ Plugin 의 CVE · upgrade
□ Backup 의 시험 결과
□ Audit log 의 review

함정 정리 (총정리)

1. Cardinality 폭발

  • dynamic 값 = label 금지
  • Recording Rule 의 aggregation
  • 정기 cardinality 모니터

2. Cost 폭증

  • Cloud 의 한도 alert
  • Mimir · Loki · BQ 의 비용 의식
  • 매일 자동 audit

3. Plugin CVE

  • 공식 plugin 우선
  • 주기적 CVE review
  • Production 의 plugin 제한

4. Alert 폭주

  • Inhibition 의 표준
  • Aggregation alert (instance X service)
  • Multi-window burn rate

5. Configuration Drift

  • editable: false 강제
  • Drift detection 자동
  • Git 의 single source of truth

6. Backup · DR 실패

  • 정기 시험 (분기 마다)
  • 전체 backup (dashboard 만 X)
  • RTO · RPO 의 명시

7. Multi-region Latency

  • Region 별 stack + 글로벌 통합
  • Cross-region replication 의식
  • Cost vs latency 의 trade-off

8. Clock Skew

  • NTP 의 강제
  • node_time_seconds 의 모니터
  • Trace 의 negative duration 의 감지

9. Compliance · PII

  • Pre-ingestion redaction
  • 정기 scan
  • Audit log 의 활성

10. Vendor Lock-in

  • Open standard 우선 (OpenTelemetry · Prometheus format)
  • Migration path 의 design
  • Vendor unique 의 신중

시험 직전 한 번 더 — 운영 함정 압축 노트

Cardinality

  • prometheus_tsdb_head_series · topk(10, count by ...)
  • dynamic 값 = label X (path · user_id · request_id)
  • Recording Rule 의 aggregation
  • 정기 모니터

Cost

  • Cloud — usage alert · monthly estimate
  • Mimir/Loki — chunk size · retention
  • BQ — materialized view · query cache
  • 매일 자동 audit · Slack 알림

Plugin

  • 공식 vs community vs Enterprise
  • CVE 의 자동 알림
  • 주기적 review · supply chain 의식

Alert 폭주

  • Inhibition (region · cluster · service down)
  • Aggregation alert (count by service)
  • Multi-window burn rate
  • Mute Timings

Drift

  • editable: false 강제
  • 매시 git diff · 자동 알림
  • 자동 revert 옵션

Backup · DR

  • 매일 backup + 분기 시험
  • 전체 export (dashboard + alert + datasource + team)
  • RTO · RPO 의 명시
  • Tabletop exercise

Multi-region

  • region 별 Prometheus + 글로벌 Mimir
  • Cross-region replication 의식
  • NTP 의 강제 · clock skew 의 알림

Compliance

  • Pre-ingestion redaction (email · 신용카드 · 주민번호)
  • 정기 PII scan
  • Audit Log 의 export · review
  • GDPR · PIPA 의 의식

Vendor Lock-in

  • OpenTelemetry · Prometheus format
  • 표준 query 의식 (vendor-unique 신중)
  • Migration path 의 design

운영 KPI

  • Availability (uptime · delivery)
  • Cost (월 trend · unit cost)
  • Adoption (사용자 · dashboard 의 사용)
  • Incident Response (MTTD · MTTR · false positive)
  • Data Quality (cardinality · 누락 · backfill)

매월 review

  • 비용 · 사용자 · noisy alert · viewed dashboard · cardinality · CVE · backup · audit

공식 문서: Prometheus 운영 · Grafana 운영 에서 더 깊은 운영 가이드를 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!