https://opentelemetry.io/docs/concepts/observability-primer/#what-is-observability
관측가능성이란 시스템 내부에서 무슨 일이 발생하고 있는지, 시스템 외부에서 이해할 수 있도록 도와준다.
이를 위해서는 trace, 매트릭스, 로그와 같은 외부로 보낼 신호를 생성해야한다.
openTelemetry는 Observability를 위한 프레임워크로, 생성 전송 수집에 용이하게 하기 위해 제작되었다.
로그는 타임스탬프가 찍힌 메시지이다. 과거부터 많이 사용되어왔지만 호출 위치 등 문맥정보가 부족하여 완벽한 추적이 어렵다. 이러한 로그의 단점 즉, 문맥정보를 부여하기 위해 span과 trace가 도입되었다
한 작업의 단위를 의미하고, 요청 수행을 위한 특정 작업에 대한 문맥 정보를 제공한다.
현대의 웹앱은 마이크로서비스 등 복잡한 아키텍처를 가진다.
분산 추적은 이러한 복잡한 아키텍처 속에서 사용자의 request부터 response까지의 이동경로를 기록하여 추적할 수 있게 도와준다.
trace는 하나 이상의 span으로 구성된 span의 집합이다.
Tracing context는 spring에선 MDC라 불리는 것으로, span과 trace에 대한 문맥정보를 쓰레드, 프로세스, 네트워크를 통해서 전달되어 유지되어야한다.
spring은 Observability를 위해서 spring 3부터 Micrometer Tracing를 사용한다.
spring2까지는 Spring Cloud Sleuth를 사용했지만, trace 시스템이 spring cloud와 종속되어있다는 문제가 있었고 이 문제를 해결하기 위해 micrometer tracing으로 분할되었다.
Micrometer Tracing을 위한 bridge 종속성과 otlp, zipkin 등으로 보낼 exporter 종속성이 필요하다.
OTLP를 사용한 OpenTelemetry에 필요한 종속성은 아래와 같다.
implementation 'io.micrometer:micrometer-tracing-bridge-otel'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
bridge-otel은 마이크로미터 API와 OpenTelemetry를 연결하고
opentelemetry-exporter-otlp는 그렇게 만든 추적정보를 otlp를 사용하여 전송하는 역할을 한다.
zipkin을 사용한다면 opentelemetry-exporter-zipkin을 사용하면 추적정보를 zipkin으로 보낼 수 있다.
그러나 우리는 zipkin 대신 grafana tempo을 사용할 것이다.
zipkin이란?
distributed tracing system이다.
트레이스 정보를 저장하기 위해 apache Cassandra 또는 엘라스틱 서치, mysql을 사용한다.
이는 trace 정보를 색인하여 나중에 검색하기 위함인데, 색인과정 및 cassandra 등의 관리 및 운영이 무겁다는 단점이 있다.
우리는 grafana-tempo를 사용할 예정이기 때문에 zipkin을 사용하지 않겠다.
(사실 grafana-tempo도 zipkin을 사용할 수 있다)
grafana tempo를 쓰는 이유는 grafana는 색인을 하지 않기 때문에 cassandra나 엘라스틱서치 등 복잡한 데이터베이스 등을 사용하지 않아서 zipkin보다 가볍다.
또 프로메테우스나 loki 등도 같이 쓸 예정이기도 하고, grafana cloud의 free tier가 생각보다 널널한 걸로 알고 있어서 이를 사용하고자 한다.
grafana tempo는 2버전부터 apache parquet을 사용한다. apache parquet는 column 단위로 데이터를 나누어 저장한다는 특징이 있다. 즉 검색하고자 하는 column에 대한 데이터만 불러와서 확인하는 간단한 방식이라고 요약할 수 있겠다. apache parquet의 자세한 원리는 다음의 링크에서 참고할 수 있다.
https://github.com/julienledem/redelm/wiki/The-striping-and-assembly-algorithms-from-the-Dremel-paper
tempo:
image: grafana/tempo:latest
container_name: tempo
command: [ "-config.file=/etc/tempo.yaml" ]
volumes:
- ./docker/tempo/tempo-config.yaml:/etc/tempo.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "3200:3200" # Tempo internal
loki:
image: grafana/loki
container_name: loki
ports:
- "3100:3100" # 배포시에는 포트를 외부에 노출하지 말 것
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3003:3000"
volumes:
- ./docker/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
- grafana-data:/var/lib/grafana
environment:
- GF_AUTH_ANONYMOUS_ENABLED=false # 익명접근 거부
- GF_AUTH_DISABLE_LOGIN_FORM=false # 로그인 화면
# 관리자 정보
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=password
# 회원가입 금지
- GF_USERS_ALLOW_SIGN_UP=false
depends_on:
- tempo
- loki
management:
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces
tracing:
sampling:
probability: 1
tracing.sampling.proability는 전체 요청 중에 몇개를 tracing할 건지 그 비율을 정하는 것이다.
grafana explore로 가서 trace 내역들을 확인할 수 있다.
사실 지금은 filter before, request, filter after만 span으로 잡히고 있다.
즉 service에 대해서는 span이 만들어지지 않고 있는데
service 단계까지 trace 하고 싶다면 @Observed를 사용하면 된다
@Configuration
public class ObservationConfig {
@Bean
public ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
아까와 달리 theater-service#read-theaters가 생긴 모습을 확이할 수 있다
implementation 'net.ttddyy.observation:datasource-micrometer-spring-boot:1.2.1'
jdbc 차원의 trace가 필요하다면 datasource-micrometer를 사용할 수 있다
기본적으로 datasource-proxy를 사용하여 datasource 내부의 일을 추적할 수 있다.
그래서 spring-boot-data-source-decorator와의 자동 구성과 충돌한다!
spring-boot-data-source-decorator를 사용해서 p6spy 등을 사용하는 사람들은 참고하길 바란다.
아래 이미지 처럼 어떤 쿼리가 날라가는지 확인할 수 있다
페이징 쿼리이기 때문에 조회와 count 쿼리가 날라가는 것을 확인할 수 있다.