옵저버빌리티 스택 도입 PRD
배경 (Background)
stock-application은 하이브리드 모노레포다. 요청 한 건이 frontend(Next.js) → aggregator(Spring BFF) → backend(Spring) → ml(FastAPI)의 4개 서비스를 거치고, Toss Open API 같은 외부 호출까지 동반한다. 그러나 현재 운영 가시성은 다음과 같이 비어 있다.
- 계측 기반 전무 — backend·ml·aggregator·frontend 어디에도 Micrometer/Actuator/OpenTelemetry/Prometheus가 없다. 어떤 메트릭도 노출되지 않는다.
- 분산 추적 부재 — 요청이 여러 서비스를 거치지만 하나의 trace로 엮이지 않는다. 느린 요청의 병목이 어느 서비스·어느 span인지 알 수 없다.
- 인프라 메트릭 부재 — MySQL(Docker, :3308)만 실재하고, Kafka·Redis는 아직 도입 전이라 메트릭 수집 대상조차 없다.
장애·지연 분석을 로그 grep과 추측에 의존하는 상태다. Datadog 수준의 “요청 단위 trace + span + latency + 메트릭”을 단일 UI에서 보는 옵저버빌리티 기반이 필요하다.
목표
- 모든 분산 시스템(frontend·aggregator·backend·ml + Kafka consume)을 하나의 trace로 연결해 요청 단위로 span·latency를 UI에서 추적한다.
- application(Spring 2종·Python 1종)의 전 메트릭(JVM/HTTP/DB 풀/처리량·에러율·지연)을 수집·시각화한다.
- infrastructure(MySQL·Kafka·Redis) 3종을 모두 도입하고 각 exporter로 전 메트릭을 수집·시각화한다.
- 동일 텔레메트리를 SigNoz와 Grafana LGTM 두 스택에 동시 전송해 PoC로 비교하고, 평가 결과를 ADR로 확정해 운영 스택 1개를 선택한다.
요구사항
| ID | 요구사항 | 우선순위 |
|---|---|---|
| R-01 | frontend·aggregator·backend·ml의 요청 경로가 단일 trace로 연결되어 UI에서 span·latency를 본다 | P0 |
| R-02 | Kafka produce→consume 경로가 동일 trace에 span으로 표시된다 (message consume 추적) | P0 |
| R-03 | Spring(backend·aggregator) 전 메트릭(JVM·HTTP server/client·HikariCP·GC)을 수집한다 | P0 |
| R-04 | Python(ml/FastAPI) 전 메트릭(요청 처리량·지연·에러율·외부 호출)을 수집한다 | P0 |
| R-05 | MySQL·Kafka·Redis 인프라 메트릭을 exporter로 수집한다 | P0 |
| R-06 | 계측은 벤더 중립(OpenTelemetry/OTLP)으로 통일해 백엔드 스택 교체에 코드 변경이 없도록 한다 | P0 |
| R-07 | 동일 텔레메트리를 SigNoz·Grafana 두 스택에 동시 전송해 같은 조건으로 비교한다 | P0 |
| R-08 | 두 스택을 평가 축으로 비교한 결과를 산출하고 운영 스택을 ADR로 결정한다 | P0 |
| R-09 | frontend(Next.js) 서버 런타임에서 요청이 trace에 합류한다 | P1 |
| R-10 | 임계치 위반 시 Discord webhook으로 알림을 보낸다 | P1 |
| R-11 | 전체 스택이 로컬 docker-compose 한 번으로 기동된다 | P1 |
| R-12 | dev에서 이미 쓰는 Grafana Loki와의 로그 연속성을 비교 축에 포함한다 | P2 |
사용자 시나리오
시나리오 1: 느린 요청의 병목 찾기
- 사용자가 추천 목록을 요청하면 frontend → aggregator → (backend 손익 + ml 예측 병렬) → 응답이 발생한다.
- 운영자는 옵저버빌리티 UI에서 해당 trace 하나를 연다.
- 각 서비스의 span과 latency를 보고 ml 예측 span이 6초로 병목임을 즉시 식별한다.
시나리오 2: 인프라 메트릭 점검
- 운영자가 대시보드에서 MySQL 커넥션 수·Kafka consumer lag·Redis 메모리 사용량을 한 화면에서 본다.
- 임계치 초과 시 Discord로 알림을 받는다.
시나리오 3: 스택 비교(PoC)
- 동일 요청 부하를 발생시켜 SigNoz와 Grafana 양쪽에 동일 텔레메트리가 들어간다.
- 운영자는 평가 축(단일 UI 경험·trace 연결 품질·계측 호환성·exporter 커버리지·로컬 메모리·생태계 성숙도·Loki 연속성)으로 양쪽을 비교한다.
- 비교표를 근거로 운영 스택 1개를 ADR로 확정한다.
세부 정책
정책 1: 계측 표준은 OpenTelemetry로 통일
모든 서비스는 OTLP로만 텔레메트리를 내보낸다. 벤더 종속 에이전트(Datadog agent 등)를 쓰지 않는다. 백엔드 스택을 SigNoz↔Grafana로 바꿔도 서비스 코드·설정은 OTLP 엔드포인트만 바뀐다.
정책 2: 동일 텔레메트리 fan-out으로 공정 비교
서비스는 단일 OpenTelemetry Collector로만 전송하고, Collector가 SigNoz와 Grafana 양쪽으로 fan-out한다. 서비스가 두 백엔드를 직접 알지 않게 해, 두 스택이 완전히 동일한 입력을 받도록 보장한다.
정책 3: Kafka·Redis 신규 도입
모니터링 대상 3종을 모두 갖추기 위해 Kafka·Redis 컨테이너를 도입한다. 단 도메인 코드가 아직 Kafka를 쓰지 않으므로, message-consume 추적은 최소 샘플 produce→consume 경로로 검증한다(실제 도메인 Kafka 사용은 본 과제 범위 밖).
정책 4: 민감 정보·포트 기존 관례 준수
Discord webhook URL 등 민감 정보는 ~/.zshrc 환경변수로만 주입한다. 신규 컨테이너 포트는 기존 점유(3306/3307/3308 등)와 충돌하지 않게 배정한다.
범위 외
- 도메인 기능에 Kafka를 실제로 적용하는 작업 (이벤트 기반 아키텍처 전환)
- 프로덕션(원격 서버) 옵저버빌리티 배포 — 본 과제는 로컬 docker-compose 한정
- 브라우저(클라이언트) RUM(Real User Monitoring) 풀 계측 — frontend는 서버 런타임 trace 합류까지만
- 로그 장기 보존·비용 최적화 정책
관련 문서
- TDD · ADR-001 · STK-OBS-01