관찰성 기초
시스템이 정상인지 아닌지를 아는 것은 모니터링입니다. 시스템이 왜 비정상인지를 파악할 수 있는 것은 **관찰성(Observability)**입니다. 관찰성은 시스템의 외부 출력(Metrics, Logs, Traces)을 통해 내부 상태를 추론하는 능력을 의미합니다. ML 모델이 갑자기 느려졌을 때, 추론 결과가 이상할 때, GPU 메모리가 부족할 때 — 원인을 신속하게 파악하려면 관찰성이 갖춰져 있어야 합니다.학습 목표
- 모니터링과 관찰성의 차이를 이해하고, 3축(Metrics, Logs, Traces)의 역할을 설명할 수 있다
- Prometheus 메트릭 타입을 구분하고 기초 PromQL 쿼리를 작성할 수 있다
- 구조화 로그의 필요성과 로그 수집 파이프라인을 설계할 수 있다
- 분산 트레이싱의 핵심 개념(Span, Trace ID)과 OpenTelemetry 아키텍처를 이해한다
왜 중요한가
LLM 서빙 환경에서는 전통적인 웹 서비스와 다른 관찰성 요구사항이 있습니다. TTFT(Time To First Token), TPS(Tokens Per Second), GPU 메모리 사용률, KV Cache 적중률 같은 ML 특화 메트릭이 필요합니다. 모델이 환각(hallucination)을 일으키는 빈도, 프롬프트 길이 분포, 토큰 비용 추적 같은 LLM 고유의 관찰성 요구사항은 기존 APM 도구만으로는 충족되지 않습니다. 데이터 파이프라인에서도 관찰성은 핵심입니다. 학습 데이터가 드리프트했는지, 전처리 단계에서 데이터 손실이 발생했는지, 모델 성능이 점진적으로 저하되고 있는지 — 이 모든 것을 Metrics, Logs, Traces의 조합으로 감지합니다.모니터링 vs 관찰성
| 구분 | 모니터링 (Monitoring) | 관찰성 (Observability) |
|---|---|---|
| 질문 | ”시스템이 정상인가?" | "왜 비정상인가?” |
| 접근 | 사전 정의된 메트릭/임계치 | 임의의 질문에 답할 수 있는 데이터 |
| 대시보드 | 미리 만든 차트로 감시 | 즉석에서 데이터를 탐색 |
| 알림 | 알려진 장애 패턴 감지 | 예상치 못한 장애 원인 추적 |
| 데이터 | Metrics 위주 | Metrics + Logs + Traces 통합 |
관찰성의 3축은 독립적이지 않습니다. 알림은 Metrics에서 발생하고, Logs에서 상세 맥락을 확인하며, Traces에서 요청 경로를 추적합니다.
이 3가지를 **상관관계(correlation)**로 연결하는 것이 진정한 관찰성입니다.
Metrics (메트릭)
메트릭은 시간에 따른 숫자 데이터입니다. 시스템의 현재 상태를 정량적으로 표현합니다.Prometheus 메트릭 타입
| 타입 | 설명 | 특징 | 사용 예시 |
|---|---|---|---|
| Counter | 단조 증가 카운터 | 0에서 시작, 리셋 시에만 0으로 | 총 요청 수, 총 에러 수, 처리 토큰 수 |
| Gauge | 증감 가능한 현재값 | 올라갈 수도, 내려갈 수도 | 현재 GPU 사용률, 메모리 사용량, 큐 깊이 |
| Histogram | 값의 분포 (버킷 기반) | le 레이블로 버킷 경계 정의 | 응답 시간 분포, 프롬프트 길이 분포 |
| Summary | 값의 분포 (분위수 기반) | 클라이언트 측 분위수 계산 | p50/p95/p99 지연시간 (비권장, Histogram 선호) |
메트릭 작명 규칙
PromQL 기초
| 함수/연산 | 설명 | 예시 |
|---|---|---|
rate() | Counter의 초당 변화율 | rate(http_requests_total[5m]) |
increase() | Counter의 구간 증가량 | increase(http_requests_total[1h]) |
histogram_quantile() | Histogram에서 분위수 계산 | histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) |
sum by() | 레이블별 합계 | sum by (status_code) (rate(http_requests_total[5m])) |
avg by() | 레이블별 평균 | avg by (instance) (gpu_utilization) |
topk() | 상위 N개 | topk(5, rate(http_requests_total[5m])) |
absent() | 메트릭 존재 여부 확인 | absent(up{job="model-server"}) |
Logs (로그)
로그는 개별 이벤트의 상세 기록입니다. 메트릭이 “무엇이 얼마나”를 알려준다면, 로그는 “무엇이 정확히 일어났는지”를 알려줍니다.비구조화 vs 구조화 로그
| 비교 | 비구조화 | 구조화 (JSON) |
|---|---|---|
| 가독성 | 높음 (사람) | 낮음 (사람), 높음 (기계) |
| 검색 | 정규표현식, grep | 필드 기반 쿼리 |
| 저장 효율 | 비일관적 | 일관적 스키마 |
| 상관관계 | 어려움 | trace_id 기반 연결 |
| 권장 | 개발/디버깅 | 프로덕션 (항상 권장) |
로그 레벨
| 레벨 | 용도 | 예시 |
|---|---|---|
| DEBUG | 상세 디버깅 정보 | 입력 텐서 shape, 중간 연산 결과 |
| INFO | 정상 동작 기록 | 모델 로딩 완료, 요청 처리 성공 |
| WARNING | 잠재적 문제 | GPU 메모리 80% 도달, 느린 쿼리 감지 |
| ERROR | 오류 발생 (복구 가능) | 모델 체크포인트 로딩 실패, API 타임아웃 |
| FATAL/CRITICAL | 치명적 오류 (프로세스 종료) | OOM, GPU 하드웨어 오류 |
로그 수집 파이프라인
| 구성 요소 | 역할 | 특징 |
|---|---|---|
| Fluent Bit | 경량 로그 수집기 | 저메모리(~450KB), K8s DaemonSet |
| Fluentd | 범용 로그 수집기 | 풍부한 플러그인, 복잡한 라우팅 |
| OpenSearch | 전문 검색 엔진 | 강력한 검색, 높은 저장 비용 |
| Loki | 로그 집계 시스템 | 레이블 기반 인덱싱, 저비용 |
Traces (분산 트레이싱)
트레이스는 하나의 요청이 여러 서비스를 거치는 전체 경로를 기록합니다.핵심 개념
| 개념 | 설명 |
|---|---|
| Trace | 하나의 요청에 대한 전체 경로. 고유한 Trace ID로 식별 |
| Span | Trace 내 개별 작업 단위. 시작/종료 시간, 속성, 이벤트 포함 |
| Trace ID | 전체 요청을 관통하는 고유 식별자. 서비스 간 전파 |
| Span ID | 개별 Span의 고유 식별자 |
| Parent Span | 현재 Span을 호출한 상위 Span. 트리 구조 형성 |
| Attributes | Span에 첨부된 키-값 메타데이터. model.name, tokens.count 등 |
샘플링 전략
| 전략 | 설명 | 사용 사례 |
|---|---|---|
| Head-based | 요청 시작 시 샘플링 결정 | 단순, 일정 비율 수집 |
| Tail-based | 요청 완료 후 결정 | 에러/느린 요청만 수집, 비용 효율적 |
| Rate limiting | 초당 최대 N개 트레이스 | 트래픽 급증 시 보호 |
프로덕션에서 모든 요청을 트레이싱하면 오버헤드가 큽니다.
일반적으로 1-10% 샘플링 + 에러/지연 요청 100% 수집 조합이 효과적입니다.
OpenTelemetry (OTEL)
OpenTelemetry는 Metrics, Logs, Traces를 통합 수집하는 벤더 중립 관찰성 프레임워크입니다.아키텍처
Python 계측 예시
Prometheus + Grafana 구축
Grafana 대시보드 구성
Grafana(
http://localhost:3000)에서:- Data Sources에 Prometheus 추가 (URL:
http://prometheus:9090) - 새 Dashboard 생성 → Panel 추가
- 각 Panel에 PromQL 쿼리 입력
- 시각화 타입 선택 (Time series, Gauge, Stat, Table 등)
운영 메트릭 세트
| 메트릭 | 유형 | PromQL 예시 | 임계치 예시 |
|---|---|---|---|
| API 성공률 | Gauge (%) | sum(rate(http_requests_total{status=~"2.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 | > 99.9% |
| p50 지연시간 | Histogram | histogram_quantile(0.5, ...) | < 100ms |
| p95 지연시간 | Histogram | histogram_quantile(0.95, ...) | < 500ms |
| p99 지연시간 | Histogram | histogram_quantile(0.99, ...) | < 1000ms |
| 에러율 | Gauge (%) | sum(rate({status=~"5.."}[5m])) / sum(rate(total[5m])) | < 0.1% |
| 큐 깊이 | Gauge | request_queue_depth | < 100 |
| GPU 사용률 | Gauge (%) | DCGM_FI_DEV_GPU_UTIL | 알림: > 95% |
| GPU 메모리 | Gauge (%) | DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL * 100 | 알림: > 90% |
| 디스크 사용률 | Gauge (%) | (1 - node_filesystem_avail_bytes/node_filesystem_size_bytes) * 100 | 알림: > 85% |
| 메모리 사용률 | Gauge (%) | (1 - node_memory_MemAvailable_bytes/node_memory_MemTotal_bytes) * 100 | 알림: > 90% |
알림 설계
좋은 알림의 원칙
| 원칙 | 설명 | 나쁜 예 → 좋은 예 |
|---|---|---|
| 행동 가능 | 받은 사람이 즉시 할 수 있는 일이 있어야 함 | ”CPU 높음” → “Pod 스케일링 필요, HPA 확인” |
| 증상 기반 | 원인이 아닌 사용자 영향 기반 | ”디스크 90%” → “API 응답 시간 SLO 위반” |
| 노이즈 억제 | 짧은 스파이크에 반응하지 않음 | 1분 간격 → “5분 이상 지속 시” |
| 에스컬레이션 | 심각도에 따라 단계적 알림 | warning → Slack, critical → PagerDuty + 전화 |
| 런북 연결 | 알림에 대응 절차 문서 포함 | 알림만 → 알림 + 런북 URL |
에스컬레이션 단계
LLM 모니터링 특화 메트릭
| 메트릭 | 설명 | 중요성 |
|---|---|---|
| TTFT (Time To First Token) | 첫 토큰 생성까지의 시간 | 사용자 체감 응답 속도의 핵심 |
| TPS (Tokens Per Second) | 초당 생성 토큰 수 | 처리량(throughput) 지표 |
| 토큰 비용 | 입력/출력 토큰당 비용 | FinOps, 비용 최적화 |
| 환각률 | 사실과 다른 응답 비율 | 모델 품질 지표 |
| 프롬프트 길이 분포 | 입력 프롬프트 토큰 수 분포 | 리소스 계획, 비용 예측 |
| KV Cache 사용률 | KV Cache 메모리 점유율 | vLLM 성능 병목 진단 |
| 배치 크기 | 동시 처리 요청 수 | Continuous Batching 효율 |
| Prefill vs Decode 시간 | 프리필/디코드 단계별 시간 | 병목 단계 식별 |
Prometheus vs InfluxDB vs Datadog: 메트릭 스토리지 비교
Prometheus vs InfluxDB vs Datadog: 메트릭 스토리지 비교
- Prometheus: 오픈소스, pull 기반, PromQL, K8s 생태계 표준. 단일 노드 한계 → Thanos/Mimir로 확장
- InfluxDB: 오픈소스, push 기반, Flux/InfluxQL, IoT/시계열 특화. 자체 클러스터링(Enterprise)
- Datadog: SaaS, 에이전트 기반, 통합 관찰성. 높은 비용, 낮은 운영 부담 스타트업/소규모는 Prometheus + Grafana, 대규모 조직은 Datadog이나 managed Prometheus(Grafana Cloud)를 고려하세요.
Loki vs OpenSearch(Elasticsearch): 로그 스토리지 선택
Loki vs OpenSearch(Elasticsearch): 로그 스토리지 선택
- Loki: 레이블 기반 인덱싱, 로그 본문은 인덱싱하지 않음, 저비용, Grafana와 네이티브 통합
- OpenSearch: 전문(full-text) 인덱싱, 강력한 검색, 높은 저장/연산 비용 로그 본문 검색이 빈번하면 OpenSearch, 레이블 기반 필터링으로 충분하면 Loki가 비용 효율적입니다. 대부분의 ML 운영 환경에서는 Loki + Grafana 조합이 적합합니다.
OpenTelemetry Collector 배포 패턴
OpenTelemetry Collector 배포 패턴
Sidecar 패턴: 각 Pod에 Collector를 사이드카로 배포. 격리 우수, 리소스 오버헤드 큼.
DaemonSet 패턴: Node당 1개 Collector. 리소스 효율적, 대부분의 환경에 적합.
Gateway 패턴: 중앙 Collector 클러스터. 대규모 환경, 복잡한 라우팅/변환 필요 시.
실무에서는 DaemonSet(수집) + Gateway(처리/라우팅)의 2-tier 구성을 많이 사용합니다.
SLI/SLO/SLA와 에러 버짓
SLI/SLO/SLA와 에러 버짓
- SLI (Service Level Indicator): 측정 가능한 서비스 품질 지표 (예: 가용성 99.95%)
- SLO (Service Level Objective): 내부 목표 (예: “99.9% 가용성 유지”)
- SLA (Service Level Agreement): 고객과의 계약 (예: “99.5% 미만 시 크레딧 제공”)
- 에러 버짓: SLO와 100% 사이의 여유분. 99.9% SLO라면 월 43.2분의 장애 허용 에러 버짓이 남아있으면 배포 속도를 높이고, 소진되면 안정성에 집중하는 것이 SRE의 핵심 원칙입니다.
Grafana 대시보드 설계 베스트 프랙티스
Grafana 대시보드 설계 베스트 프랙티스
- USE Method (리소스): Utilization, Saturation, Errors — CPU, 메모리, 디스크, 네트워크
- RED Method (서비스): Rate, Errors, Duration — API 엔드포인트별
- Four Golden Signals: Latency, Traffic, Errors, Saturation — Google SRE 책 기반 대시보드 구성 순서: 최상단에 서비스 전체 상태 → 중간에 개별 서비스 상세 → 하단에 인프라 리소스. 한 대시보드에 Panel 20개 이하로 유지하고, 드릴다운 링크로 상세 대시보드를 연결하세요.
LLM 관찰성 도구: LangSmith, Langfuse, Phoenix
LLM 관찰성 도구: LangSmith, Langfuse, Phoenix
전통적인 APM 도구는 LLM 특화 메트릭을 지원하지 않습니다. 전용 도구를 고려하세요:
- LangSmith: LangChain 공식, 프롬프트 추적/평가, SaaS
- Langfuse: 오픈소스, 셀프호스팅 가능, OpenTelemetry 호환
- Phoenix (Arize): 오픈소스, 트레이싱 + 평가, LLM 특화 시각화 셀프호스팅이 필요하면 Langfuse, LangChain 생태계면 LangSmith를 추천합니다.
체크리스트
- 모니터링과 관찰성의 차이를 설명할 수 있는가
- Counter, Gauge, Histogram, Summary 메트릭 타입을 구분할 수 있는가
- 기초 PromQL(
rate,histogram_quantile,sum by)을 작성할 수 있는가 - 구조화 로그(JSON)의 장점과 필수 필드를 설명할 수 있는가
- Trace, Span, Trace ID의 관계를 이해하고 있는가
- OpenTelemetry의 SDK/Collector 아키텍처를 설명할 수 있는가
- Prometheus + Grafana 기본 스택을 구성할 수 있는가
- 행동 가능한 알림의 원칙을 이해하고 있는가
- TTFT, TPS 등 LLM 특화 메트릭의 의미를 설명할 수 있는가
- USE/RED Method를 활용한 대시보드 설계 원칙을 알고 있는가

