가능해. 지금 가지고 있는 Prometheus + Grafana + Fluent Bit + OpenSearch + Thanos + Alertmanager를 기반으로 하면 상당히 좋은 체계를 만들 수 있어.
다만 지금 스택은 크게 보면 Metrics + Logs 중심이고, Application 문제를 “어느 함수/어느 요청/어느 서비스에서 느려졌는지”까지 내려가는 Tracing과 Profiling 계층이 비어 있는 상태라고 보는 게 좋아. OpenTelemetry는 metrics/logs/traces를 공통된 방식으로 생성·수집·전달하는 표준 계층이라 이 부분을 보강하기에 적합해. (OpenTelemetry)
내가 권하는 방향은 단순히 "모니터링 서버를 하나 만드는 것"이 아니라,
Data Lakehouse 전체를 하나의 관측 가능한 시스템으로 만들고, 장애 발생 시 Business → Application → Service → Runtime → Infrastructure → Storage까지 drill-down할 수 있는 체계
로 설계하는 거야.
전체를 다음 6개 Layer로 나누는 것을 추천해.
┌───────────────────────────────────────────────────────────────┐
│ ① BUSINESS / SLO │
│ │
│ Data Freshness │ Job Success │ Query SLA │ Data Quality │
│ Pipeline SLA │ Availability │ Cost / Capacity │
└───────────────────────────────┬───────────────────────────────┘
│
┌───────────────────────────────▼───────────────────────────────┐
│ ② APPLICATION │
│ │
│ API │ Ingestion │ Scheduler │ Spark │ Flink │ Trino │ ETL │
│ Kafka │ Metadata │ Catalog │ Query Engine │ Data Services │
└───────────────┬────────────────┬────────────────┬──────────────┘
│ │ │
Metrics Logs Traces
│ │ │
┌───────────────▼────────────────▼────────────────▼──────────────┐
│ ③ TELEMETRY │
│ │
│ Prometheus Fluent Bit OpenTelemetry │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ Data Prepper OTel Collector │
└──────┼────────────────┬─────────────────┬──────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Thanos │ │ OpenSearch │ │ Trace Backend│
│ Metrics │ │ Logs │ │ Tempo/Jaeger │
└──────┬──────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└────────────────┼─────────────────┘
▼
┌──────────────┐
│ Grafana │
│ Unified View │
└──────┬───────┘
│
┌──────▼───────┐
│ Alertmanager │
│ Incident │
└──────────────┘
┌───────────────────────────────────────────────────────────────┐
│ ④ RUNTIME / INFRA │
│ │
│ Kubernetes │ Node │ CPU │ Memory │ Network │ Disk │ JVM │ Go │
│ cAdvisor │ kube-state │ node-exporter │ DCGM │ eBPF │
└───────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────┐
│ ⑤ DATA PLATFORM │
│ │
│ Object Storage │ MinIO │ Kafka │ Spark │ Flink │ Trino │
│ Iceberg/Hudi/Delta │ Catalog │ Metastore │ DB │ Cache │
└───────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────┐
│ ⑥ GOVERNANCE / OPERATIONS │
│ │
│ Service Catalog │ Ownership │ Runbook │ SLO │ Incident │
│ Change History │ Deployment │ Version │ Config │ Dependency │
└───────────────────────────────────────────────────────────────┘
핵심은 Grafana를 단순한 그래프 모음이 아니라 전체 시스템의 Observability Portal로 만드는 거야.
현재 가지고 있는 것들을 이렇게 정의하면 좋아.
| Component | 역할 |
|---|---|
| Prometheus | 실시간 Metrics 수집 |
| Thanos | Metrics 장기 보관/Global Query |
| Grafana | 통합 Visualization |
| Alertmanager | Alert routing / grouping / notification |
| Fluent Bit | Node/Pod/Application Log 수집 |
| OpenSearch | Log 검색/분석 |
| OpenTelemetry | Application telemetry 표준화 추가 추천 |
| OTel Collector | Metrics/Logs/Traces 수집/전달 추가 추천 |
| Data Prepper | OpenSearch 쪽 Log/Trace processing 선택/추천 |
| Trace Backend | Distributed tracing 추가 필요 |
| Profiler | CPU/Memory/Goroutine 수준 분석 추가 추천 |
Prometheus는 기본적으로 time-series metrics를 수집하고, recording rule을 사용하면 반복적으로 계산하는 복잡한 PromQL을 미리 계산해서 dashboard/alert 성능을 높일 수 있어. (Prometheus)
Thanos는 여기서 장기 metrics 저장 + 여러 Prometheus의 global query 계층으로 생각하면 돼.
현재 구성에서 내가 가장 먼저 추가할 것은 이거야.
추천 구조:
Application
│
│ OTLP
▼
OpenTelemetry Collector
│
├───────────────► Prometheus
│ │
│ ▼
│ Metrics
│
├───────────────► OpenSearch
│ │
│ ▼
│ Logs
│
└───────────────► Tempo
│
▼
Traces
OpenTelemetry Collector는 receiver → processor → exporter 구조로 telemetry를 받아 처리하고 여러 backend로 fan-out할 수 있어서 이 역할에 잘 맞아. (OpenTelemetry)
OpenSearch 쪽에서도 공식적으로 OTel Collector → Data Prepper → OpenSearch, 그리고 metrics는 Prometheus로 보내는 형태를 지원하고 있어. (OpenSearch Observability)
나는 현재 Stack을 고려하면:
1순위: Grafana Tempo
또는
2순위: OpenSearch Trace Analytics
를 검토할 것 같아.
Tempo는 distributed tracing backend이고 Grafana에서 바로 trace를 조회하고 metrics/logs와 연결할 수 있다. (Grafana Labs)
OpenSearch를 이미 많이 사용한다면 OpenSearch의 Trace Analytics + Data Prepper로 통합하는 것도 가능하다. (OpenSearch Documentation)
개인적으로는:
Metrics = Prometheus/Thanos
Logs = OpenSearch
Traces = Tempo
Visualization = Grafana
를 추천해.
각 저장소의 역할이 명확하기 때문이야.
특히 앞에서 이야기했던 Go/MinIO 계열 application이 있다면 상당히 중요해.
현재:
CPU high
↓
Grafana
↓
"CPU가 높다"
에서 끝나잖아.
Profiling을 넣으면:
CPU high
↓
Trace
↓
slow request
↓
Profile
↓
goroutine / function
↓
실제 bottleneck
까지 내려갈 수 있어.
Grafana Pyroscope
Continuous profiling 시스템이고 Grafana에서 metrics/logs/traces와 profiling 데이터를 연결할 수 있어. 특히 trace와 profile을 연결하면 특정 request의 어느 함수가 CPU를 소비하는지까지 내려갈 수 있다. (Grafana Labs)
예:
Request
│
▼
Trace
│
├── API 20ms
├── Scheduler 50ms
├── Storage 4.2s
│
▼
Profile
│
├── goroutine
├── CPU
├── memory
└── lock
이게 application performance troubleshooting의 마지막 단계가 돼.
이게 매우 중요해.
일반 Web Service라면:
CPU
Memory
Request
Latency
Error
만 잘 봐도 되는데,
Data Lakehouse는 Data Pipeline 자체가 서비스의 핵심이야.
그래서 별도의 Data Observability Layer가 필요해.
나는 다음과 같이 분류할 것을 추천해.
CPU
Memory
Disk
Network
Filesystem
Kubernetes
Node
Pod
Container
Kafka
Spark
Flink
Trino
MinIO/Object Storage
Metadata DB
Catalog
Metastore
Airflow/Scheduler
API
Ingestion
Query
Transformation
Metadata
Data Service
Job Success
Job Duration
Queue Time
Throughput
Retry
Backlog
Lag
Failure
여기가 일반 Observability와 가장 다른 부분이야.
Data Freshness
Data Completeness
Data Volume
Data Distribution
Schema Change
Null Rate
Duplicate Rate
Data Quality
Partition Health
Small File
Compaction
최종적으로는:
"데이터가 정상적으로 제공되고 있는가?"
를 봐야 해.
이걸 안 만들면 Grafana dashboard가 금방 엉망이 돼.
예를 들어 모든 metric에 최소한:
environment
cluster
namespace
service
component
instance
node
pod
job
version
team
같은 공통 label을 갖게 해야 해.
Data Lakehouse라면 추가로:
pipeline
dataset
table
database
catalog
job
stage
partition
tenant
등을 고려해야 해.
단, table, dataset, request_id 등을 무조건 metric label로 넣으면 cardinality 폭발이 발생할 수 있어.
예를 들어:
request_id = 123456789
같은 값을 metric label로 넣으면 안 돼.
그런 high-cardinality 정보는 logs/traces로 보내는 게 맞아.
Infrastructure:
Utilization
Saturation
Errors
예:
CPU utilization
Disk utilization
Disk queue
Network utilization
Memory pressure
Application:
Rate
Errors
Duration
예:
API request rate
API error rate
API latency
OpenTelemetry/Trace 기반으로 RED metric을 만들 수도 있다. OpenSearch observability 역시 trace 기반 RED metrics를 지원한다. (OpenSearch)
Input records/sec
Output records/sec
Processing latency
Backlog
Lag
Retry
Failure
Freshness
Completeness
Validity
Duplicate
Schema
Volume
Grafana를 다음처럼 계층화하는 걸 추천해.
00 Executive / Service Health
│
├── 01 Lakehouse Overview
│
├── 02 Data Pipeline
│
├── 03 Application
│
├── 04 Compute
│
├── 05 Storage
│
├── 06 Network
│
├── 07 Kubernetes
│
├── 08 Kafka
│
├── 09 Spark
│
├── 10 Flink
│
├── 11 Trino
│
├── 12 MinIO / Object Storage
│
├── 13 Metadata / Catalog
│
├── 14 Data Quality
│
└── 99 Observability System
특히 99 Observability System이 중요해.
Monitoring 시스템 자체가 죽었는데 "모니터링상 정상"으로 보이면 안 되니까.
운영자가 아침에 Grafana를 열었을 때 가장 먼저 보는 화면은 이런 형태가 좋다.
┌─────────────────────────────────────────────────────┐
│ DATA LAKEHOUSE │
├────────────┬────────────┬────────────┬───────────────┤
│ Availability│ Pipeline │ Freshness │ Query SLA │
│ 99.98% │ 98.7% │ 99.2% │ 99.9% │
├────────────┴────────────┴────────────┴───────────────┤
│ │
│ 🔴 Pipeline Failure 2 │
│ 🟡 Storage Degraded 1 │
│ 🟢 Kafka Healthy │
│ 🟢 Spark Healthy │
│ 🟡 Trino High Latency │
│ │
├─────────────────────────────────────────────────────┤
│ Data Freshness │
│ │
│ Dataset A ████████████████████ 2 min │
│ Dataset B ███████████████ 12 min │
│ Dataset C █████ 43 min 🔴 │
│ │
├─────────────────────────────────────────────────────┤
│ Active Incidents │
│ │
│ INC-123 Storage latency ↑ │
│ INC-124 Spark job failure │
└─────────────────────────────────────────────────────┘
이 부분은 표준 Runbook으로 만들어 놓는 게 좋아.
내가 추천하는 순서는:
먼저:
"무엇이 얼마나 영향을 받았는가?"
를 확인.
어떤 dataset?
어떤 pipeline?
어떤 사용자?
어떤 API?
언제부터?
전체인가 일부인가?
Error ↑
Latency ↑
Throughput ↓
Freshness ↓
Job failure ↑
즉,
무엇이 이상한가?
를 먼저 찾는다.
Prometheus/Thanos:
CPU
Memory
Disk
Network
Request
Latency
Error
Queue
Thread
Goroutine
GC
여기서 중요한 건 원인이라고 판단하지 말고 correlation을 찾는 것.
예:
14:02
API latency ↑
14:02
CPU normal
14:02
Disk latency ↑
14:02
MinIO node-07 latency ↑
그러면 다음 단계로 내려간다.
OpenSearch에서:
service
pod
node
timestamp
trace_id
request_id
error
exception
을 기준으로 검색.
가장 중요한 것은 trace_id/request_id를 log에 넣는 것이야.
예:
{
"timestamp": "...",
"level": "ERROR",
"service": "ingestion",
"trace_id": "abc123",
"request_id": "xyz789",
"dataset": "customer",
"error": "timeout"
}
이렇게 해야:
Grafana
↓
Trace
↓
trace_id
↓
OpenSearch
↓
exact error log
가 가능해.
OpenSearch도 Fluent Bit → Data Prepper → OpenSearch 형태의 log pipeline을 공식적으로 지원한다. (OpenSearch Documentation)
이 단계에서:
Request
↓
API
↓
Ingestion
↓
Kafka
↓
Spark
↓
MinIO
↓
Metadata
같은 dependency chain을 확인.
예를 들어:
POST /ingest
5.2s
│
├── Auth 10ms
├── Metadata 30ms
├── Kafka 40ms
├── Processing 120ms
└── Storage 5.0s 🔴
이면 바로 Storage 쪽으로 이동.
Tracing은 여러 서비스 사이의 latency/error 위치와 dependency 관계를 파악하는 데 특히 유용하다. (Grafana Labs)
여기서부터 system internals로 들어간다.
Go라면:
CPU profile
Goroutine
Heap
GC
Mutex
Block
Scheduler
Java라면:
JVM CPU
Heap
GC
Thread
Lock
Class
Python이면:
CPU
Memory
GIL
Coroutine
여기서 앞에서 질문했던:
"CPU는 43코어인데 goroutine이 200k"
같은 문제가 등장하는 거야.
그 다음:
Node
↓
CPU
Memory
Disk
Network
Kernel
Filesystem
특히 Data Lakehouse에서는 Disk를 매우 중요하게 봐야 해.
예:
CPU 45%
Memory 62%
Disk Util 95%
Disk Lat 120ms 🔴
IOPS 100%
Queue 80 🔴
이 경우 CPU가 45%라고 해서 "CPU 여유가 있으니 문제없다"고 판단하면 안 돼.
앞에서 이야기한 것처럼 CPU가 낮은데 goroutine/thread가 증가하는 상황은 I/O wait 때문일 수도 있기 때문이야.
여기까지 내려가면:
MinIO
Kafka
Spark
Flink
Trino
Iceberg
Metastore
Catalog
등을 확인.
예를 들어 MinIO라면:
Drive latency
Drive errors
Disk utilization
IOPS
Throughput
Network
Erasure set
Node health
Healing
Replication
같은 지표가 필요해.
특히 네가 앞에서 이야기했던 것처럼 erasure set 기반 storage라면 node/drive별 latency distribution을 반드시 봐야 한다.
마지막으로:
파일 개수
파일 크기
partition
record count
schema
null
duplicate
freshness
를 확인.
예:
Pipeline 정상
Application 정상
Storage 정상
그런데 Dataset B
record count = 0
이면 infrastructure 장애가 아니라 data issue일 수 있어.
Incident
│
▼
Business Impact?
│
▼
SLO / Symptom
│
├── Error?
├── Latency?
├── Throughput?
├── Freshness?
└── Job failure?
│
▼
Metrics
│
├── CPU
├── Memory
├── Disk
├── Network
├── Application
└── Pipeline
│
▼
Logs
│
▼
Trace
│
▼
Runtime Profile
│
▼
Infrastructure
│
▼
Data Platform
│
▼
Data Quality
│
▼
Root Cause
이 순서를 운영 표준 SOP로 만드는 게 굉장히 중요해.
모든 Application log에 최소한:
timestamp
level
service
component
environment
version
node
pod
trace_id
span_id
request_id
job_id
dataset
error_code
를 표준화.
특히:
trace_id / span_id
가 굉장히 중요해.
각 주요 operation을 span으로 만든다.
예:
ingestion.request
├── authentication
├── metadata.lookup
├── kafka.produce
├── processing
│ ├── read
│ ├── transform
│ └── write
└── storage.commit
Data Lakehouse에서는 특히:
dataset
table
partition
job
stage
정보를 trace attribute로 활용하면 굉장히 강력해.
단, high-cardinality 정보를 metrics label로 넣는 것과 trace attribute로 넣는 것은 구분해야 한다.
Application별로 최소:
CPU
Heap
Goroutine/Thread
Mutex
Block
Allocation
GC
을 확보.
Go application이 많다면 Pyroscope 도입 효과가 상당히 클 것 같아.
Alert를 단순히:
CPU > 80%
로 만드는 것은 지양하는 게 좋아.
대신:
Alert
↓
Symptom
↓
Impact
중심으로.
예:
CPUHigh
CPU > 80%
IngestionPipelineHighLatency
p95 ingestion latency > 5s for 10m
DataFreshnessSLOViolation
dataset freshness > 15m
즉 Infrastructure Alert → Service Alert → SLO Alert 순으로 발전시키는 거야.
Prometheus alerting rules에는 for 등을 이용해서 조건이 일정 시간 지속된 경우에만 firing하도록 만들 수 있다. (Prometheus)
Data Lakehouse에서는 다음 SLO가 중요해.
API availability
Query availability
Storage availability
API p95
Query p95
Ingestion p95
Dataset A < 5 min
Dataset B < 15 min
Job success > 99%
Pipeline completion < 30 min
Completeness > 99.9%
Schema violation = 0
Duplicate < threshold
이걸 Observability Platform Team 하나가 다 책임지는 구조로 만들면 안 돼.
추천 구조:
Observability Platform
│
┌─────────────────┼─────────────────┐
│ │ │
Metrics Logs Traces
Platform Platform Platform
│ │ │
└─────────────────┼─────────────────┘
│
Grafana / Alert
│
┌────────────────┼────────────────┐
│ │ │
Data Platform Application Storage
Team Team Team
│ │ │
Spark/Kafka API/Service MinIO
Flink/Trino Runtime Disk
Catalog JVM/Go Network
책임:
책임:
책임:
책임:
이걸 의외로 많이 놓쳐.
예:
service: ingestion-api
owner: data-platform
tier: critical
dependencies:
- kafka
- metadata
- minio
slo:
availability: 99.9%
latency_p95: 2s
dashboard:
- ingestion-overview
runbook:
- ingestion-timeout
alerts:
- ingestion-high-error
- ingestion-high-latency
이런 metadata를 가지고 있어야 Alert가 왔을 때:
Alert
↓
Service
↓
Owner
↓
Dashboard
↓
Runbook
↓
Dependency
로 바로 이동할 수 있어.
현재 환경을 최대한 활용한다면:
| 영역 | 추천 |
|---|---|
| Metrics | Prometheus |
| Long-term Metrics | Thanos |
| Visualization | Grafana |
| Alert | Alertmanager |
| Logs Agent | Fluent Bit |
| Logs Processing | Data Prepper |
| Logs Storage | OpenSearch |
| Application Telemetry | OpenTelemetry |
| Telemetry Gateway | OTel Collector |
| Tracing | Grafana Tempo 또는 OpenSearch Trace Analytics |
| Profiling | Grafana Pyroscope |
| K8s | kube-state-metrics + cAdvisor + node-exporter |
| Network | node/network metrics + 필요시 eBPF |
| Data Quality | Great Expectations / Soda 계열 검토 |
| Synthetic Monitoring | Blackbox Exporter 또는 k6 |
| Incident | PagerDuty/Opsgenie/Slack/사내 Incident 시스템 |
| Service Catalog | Backstage 또는 사내 Catalog |
| Runbook | Git 기반 Markdown/Wiki |
| Deployment correlation | Git/CI/CD metadata |
| Security | RBAC + TLS + Secret management |
OpenTelemetry Collector를 중심으로 잡으면 애플리케이션이 backend에 직접 종속되지 않고 telemetry를 처리·retry·batch·filter한 뒤 여러 backend로 전달할 수 있다는 장점이 있다. (OpenTelemetry)
모든 걸 한꺼번에 넣을 필요는 없어.
OpenTelemetry
OTel Collector
Service/Metric naming standard
SLO
Runbook
Service Catalog
Distributed Tracing Tempo 또는 OpenSearch Trace AnalyticsP1 — 성능 문제 분석
PyroscopeP1 — Kubernetes
kube-state-metrics node-exporter cAdvisorP2 — Data Observability
Data Freshness Data Quality Schema monitoring Data volume monitoringP2 — Synthetic
Blackbox Exporter k6P2 — Network/Kernel
eBPF
29. 결국 가장 중요한 것은 "Correlation"
최종적으로 Grafana에서 다음이 연결되어야 해.
┌──────────────┐ │ Dashboard │ └──────┬───────┘ │ ┌───────────┼───────────┐ ▼ ▼ ▼ Metrics Logs Traces │ │ │ │ │ │ └───────────┼───────────┘ │ Profile │ ▼ Infrastructure │ ▼ Data Platform │ ▼ Data Quality그리고 이 모든 연결의 핵심 키가:
service namespace pod node deployment version trace_id span_id request_id job_id dataset가 되는 거야.
30. 내가 이 프로젝트를 실제로 시작한다면
순서를 이렇게 잡겠어.
Phase 1 — Standardization
Metric naming Label convention Log schema Trace convention Service catalog Ownership SLO definitionPhase 2 — Infrastructure Observability
Kubernetes Node CPU Memory Disk Network MinIO KafkaPhase 3 — Application Observability
Application metrics Structured logs OpenTelemetry Trace ID Request IDPhase 4 — Distributed Tracing
OTel Collector Tempo/OpenSearch Trace Grafana correlationPhase 5 — Profiling
Pyroscope CPU Memory Goroutine Mutex GCPhase 6 — Data Observability
Freshness Completeness Volume Schema Quality Pipeline SLAPhase 7 — SRE 운영체계
SLO Alert Incident Runbook On-call Postmortem Capacity planning
최종적으로 만들고 싶은 모습
장애가 발생했을 때 운영자가 “Prometheus를 볼까? OpenSearch를 볼까?”라고 고민하지 않는 구조가 목표야.
예를 들어:
14:02 Dataset A freshness SLO violation
에서 시작해서,
Dataset A ↓ Pipeline A ↓ Spark Job #1234 ↓ Ingestion Service ↓ Trace ID: abc123 ↓ Storage write = 4.8 sec ↓ MinIO node-07 ↓ Drive latency p99 ↑ ↓ /dev/nvme3 ↓ Disk I/O saturation ↓ Root Cause까지 하나의 흐름으로 내려갈 수 있어야 해.
이렇게 되면 지금 가지고 있는 Prometheus + Thanos + Grafana + Fluent Bit + OpenSearch + Alertmanager는 상당히 좋은 기반이 되고, 여기에 OpenTelemetry/Collector + Tracing + Profiling + Data Observability + SLO/Service Catalog를 추가하는 것이 전체 체계를 완성하는 핵심이라고 봐.
특히 OpenSearch도 현재 OTel 기반으로 logs/metrics/traces를 통합하는 observability architecture를 제공하고 있으므로, OpenSearch를 단순 로그 저장소로만 사용할지, trace/APM까지 가져갈지는 향후 architecture 결정에서 중요한 선택지가 된다. (OpenSearch)
원한다면 다음 단계로는 이걸 실제 설계 문서 수준으로 내려서 ① 전체 Logical/Physical Architecture Diagram → ② Kubernetes 배포 구조 → ③ Prometheus/Thanos/OTel/OpenSearch 데이터 흐름 → ④ Grafana Dashboard hierarchy → ⑤ Metric/Log/Trace naming convention → ⑥ Alert/SLO 목록 → ⑦ 장애 대응 Runbook → ⑧ 팀별 R&R까지 한 번에 설계하는 게 가장 좋을 것 같아.