architecture/sw architecture

[OpenTelemetry] observability 아키텍쳐

방푸린 2025. 2. 25. 10:54
반응형

고려사항

ASIS 고려

  • 각 서버에서 요청 시 수집 서버를 호출하는 방식 말고 쌓인 로그 파일을 수집하여 수집 서버에서 로그를 분석하는 방식을 우선적으로 고려
  • 기존에 그라파나, 프로메테우스 설정이 되어 있으니 필요 시 이를 활용할 수 있는 방안을 고려
  • 기존 PIS 알림 방식이 가능한지 고민(특정 에러가 1분 안에 5번 이상 호출 시 알람 발생 등)

요청의 흐름에 대한 모니터링이 쉽게 되었으면 좋겠다고 생각함

  • trace id는 프론트에서 생성해서 헤더에 심어 백엔드로 전파하는게 제일 좋을 것 같음
  • 백엔드는 헤더에 trace id가 있으면 이걸 다음 컴포넌트에게 전파, 없으면 생성하여 헤더에 심어서 전파
    • 추가적인 의미있는 정보: global trace id, span id, user key...
  • 이걸 모듈화(기존 log module을 활용하여)하면 좋겠다는 생각..
  • was, ws 간 로그 포맷 정형화 필요
    • springboot 로그에서도 심지만 nginx 등 웹서버, 디비 호출, 인프라(loadbalancer) 등 에서도 trace id, 유저 구분자 등 활용 필요

Observability

로그나 실시간으로 수집되고 있는 모니터링 지표와 같은 출력을 통해 시스템의 상태를 이해할 수 있는 능력

  • 시스템/어플리케이션의 내부 상태를 이해 -> 원인/문제를 진단(디버깅) -> 성능을 최적화하는 능력

측정 데이터

  1. 메트릭 (Metrics)
    • 설명: 성능 지표. 시간에 따른 수치 데이터를 측정하여 시스템의 성능을 모니터링하기 위한 데이터
      • CPU 사용량, 메모리 소비, 요청 수 등의 지표를 포함
    • 도구 예시: Prometheus, Grafana
  2. 로그 (Logs)
    • 설명: 시간 기반 텍스트, 애플리케이션과 시스템의 이벤트에 대한 기록. 구조화된 로그 필요
    • 도구 예시: Elasticsearch, Loki
  3. 트레이스 (Traces)
    • 설명: 데이터가 흘러가는 전체적인 경로(큰그림)
      • 많은 시스템을 거쳐가는 분산 시스템에서 요청의 흐름을 추적하여 성능 병목 현상을 식별할 수 있음
      • Trace ID 기반으로 로그-트레이스 연결 가능
    • 도구 예시: Jaeger, Zipkin, Tempo

OpenTelemetry(OTel)


OpenTelemetry은 Traces, Metrics, Logs 같은 데이터를 instrumenting, generating, collecting, exporting 할 수 있는 Observability Framework

  • 오픈소스, 클라우드 네이티브 컴퓨팅 재단(CNCF, Cloud Native Computing Foundation) 프로젝트
  • 분산 추적(Distributed Tracing) 및 모니터링을 위한 표준을 제공
  • 벤더 종속적이지 않음, 큰 틀을 제공
  • OpenTelemetry는 Spring Boot와 잘 호환되는 APM(Application Performance Monitoring) 솔루션, 자동 계측 가능

위 프래임워크와 함께 선택한 기술 스택

  • LGT(M)

제안하는 아키텍쳐

OpenTelemetry Collector

설치 방식: 바이너리 다운로드 / Docker / Kubernetes(Helm Chart) 중 선택

Collector는 꼭 필요한가?

  • 애플리케이션이 많을 때 → 모든 서비스가 개별적으로 Tempo랑 연결하는 것보다 효율적
  • 샘플링, 필터링이 필요할 때 → Collector에서 간편하게 설정 가능. 오류 발생한 Trace만 Tempo로 보낼 수 있음
  • 다른 백엔드로도 보내야 할 때 → Tempo뿐만 아니라 Zipkin, Jaeger, Loki 등에도 동시에 전송 가능


Collector는 필수가 아님, 하지만 확장성을 고려하면 강력한 도구!
대규모 MSA 환경에서는 Collector가 필수!

직접 구현해도 되나?

Java로 OpenTelemetry Collector 구현 가능

  • OpenTelemetry가 공식적으로 제공하는 proto 정의 파일을 기반으로 Java 코드를 생성해야 함

하지만… 일반적인 방식은 아니며 비효율적일 수도 있음

  • 기존 Go 기반 OpenTelemetry Collector보다 성능 저하 가능성 있음.
  • 기능 추가 및 유지보수가 어려움 (기본 OpenTelemetry Collector는 이미 다양한 Exporter 제공).
  • 프로토버프 버전 관리 및 업데이트 부담.

그래도 직접 Java로 Collector를 만들고 싶다면?

  • ProtoBuf를 이용해 OTLP 데이터 처리
  • gRPC 서버로 수신 후 필요한 백엔드로 Export
  • 필요한 Receiver, Processor 및 Exporter를 추가 개발

결론: 가능하지만 OpenTelemetry 공식 Collector를 사용하는 것이 더 현실적!

내부 데이터 흐름 (Receiver → Processor → Exporter)

  • Receiver(수집기): 외부 시스템(애플리케이션, 에이전트, 다른 Collector 등)에서 데이터를 수신예시: OTLP, Jaeger, Zipkin, Prometheus, Loki 등 다양한 수집기 지원
  • Processor(처리기): 데이터를 필터링, 배치 처리, 속성 추가 등의 변환 작업 수행예시: batch, filter, transform 등 다양한 프로세서 사용 가능
  • Exporter(전송기): 데이터를 최종 모니터링 시스템(Grafana Tempo, Prometheus, Loki 등)으로 전송
    • 예시: Tempo, Zipkin, Jaeger, Loki, Prometheus 등 다양한 Exporter 지원

 

collector 설정은 yaml로

log, trace, metric 각각은 파이프라인으로 연결

설정 예시

receivers:
  otlp:
    protocols:
      grpc: "0.0.0.0:4317"  # gRPC 기본 포트
      http: "0.0.0.0:55681"  # HTTP 기본 포트
  loki:
    endpoint: "http://loki:3100"
  prometheus:
    config:
      scrape_configs:
        - job_name: 'otel-metrics'
          static_configs:
            - targets: ['localhost:9090']

processors:
  batch:     # 배치로 전송
    timeout: 10s
  attributes:
    actions:
      - key: "http.status_code"
        value: "404"
        action: "drop"  # 404 응답 코드가 포함된 트레이스나 로그를 드롭
  filterlogs:
    match:
      log:
        severity: ERROR  # ERROR 로그만 필터링

exporters:
  otlp:
    endpoint: "http://tempo:4317"
  loki:
    endpoint: "http://loki:3100"
  prometheus:
    endpoint: "http://prometheus:9090"
  logging:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [attributes, batch]
      exporters: [logging, otlp]
    logs:
      receivers: [loki]
      processors: [filterlogs, batch]
      exporters: [loki, logging]
    metrics:
      receivers: [prometheus]
      processors: [batch]
      exporters: [prometheus]
 



로그 전송기 - Promtail

중간에 Promtail 안 쓰고 바로 collector로 연결한다면?

Promtail을 사용하지 않으면, 애플리케이션이 직접 로그를 OpenTelemetry Collector로 전송해야 함.

  1. Collector가 로그 파일을 직접 읽어 Loki로 전송하는 방식
    • 로그 포맷(구조화 로그 등)을 사전에 맞춰야 함
  2. 애플리케이션에서 직접 Collector로 Push (OTLP 사용)
    • 파일 기반이 아니라 애플리케이션 내부에서 생성된 로그를 바로 전송 가능

기존 로그 파일을 그대로 활용하고 싶다면 Promtail → Collector → Loki

  • Promtail에서도 로그 포매팅 가능
  • Collector에서도 로그 변환 가능
  • Loki에서도 포맷 조정 가능

 

Log 저장소 - Loki

Loki는 로그 수집, 저장, 쿼리를 위한 오픈 소스 로그 집계 시스템으로 별도의 데이터베이스 없이 파일 시스템이나 클라우드 스토리지, 객체 저장소 등을 사용하여 로그를 저장

참고: 왜 Loki?

Metric 저장소 - Prometheus

Prometheus는 수집된 메트릭 데이터를 저장하고 이를 쿼리할 수 있는 중앙 데이터베이스 역할을 하기 위한 것

꼭 있어야 하나?

springboot actuator prometheus 사용 시..
콜랙터에서 바로 그라파나와 연결하여 실시간 metric 확인 가능

다만 사용 시 아래의 혜택을 얻을 수 있음

  • 메트릭 저장을 통한 장기적인 메트릭 분석 가능
  • 고급 쿼리 기능 활용
  • 알림 시스템 지원

 

Trace 저장소 - Tempo

Tempo 없이 Loki만으로 분산 추적이 가능할까?

  • Tempo 없이 완전한 분산 추적은 어려움
  • Trace ID를 로그에 남기면 Trace ID가 포함된 로그를 검색할 수는 있지만, 서비스 간 호출 관계(Span, Parent-Child 관계)는 분석 불가
  • Tempo는 Trace 간 시간 흐름을 시각화하여, 어느 서비스가 느린지, 어디에서 지연이 발생하는지 확인 가능. Loki는 단순한 텍스트 로그 검색이라 이런 분석 불가

 

Tempo는 기본적으로 Push 방식(Polling 지원 X) 관련하여 아래 설정 가능

  • Head-based sampling: 모든 요청을 추적하지 않고 일부만 추적(확률 설정) - 기본값
  • Tail-based sampling: 모든 요청을 수집하지만 collector에서 특정 조건을 만족하는 요청만 저장하도록 설정(필터링 사용) / 어플리케이션 성능에 영향 없음
    • 정상 요청은 버리고, 오류만 저장하도록 설정할 수도 있음
    • 배치 설정: 일정량 쌓이면 한번에 Push하도록 설정
  • Always on sampling: 모든 요청을 100% 저장, 데이터 저장 비용 증가 가능


참고: 다른 trace 저장소와 비교

알람 관련

Grafana 방식

  • Grafana는 Prometheus/Loki에서 데이터를 가져와 알람(Alert)을 설정 가능(템포 x)

장점

  • UI로 알람을 설정할 수 있어 편리
  • Alertmanager 없이 바로 메일/Slack/Webhook 전송 가능

단점

  • 중앙 집중형 관리 어려움
  • 코드 기반 관리 불가능
  • 확장성 없음

 

Prometheus Ruler + Alertmanager 방식이 가장 표준적인 방식

(Loki, Premetheus, Tempo) → Prometheus Ruler → Alertmanager → Email/Slack/Webhook
 


Prometheus Ruler?

  • Prometheus Ruler는 Prometheus 서버와 함께 동작(내장됨)하며, 알림 규칙(Alerting Rules)을 관리하는 기능을 제공
  • Prometheus Ruler는 알람 규칙(Alerting Rules)을 처리하고, 이 규칙이 Trigger되면 Alertmanager에 알림을 보냄
  • yaml 설정으로 관리

Alertmanager?

  • Prometheus 및 Loki, Tempo 등에서 발생한 알람을 관리하고, 이메일, Slack, PagerDuty 등의 채널로 알림을 전송하는 역할을 하는 도구
  • 알람 수신 및 라우팅, 집계, mute, 알람 중복 방지 등 기능이 있음
  • 프로메테우스 설정에서 ruler를 사용하도록 설정 후 별도 파일(yaml)로 설정 관리


Loki에서 직접 알람 가능?

  • Loki 자체적으로는 알람을 트리거할 기능이 제한적
  • logql 쿼리를 사용하여 Prometheus Ruler에서 감지 후 Alertmanager로 전송하는 방식이 일반적

Prometheus에서 직접 알람 가능?

  • Prometheus는 자체적으로 Prometheus Ruler를 통해 알람을 감지 가능
  • 하지만 Alertmanager 없이 직접 알람을 보낼 수 없음

Tempo에서 직접 알람 가능?

  • Tempo는 직접적인 알람 기능이 없음
  • Trace 기반으로 메트릭을 생성한 후 Prometheus Ruler를 통해 감지하는 방식 사용


Prometheus Ruler + Alertmanager를 사용 시 장단점

장점

1. 유연한 알림 라우팅

  • Alertmanager는 알림을 '라벨' 기반으로 라우팅할 수 있어서, 다양한 알림 조건에 대해 수신자를 유연하게 지정할 수 있음.
  • 예를 들어, severity, project, team과 같은 라벨을 기반으로 알림을 각기 다른 수신자 그룹(메일, 슬랙, 웹훅 등)으로 전달할 수 있음.
  • 라벨을 이용하여 프로젝트별로 혹은 환경 별로 다양한 알림 조건을 설정할 수 있음

2. 알림 집계 및 수집

  • Alertmanager는 동일한 경고에 대해 여러 번 알림을 보내지 않도록 알림을 집계하고 알림 그룹화 기능을 제공
  • 예를 들어, 여러 번 발생하는 동일한 경고를 하나의 알림으로 묶어서 처리할 수 있음

3. 알림 수신 채널 다채로움

  • 알림을 다양한 채널(이메일, 슬랙, 페이지듀티, SMS 등)로 전송할 수 있음.
  • Alertmanager는 알림을 설정한 대로 다양한 형식으로 전송할 수 있는 기능을 제공함

4. 정밀한 알림 조건 설정

  • Prometheus Ruler에서 제공하는 고급 알림 규칙 설정을 통해, 알림 조건을 세밀하게 정의할 수 있음.
  • 예를 들어, 특정 메트릭이 1분 동안 특정 값을 초과하거나, 특정 상황이 반복되는 경우에만 알림을 보내는 식으로 알림의 발생 조건을 세밀하게 조정할 수 있음.


단점

1. 설정이 복잡함

  • 설정하는 화면이 없고 yaml  파일을 작성하는 방식
  • 여러 팀이나 프로젝트별로 맞춤형 알림을 설정하는 경우, 설정 파일이 방대해질 수 있으며, 이를 관리하기 어려울 수 있다.

2. 리소스 요구사항

  • Prometheus Ruler와 Alertmanager는 각각 다른 시스템과 연동되어야 하므로, 시스템 자원의 관리가 필요함. Prometheus의 수집 데이터 양이 많아지면 알림 평가에 드는 시간과 리소스가 커질 수 있다.
  • 알림을 너무 많이 생성하거나 복잡한 계산을 수행하면 시스템에 부하를 줄 수 있음

도입 시 각 어플리케이션 수정 양은?

1. 아래 의존성 추가

// 애플리케이션에서 메트릭, 트레이스, 로그와 같은 관측 데이터를 수집하는 데 필요한 인터페이스를 제공; trace id 생성
implementation 'io.opentelemetry:opentelemetry-api:${version}'

// OpenTelemetry API의 구현체로, 실제 데이터를 수집하고 처리하는 기능을 제공; 데이터 수집
implementation 'io.opentelemetry:opentelemetry-sdk:${version}'

// Traces, Metrics, Logs 데이터를 OTLP(HTTP/gRPC) 프로토콜을 통해 Collector로 전송; 외부로 전송
implementation 'io.opentelemetry:opentelemetry-exporter-otlp:${version}'
 

2. tracer 빈 등록 - 콜렉터 정보 등록
3. 로그백 설정

  • logback.xml 파일에서 MDC(Mapped Diagnostic Context)를 사용하여 트래스 아이디와 스팬 아이디를 자동으로 포함시킬 수 있음
  • 받은 요청에 트래스(Trace) 아이디가 있으면, 이미 존재하는 트래스 아이디를 그대로 사용하고, 새로운 스팬(Span)을 생성한다. 만약 요청에 트래스 아이디가 없다면, 새로운 트래스 아이디를 생성하고 이를 기반으로 스팬을 생성한다.
  • 소스에서 수동으로도 트래이싱 정보 추가 가능(마킹 가능)

참고

오텔 설명
https://medium.com/@dudwls96/opentelemetry-%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-18b6e4fe6e36
https://www.anyflow.net/sw-engineer/opensource-observability
위에서 제안한 아키텍쳐와 비슷한 구조로 세팅하는 과정 설명
https://blog.nashtechglobal.com/setup-observability-with-open-telemetry-prometheus-loki-tempo-grafana-on-kubernetes/

[토스] observability 시스템 구축 시 고려해야하는 사항들
https://youtu.be/Ifz0LsfAG94?si=cYAPtvm8eRy0Srk- 
[nhn forward] nhn cloud가 구축한 사용 예시
https://youtu.be/EZmUxMtx5Fc?si=_YtHU2mDayS2uxKr

728x90
반응형