
엊그제 일하던 중 책임님이 갑자기 나를 찾으셨다..
사내에서 모니터링 시스템을 구축하고 있는 중이셨는데 local 환경에선 jaeger가 잘 붙었지만 dev 환경에서는 안붙는다고 새로운 시선이 필요하시다며 도움을 요청하셨다.
처음에는 단순히 dev 환경의 ec2가 인터넷이 안돼서 그런줄로 알았지만, Spring Gateway로 라우팅이 돼있고, ec2의 보안그룹도 jaeger가 사용하는 port가 다 뚫려있는 상태라서 그런 문제는 아니였다.
한참을 헤메다가 결국 해결했고, 그 과정을 적어보겠다.
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=jaeger \
-Dotel.exporter.jaeger.endpoint=http://SpringGatewayUrl/trace \
-Dotel.resource.attributes=service.name=test,compose_service=test \
-Dotel.jaeger.service.name=test \
-jar test-0.0.1-SNAPSHOT.jar
OpenTelemetry Javaagent failed to start
io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException: otel.traces.exporter set to "jaeger" but opentelemetry-exporter-jaeger not found on classpath. Make sure to add it as a dependency.
at io.opentelemetry.sdk.autoconfigure.SpanExporterConfiguration.configureExporter(SpanExporterConfiguration.java:96)
at io.opentelemetry.sdk.autoconfigure.SpanExporterConfiguration.configureSpanExporters(SpanExporterConfiguration.java:68)
at io.opentelemetry.sdk.autoconfigure.TracerProviderConfiguration.configureTracerProvider(TracerProviderConfiguration.java:58)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.build(AutoConfiguredOpenTelemetrySdkBuilder.java:465)
at io.opentelemetry.javaagent.tooling.OpenTelemetryInstaller.installOpenTelemetrySdk(OpenTelemetryInstaller.java:29)
at io.opentelemetry.javaagent.tooling.AgentInstaller.installBytebuddyAgent(AgentInstaller.java:122)
at io.opentelemetry.javaagent.tooling.AgentInstaller.installBytebuddyAgent(AgentInstaller.java:102)
at io.opentelemetry.javaagent.tooling.AgentStarterImpl.start(AgentStarterImpl.java:99)
at io.opentelemetry.javaagent.bootstrap.AgentInitializer$1.run(AgentInitializer.java:53)
at io.opentelemetry.javaagent.bootstrap.AgentInitializer$1.run(AgentInitializer.java:47)
at io.opentelemetry.javaagent.bootstrap.AgentInitializer.execute(AgentInitializer.java:68)
at io.opentelemetry.javaagent.bootstrap.AgentInitializer.initialize(AgentInitializer.java:46)
at io.opentelemetry.javaagent.OpenTelemetryAgent.startAgent(OpenTelemetryAgent.java:57)
at io.opentelemetry.javaagent.OpenTelemetryAgent.premain(OpenTelemetryAgent.java:45)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:560)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:572)
위와 같이 코드를 실행하면 프로젝트가 실행되면서 아래와 같은 에러를 내뿜었다.
opentelemetry-exporter-jaeger가 class-path에 포함되지 않았다고 했는데, 필요한 세팅은 다 해주어서 하나하나 체크해봤다.
jaeger에서 매트릭을 수집하는 14250 port는 gateway에서 라우팅을 해줬고 코드 상 문제가 없어 보였다.
implementation 'io.opentelemetry:opentelemetry-exporter-jaeger:1.34.1'
또한, 구글링을 해도 위와 같은 종속성을 넣으면 해결된다했는데 이미 책임님이 시도하셨던 방법이였고 역시나 되지않았다.
opentelemetry-exporter-jaeger 이놈을 class-path에 추가하기 위해 여러가지 방법을 시도했다.
fat-jar 파일을 만들어보기도 했고 이것 저것 시도해보다가 안됐다.
그때 책임님이 local에서는 분명 잘됐다고 하신 말씀이 떠올랐다.
local 환경에서 jaeger를 구축하고 테스트해보기로 했다.
일단 docker-compose를 이용해서 jaeger all-in-one을 설치했다.
이 이미지는 jaeger의 모든 구성요소를 단일 컨테이너에 넣은 것이다.
version: '3.9'
services:
jaeger:
platform: linux/amd64
image: jaegertracing/all-in-one:1.59
container_name: jaeger_1_59
volumes:
ports:
- "16686:16686"
- "6831:6831/udp"
- "6832:6832/udp"
- "4317:4317"
- "4318:4318"
- "9411:9411"
- "14250:14250"
expose:
- 16686
- 6381
- 6832
- 4317
- 4318
- 9411
- 14250
environment:
...
extra_hosts:
- 'host.docker.internal:host-gateway'
networks:
...
mem_limit: "2g"
// 일부 내용 생략
-javaagent:/path/to/opentelemetry-javaagent.jar
-Dotel.traces.exporter=jaeger
-Dotel.exporter.jaeger.endpoint=http://localhost:14250
-Dotel.resource.attributes=service.name=test,compose_service=test
-Dotel.jaeger.service.name=test
localhost에 jaeger 컨테이너를 띄우고 vm option에 다음과 같이 넣어 실행하니 jaeger에 데이터가 전송됐다.
하지만 VM option에 넣어 실행하면 되는데 똑같은 옵션으로 build를 해서 -jar로 실행하면 맨처음에 오류와 같이 뜨면서 jaeger에 데이터를 전송하지 못하는 문제가 발생했다!
java -javaagent:/path/to/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=jaeger \
-Dotel.exporter.jaeger.endpoint=http://localhost:14250 \
-Dotel.resource.attributes=service.name=test,compose_service=test \
-Dotel.jaeger.service.name=test \
-jar test-0.0.1-SNAPSHOT.jar
여기서 두 가지의 차이점을 알면 해결할 수 있을까라고 생각했고 Intellij가 어떻게 프로젝트를 실행하는지 분석해봤다.
프로젝트를 실행하니 맨 위에 실행구문 같은 게 있었다.
이 구문을 복사해서 메모장에 붙여서 분석하니 다음과 같은 구조로 프로젝트를 실행하고 있었다.
/Path/to/Library/Java/JavaVirtualMachines/corretto-21.0.3/Contents/Home/bin/java \
-javaagent:/Path/to/opentelemetry/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=jaeger \
-Dotel.exporter.jaeger.endpoint=http://localhost:14250 \
-Dotel.resource.attributes=service.name=test,compose_service=test \
-Dotel.jaeger.service.name=test \
-XX:TieredStopAtLevel=1 \
-Dspring.output.ansi.enabled=always \
-Dcom.sun.management.jmxremote \
-Dspring.jmx.enabled=true \
-Dspring.liveBeansView.mbeanDomain \
-Dspring.application.admin.enabled=true \
-Dmanagement.endpoints.jmx.exposure.include=* \
-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=55792:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 \
-Dsun.stdout.encoding=UTF-8 \
-Dsun.stderr.encoding=UTF-8 \
-classpath /Path/to/IdeaProjects/test/build/classes/java/main:/Path/to/IdeaProjects/test/build/resources/main:각종외부종속성... \
com.package.TestApplication
구조는 java / VM option / 각종 intellij 옵션 / classpath로 외부 종속성 및 main calss / 메인 클래스 였다.
몇가지 실행 안되는 구문들을 빼고 실행하니 localhost jaeger에 데이터를 전송할 수 있었다.
여기서 어떤 차이가 있는지 분석했고, classpath 부분에서 힌트를 찾았다.
path/to/.gradle/caches/modules-2/files-2.1/io.opentelemetry.javaagent/opentelemetry-javaagent/1.28.0/dd05936291e121dfad1fd994f2b719be5898c238/opentelemetry-javaagent-1.28.0.jar
외부 종속성 부분의 코드를 하나씩 지우면서 어느 부분이 사라지면 똑같은 에러가 발생하는지 봤는데 이부분이 포함되어야 에러가 발생하지 않았다.
이 부분을 -jar 구문에 붙여넣기 하고 실행해봤는데, -jar 와 class-path와는 서로 연동되지 않는다고 해서 다른 방법을 찾았다.
근데 어딘가 익숙한 jar파일인가 했는데 vm option으로 넣어주는 opentelemetry-javaagent 였던 것이였다!
이 사실을 안 뒤에 해당 외부 종속성을 찾아갔고 경로를 열어서 파일을 추출했다.
jar파일로 javaagent 항목의 javaagent를 바꿔주니 jar 파일로도 실행이 가능했다.
java -javaagent:/path/to/opentelemetry-javaagent-1.28.0.jar \
-Dotel.traces.exporter=jaeger \
-Dotel.exporter.jaeger.endpoint=http://SpringGatewayUrl/trace \
-Dotel.resource.attributes=service.name=test,compose_service=test \
-Dotel.jaeger.service.name=test \
-jar test-0.0.1-SNAPSHOT.jar
그리고 ec2에 해당 jar를 sftp로 밀어넣고 실행하니 게이트웨이를 통해 jaeger에 연결됐다!
결국 opentelemetry javaagent의 버전이 안맞아서 발생한 문제였다.
한국어로 된 메뉴얼도 많이 없어서 힘들었지만, 이슈를 해결하면서 짜릿한 감정과 업무에 도움이 된 것 같아 뿌듯함도 느꼈다.
이 글을 읽는 분들도 저런 에러를 마주친다면 위와 같은 방법으로 해결하길 바라며 블로그에 글을 남긴다.
별거 아닌 문제였지만, 에러가 불친절해서 오래 걸렸던 것 같다.
--끝--