paint-brush
Spring Boot에서 OpenTelemetry 추적: Java Agent와 Micrometer 중에서 선택~에 의해@nfrankel
193 판독값

Spring Boot에서 OpenTelemetry 추적: Java Agent와 Micrometer 중에서 선택

~에 의해 Nicolas Fränkel8m2024/08/11
Read on Terminal Reader

너무 오래; 읽다

이 게시물은 Java Agent v1, Java Agent v2 및 Micrometer Tracing을 사용하여 Spring Boot 애플리케이션에서 OpenTelemetry 추적 솔루션을 비교합니다. 구성, 기능 및 추적 기능의 차이점을 강조하여 관찰 가능성을 위한 각 접근 방식의 효과와 적용에 대한 통찰력을 제공합니다.
featured image - Spring Boot에서 OpenTelemetry 추적: Java Agent와 Micrometer 중에서 선택
Nicolas Fränkel HackerNoon profile picture

OpenTelemetry Tracing 데모에는 두 개의 Spring Boot 구성 요소가 있습니다. 하나는 Java 에이전트를 사용하는데, 최근에 v1.x에서 v2.x로 업그레이드했을 때 다른 동작을 발견했습니다. 다른 하나는 GraalVM 네이티브로 컴파일했기 때문에 Micrometer Tracing을 사용하고 있으며 Java 에이전트를 처리할 수 없습니다.



이 글에서는 Java 에이전트 v1, Java 에이전트 v2, Micrometer Tracing의 세 가지 접근 방식을 비교해보고 싶습니다.

기본 애플리케이션 및 인프라

동일한 기본 애플리케이션을 사용하겠습니다. Kotlin으로 코딩된 간단한 Spring Boot 애플리케이션입니다. 단일 엔드포인트를 제공합니다.


  • 엔드포인트 너머의 함수는 entry() 로 명명됩니다.
  • intermediate() 라는 또 다른 함수를 호출합니다.
  • 후자는 RestTemplate 을 대체하는 WebClient 인스턴스를 사용하여 위의 엔드포인트에 대한 호출을 수행합니다.
  • 무한 루프를 피하기 위해 사용자 정의 요청 헤더를 전달합니다. entry() 함수가 해당 헤더를 찾으면 더 이상 진행하지 않습니다.


샘플 앱 시퀀스 다이어그램


이는 다음 코드로 변환됩니다.


 @SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } }


모든 설정에 대해 두 단계를 확인합니다. OpenTelemetry가 활성화된 기본 단계와 추가 내부 범위를 생성하는 사용자 지정 단계입니다.

마이크로미터 추적

마이크로미터 추적은 "공급업체에 중립적인 애플리케이션 관찰성 외관"인 마이크로미터 에서 비롯되었습니다.


Micrometer Tracing은 가장 인기 있는 추적 라이브러리에 대한 간단한 외관을 제공하여 공급업체에 종속되지 않고 JVM 기반 애플리케이션 코드를 계측할 수 있습니다. 추적 작업의 이식성을 극대화하는 동시에 추적 수집 활동에 거의 또는 전혀 오버헤드를 추가하지 않도록 설계되었습니다.


-- 마이크로미터 추적 사이트


마이크로미터 추적을 시작하려면 몇 가지 종속성을 추가해야 합니다.


  • Spring Boot Actuator, org.springframework.boot:spring-boot-starter-actuator
  • 마이크로미터 추적 자체, io.micrometer:micrometer-tracing
  • 대상 추적 백엔드 API로의 "브리지". 제 경우에는 OpenTelemetry이므로 io.micrometer:micrometer-tracing-bridge-otel 입니다.
  • 백엔드에 대한 구체적인 내보내기, io.opentelemetry:opentelemetry-exporter-otlp


Spring Boot 부모에 버전이 이미 정의되어 있으므로 BOM이 필요하지 않습니다.


그러나 두 가지 런타임 구성 매개변수가 필요합니다. 추적을 어디로 보내야 하는지, 그리고 구성 요소의 이름은 무엇입니까. 이는 MANAGEMENT_OTLP_TRACING_ENDPOINTSPRING_APPLICATION_NAME 변수에 의해 관리됩니다.


 services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3
  1. Jaeger에 대한 OpenTelemetry 수집기 활성화
  2. Jaeger OpenTelemetry gRPC 엔드포인트에 대한 전체 URL
  3. OpenTelemetry의 서비스 이름을 설정합니다.


결과는 다음과 같습니다.

사용자 정의가 없는 Jaeger의 마이크로미터 추적


Micrometer는 어떠한 사용자 정의 없이 HTTP 요청을 수신하고 전송할 때 스팬을 생성합니다.


프레임워크는 전송을 위해 RestClient 에 마법을 주입해야 합니다. 우리는 전자가 후자를 인스턴스화하도록 해야 합니다.


 @SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }


우리는 여러 가지 방법으로 수동 스팬을 만들 수 있는데, 그 중 하나는 OpenTelemetry API 자체를 통한 것입니다. 그러나 설정에는 많은 보일러플레이트 코드가 필요합니다. 가장 간단한 방법은 Micrometer의 Observation API 입니다. 주요 이점은 메트릭추적을 모두 관리하는 단일 API를 사용한다는 것입니다.


관찰 API 클래스 다이어그램

업데이트된 코드는 다음과 같습니다.


 class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } }


추가된 관찰 호출은 생성된 추적에 반영됩니다.


Observation API를 사용한 Jaeger의 마이크로미터 추적

OpenTelemetry 에이전트 v1

Micrometer Tracing의 대안은 일반적인 OpenTelemetry Java Agent 입니다. 가장 큰 장점은 코드나 개발자에게 영향을 미치지 않는다는 것입니다. 에이전트는 순수한 런타임 범위의 관심사입니다.


 java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar


에이전트는 환경 변수를 사용하여 OpenTelemetry의 구성을 준수합니다.


 services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080"
  1. 프로토콜, 도메인, 포트를 설정합니다. 라이브러리는 /v1/traces 추가합니다.
  2. OpenTelemetry의 서비스 이름을 설정합니다.
  3. 메트릭이나 로그를 내보내지 마십시오.


더 이상 구성하지 않으면 다음과 같은 추적 결과를 얻게 됩니다.


Agent v1은 사용자 정의 없이 Jaeger에서 추적합니다.


에이전트는 수신 및 전송된 요청 과 Spring 관련 주석으로 표시된 함수를 자동으로 추적합니다. 추적은 호출 스택에 따라 서로 올바르게 중첩됩니다. 추가 함수를 추적하려면 코드베이스에 종속성인 io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations 추가해야 합니다. 이제 이전에 추적되지 않은 함수에 @WithSpan 주석을 달 수 있습니다.

@WithSpan 클래스 다이어그램


value() 부분은 추적의 레이블을 제어하는 반면 kind span.kind 속성으로 변환됩니다. value가 기본값인 빈 문자열로 설정되면 함수의 이름이 출력됩니다. 제 목적상 기본값이 충분합니다.


 @WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }


예상한 새로운 intermediate() 추적이 생성됩니다.

Jaeger에서 주석이 있는 Agent v1 추적

OpenTelemetry 에이전트 v2

OpenTelemetry는 올해 1월에 에이전트의 새로운 주요 버전을 출시했습니다. 저는 데모를 업데이트했습니다. 이제 추적은 앱이 요청을 수신하고 보낼 때만 생성됩니다.

사용자 정의 없이 Jaeger에서 Agent v2 추적


이전 버전과 마찬가지로 @WithSpan 주석으로 추적을 추가할 수 있습니다. 유일한 차이점은 entry() 함수에도 주석을 달아야 한다는 것입니다. 기본적으로 추적되지 않습니다.

Jaeger에서 주석이 있는 Agent v2 추적


논의

Spring은 두 가지 이유로 성공했습니다. 복잡한 솔루션, EJB 2를 간소화하고 경쟁 라이브러리에 대한 추상화 계층을 제공했습니다. Micrometer Tracing은 Zipkin과 Jaeger에 대한 추상화 계층으로 시작되었으며 완전히 합리적이었습니다. 이 주장은 OpenTelemetry가 프로그래밍 언어와 추적 수집기에서 대부분 라이브러리에서 지원됨에 따라 더 이상 의미가 없습니다. Observation API는 여전히 Micrometer Tracing의 상당한 이점인데, Metrics와 Traces에 대한 단일 API를 사용하기 때문입니다.


Java Agent 측면에서 OpenTelemetry 구성은 모든 기술 스택과 라이브러리에서 비슷합니다. 환경 변수입니다. v1에서 v2로 업그레이드했을 때 약간 실망했습니다. 새로운 에이전트는 Spring을 인식하지 못하기 때문입니다. Spring 주석이 달린 함수는 기본적으로 추적되지 않습니다.


결국, 그것은 현명한 결정입니다. 보고 싶지 않은 스팬을 제거하는 것보다 원하는 스팬에 대해 명확하게 말하는 것이 훨씬 낫습니다.


도움과 리뷰를 제공해 주신 Jonatan Ivanov 에게 감사드립니다 .


이 게시물의 전체 소스 코드는 GitHub에서 찾을 수 있습니다.

더 자세히 알아보려면:


원래 2024년 8월 3일 A Java Geek 에 게시됨