리포지토리에는 Docker 컨테이너 내에서 실행되도록 미리 구성된 멀티 서비스 자바에 애플리케이션이 포함되어 있습니다. 샘플 앱은 데이터를 추가하고 변경할 수 있는 REST API가 있는 기본 노트 앱입니다.
이 튜토리얼의 경우 docker-compose YAML 파일은 apm-tutorial-java-host/docker 폴더에 있습니다. 다음 지침은 Agent가 리눅스 호스트에서 실행되고 있다고 가정하므로 service-docker-compose-linux.yaml 파일을 사용합니다. Agent 사이트가 macOS 또는 Windows 호스트에 있는 경우에는 동일한 지침을 따르되 service-docker-compose.yaml 파일을 대신 사용합니다. 리눅스 파일에는 파일 내 주석에 설명된 리눅스 전용 Docker 설정이 포함되어 있습니다.
notes 및 calendar 디렉토리에는 Maven 또는 Gradle로 애플리케이션을 빌드하는 두 세트의 Dockerfile이 있습니다. 이 튜토리얼에서는 Maven 빌드를 사용하지만, Gradle에 더 익숙하다면 해당 빌드 명령의 변경 사항을 사용하여 Maven 빌드를 사용할 수 있습니다.
참고: 이 샘플 명령의 플래그, 특히 샘플 속도는 이 튜토리얼이 적용되지 않은 환경에는 적합하지 않을 수 있습니다. 실제 환경에서 어떤 플래그를 사용해야 하는지에 대해 살펴보려면 추적 설정을 참고하세요.
유니버설 서비스 태그는 다양한 버전 및 배포 환경에서 추적된 서비스를 식별하여 Datadog 내에서 상호 연관시킬 수 있으므로 이를 사용하여 검색 및 필터링할 수 있습니다. 통합 서비스 태그에 사용되는 세 가지 환경 변수는 DD_SERVICE, DD_ENV, DD_VERSION입니다. Docker로 배포된 애플리케이션의 경우 이러한 환경 변수를 Dockerfile 또는 docker-compose 파일 내에 추가할 수 있습니다.
이 튜토리얼의 경우 service-docker-compose-linux.yaml 파일에 이러한 환경 변수가 이미 정의되어 있습니다.
Linux에서: YAML에 Docker 내부 네트워크에서 통신을 허용하는 extra_hosts도 지정되어 있는지 확인하세요. Docker이 20.10 이전 버전인 경우 이 extra_hosts 설정 줄을 제거하세요.
작성 파일의 notes 섹션은 다음과 같이 표시되어야 합니다:
notes:container_name:notesrestart:alwaysbuild:context:../dockerfile:notes/dockerfile.notes.mavenports:- 8080:8080extra_hosts:# Linux only- "host.docker.internal:host-gateway"# Linux onlylabels:- com.datadoghq.tags.service="notes"- com.datadoghq.tags.env="dev"- com.datadoghq.tags.version="0.0.1"environment:- DD_SERVICE=notes- DD_ENV=dev- DD_VERSION=0.0.1- DD_AGENT_HOST=host.docker.internal
컨테이너를 시작해 자동 추적을 확인합니다.
이제 추적 라이브러리가 설치되어 에이전트가 실행 중입니다. 애플리케이션을 재시작하여 트레이스 수신을 시작합니다. 다음 명령을 실행합니다.
docker-compose -f service-docker-compose.yaml build notes
docker-compose -f service-docker-compose.yaml up notes
실행되는 애플리케이션에서 CURL 요청을 전송합니다.
curl localhost:8080/notes
[]
curl -X POST 'localhost:8080/notes?desc=hello'
{"id":1,"description":"hello"}
curl localhost:8080/notes/1
{"id":1,"description":"hello"}
curl localhost:8080/notes
[{"id":1,"description":"hello"}]
잠시 기다린 다음 Datadog에서 APM > 트레이스로 이동합니다. API 호출과 대응되는 트레이스 목록을 확인할 수 있습니다.
h2는 이 튜토리얼에 대해 내장된 인메모리 데이터베이스이며, notes는 Spring Boot 애플리케이션입니다. 추적 목록에는 모든 스팬, 시작 시점, 스팬과 함께 추적된 리소스, 그리고 소요 시간이 표시됩니다.
몇 분 후에도 추적이 표시되지 않으면 Agent가 실행 중인지 확인하세요. 추적 검색 필드에서 필터를 지웁니다. 사용하지 않는 ENV 같은 환경 변수를 필터링하는 경우도 있습니다.
트레이스 검사
트레이스 페이지에서 POST /notes 트레이스를 클릭해 각 스팬에 걸리는 시간 및 스팬 완료 전 발생한 다른 스팬을 나타내는 플레임(Flame) 그래프를 확인할 수 있습니다. 그래프 상단의 막대는 이전 화면에서 선택한 스팬입니다. (이 경우 메모 애플리케이션의 최초 엔트리 포인트입니다.)
바의 너비는 완료되는 데 소요된 시간을 나타냅니다. 낮은 깊이의 막대는 높은 깊이의 막대 수명 동안 완료된 스팬을 나타냅니다.
POST 트레이스의 불꽃 그래프는 이와 비슷한 형태입니다.
GET /notes 트레이스는 이와 비슷한 형태입니다.
추적 설정
Java 추적 라이브러리는 Java에 내장된 Agent 및 모니터링 지원을 사용합니다. Dockerfile의 플래그 -javaagent:../dd-java-agent.jar는 JVM에 Java 추적 라이브러리의 위치를 알려 Java Agent로 실행할 수 있도록 합니다. Java Agent에 대한 자세한 내용은 https://www.baeldung.com/java-instrumentation에서 확인하세요.
dd.trace.sample.rate 플래그는 이 애플리케이션의 샘플 속도를 설정합니다. Dockerfile의 ENTRYPOINT 명령은 해당 값을 1로 설정하며 이는 notes 서비스에 대한 모든 요청을 100% 분석하고 표시하기 위해 요청을 Datadog 백엔드에 전송함을 의미합니다. 볼륨이 낮은 테스트 애플리케이션의 경우 좋습니다. 프로덕션 환경이나 대용량 환경에서는 매우 많은 양의 데이터가 발생하므로 이 작업을 수행하지 마시기 바랍니다. 대신 일부 요청을 샘플링하세요. 0에서 1 사이의 값을 선택합니다. 예를 들어 -Ddd.trace (트레이스).sample.rate=0.1은 요청의 10%에 대한 트레이스를 Datadog로 보냅니다. 추적 구성 설정 및 샘플링 메커니즘에 대해 자세히 알아보세요.
명령에서 샘플링 속도 플래그가 -jar 플래그 앞에 나타나는 것을 확인할 수 있습니다. 이는 이 플래그가 애플리케이션이 아닌 Java Virtual Machine의 파라미터이기 때문입니다. 애플리케이션에 Java Agent를 추가할 때 플래그를 올바른 위치에 지정해야 합니다.
Java 애플리케이션에 수동 계측 추가
자동 계측은 편리하지만 때때로 더욱 세분화된 스팬을 원할 수 있습니다. Datadog의 Java DD 트레이스 API를 사용하면 주석이나 코드로 코드 내 스팬을 지정할 수 있습니다.
다음 단계는 코드에 주석을 추가하여 일부 샘플 메서드를 추적하는 방법을 안내합니다.
/notes/src/main/java/com/datadog/example/notes/NotesHelper.java를 엽니다. 이 예제에는 주석 처리된 코드가 포함되어 있으며, 코드에서 커스텀 추적을 설정하는 다양한 방법을 보여줍니다.
애플리케이션의 특정 코드 블록에 대해 별도의 스팬을 생성할 수도 있습니다. 스팬 내에 서비스 및 리소스 이름 태그와 오류 처리 태그를 추가합니다. 이러한 태그를 추가하면 Datadog 시각화에서 스팬과 메트릭을 보여주는 플레임 그래프가 생성됩니다. 프라이빗 메서드를 수동으로 추적하는 줄의 주석 처리를 제거합니다.
Tracertracer=GlobalTracer.get();// Tags can be set when creating the spanSpanspan=tracer.buildSpan("manualSpan1").withTag(DDTags.SERVICE_NAME,"NotesHelper").withTag(DDTags.RESOURCE_NAME,"privateMethod1").start();try(Scopescope=tracer.activateSpan(span)){// Tags can also be set after creationspan.setTag("postCreationTag",1);Thread.sleep(30);Log.info("Hello from the custom privateMethod1");
그리고 오류에 태그를 설정하는 줄도 있습니다.
}catch(Exceptione){// Set error on spanspan.setTag(Tags.ERROR,true);span.setTag(DDTags.ERROR_MSG,e.getMessage());span.setTag(DDTags.ERROR_TYPE,e.getClass().getName());finalStringWritererrorString=newStringWriter();e.printStackTrace(newPrintWriter(errorString));span.setTag(DDTags.ERROR_STACK,errorString.toString());Log.info(errorString.toString());}finally{span.finish();}
수동 추적을 위한 종속성을 구성하는 notes/pom.xml 줄을 열고 주석 처리를 제거하여 Maven 빌드를 업데이트하세요. dd-trace-api 라이브러리는 @Trace 어노테이션에 사용되고, opentracing-util 와 opentracing-api는 수동 스팬 생성에 사용됩니다.
컨테이너 다시 빌드(Linux: service-docker-compose-linux.yaml 사용):
docker-compose -f service-docker-compose.yaml build notes
docker-compose -f service-docker-compose.yaml up notes
일부 HTTP 요청, 특히 GET 요청을 다시 전송합니다.
트레이스 탐색기에서 새로운 GET 요청 중 하나를 클릭한 다음 이와 같은 불꽃 그래프를 확인하세요.
getAll 함수가 커스텀 추적을 포함하므로 스택 트레이스(stack trace)에서 상위 수준의 상세 정보를 확인할 수 있습니다.
단일 애플리케이션 추적은 좋은 시작이지만 추적의 진정한 가치는 서비스를 통한 요청의 흐름을 확인하는 데 있습니다. 이것을 _분산 추적_이라고 부릅니다.
샘플 프로젝트에 calendar라는 두 번째 애플리케이션이 포함되어 있습니다. 이 애플리케이션은 호출 시 임의의 날짜를 반환합니다. 메모 애플리케이션의 POST 엔드포인트는 add_date라는 두 번째 쿼리 파라미터를 포함합니다. y로 설정되어 있는 경우 메모는 캘린더 애플리케이션을 호출하여 메모에 추가할 날짜를 가져옵니다.
앞서 메모 앱에 대해 했던 것처럼 Dockerfile의 시작 명령에 dd-java-agent를 추가해 추적을 위한 캘린더 앱을 구성합니다. calendar/Dockerfile.calendar.maven을 열고 이미 dd-java-agent를 다운로드하고 있는지 확인합니다.
RUN curl -Lo dd-java-agent.jar 'https://dtdg.co/latest-java-tracer'
같은 calendar/dockerfile.calendar.maven 파일 내에서 추적 없이 실행할 ENTRYPOINT 줄을 주석 처리합니다. 그런 다음 추적 기능을 활성화하여 애플리케이션을 실행하는 ENTRYPOINT 줄의 주석 처리를 제거합니다.
참고: 이 플래그, 특히 샘플 속도는 이 튜토리얼이 적용되지 않은 환경에는 적합하지 않을 수 있음을 다시 강조합니다. 실제 환경에서 어떤 플래그를 사용해야 하는지에 살펴보려면 추적 설정을 참고하세요.
docker/service-docker-compose-linux.yaml을 열고 calendar 서비스에 대한 환경 변수를 주석 처리하여 앱과 Docker에 대한 Agent 호스트 및 통합 서비스 태그를 설정합니다. notes 컨테이너에서와 마찬가지로 DD_AGENT_HOST 값을 Docker에 필요한 것과 일치하도록 설정하고 리눅스가 아닌 경우 extra_hosts 을 제거합니다:
calendar:container_name:calendarrestart:alwaysbuild:context:../dockerfile:calendar/dockerfile.calendar.mavenports:- 9090:9090labels:- com.datadoghq.tags.service="calendar"- com.datadoghq.tags.env="dev"- com.datadoghq.tags.version="0.0.1"environment:- DD_SERVICE=calendar- DD_ENV=dev- DD_VERSION=0.0.1- DD_AGENT_HOST=host.docker.internalextra_hosts:# Linux only- "host.docker.internal:host-gateway"# Linux only
notes 서비스 섹션에서 CALENDAR_HOST 환경 변수 및 calendar 항목의 주석을 해제하여 depends_on 두 앱 간에 필요한 연결을 설정합니다.
컨테이너를 다시 시작하여 다중 서비스 애플리케이션을 빌드합니다. 먼저 실행 중인 모든 컨테이너를 중단합니다.
docker-compose -f service-docker-compose-linux.yaml down
그런 다음 다음 명령을 실행하여 시작하세요.
docker-compose -f service-docker-compose-linux.yaml build
docker-compose -f service-docker-compose-linux.yaml up
add_date 파라미터를 사용하여 POST 요청을 보냅니다.
curl -X POST 'localhost:8080/notes?desc=hello_again&add_date=y'
{"id":1,"description":"hello_again with date 2022-11-06"}
트레이스 탐색기에서 다음 최신 트레이스를 클릭하여 두 서비스 간의 분산 트레이스를 확인하세요.
notes 애플리케이션에서는 아무것도 변경하지 않았습니다. Datadog은 notes에서 calendar로의 HTTP 호출에 사용되는 okHttp라이브러리와 notes 및 calendar에서 HTTP 요청을 수신하는 데 사용되는 Jetty 라이브러리를 모두 자동으로 계측합니다. 이를 통해 트레이스 정보를 한 애플리케이션에서 다른 애플리케이션으로 전달하여 분산 트레이스를 캡처할 수 있습니다.
트러블슈팅
예상대로 트레이스를 수신하지 않으면 Java 트레이서의 디버그 모드를 설정합니다. 자세한 내용은 디버그 모드 활성화에서 확인하세요.