본문 바로가기
Golang

Datadog APM with Golang 1 - 배경 이야기

by Limm_jk 2023. 2. 25.

원활한 서비스 운영을 위하여 트레이싱 툴은 선택이 아닌 필수라고 생각합니다. 그런 만큼 저희 회사 또한 트레이싱 툴을 사용하는데요. 바로 datadog이라는 SaaS 툴을 사용하고 있습니다. 이 중 어플리케이션 성능을 모니터링해주는 APM 기능을 정말 잘 사용하고 있습니다.

 

이 datadog APM을 Java환경에서 사용할 때는 고민이 적었습니다. 에이전트부터 트레이싱까지 아래와 같이 pre-build 된 jar 파일을 끼워 넣어서 해결할 수 있었기 때문입니다.

java -javaagent:/path/to/dd-java-agent.jar \
  -Ddd.logs.injection=true \
  -jar path/to/your/app.jar

이와 유사한 환경을 기대하며 golang datadog 세팅을 시작한 저는 곧 눈물을 흘릴 수 밖에 없었습니다. 위와 다르게 agent를 따로 띄워야 하고, 트레이싱 할 범위를 명시적으로 코드단에 선언해줘야 했기 때문인데요. 이번 글에서는 datadog에 대한 이야기를, 다음 글에서는 이를 해결한 이야기를 해보겠습니다.

 

용어 정리

java에서 사용할 때는 추상화 레벨이 높아서 datadog에서 쓰이는 용어를 잘 몰라도 상관 없었는데요. golang에서 세팅하다 보니 직접 operation을 나눠주고, span 범위를 설정해 주는 등 세부적으로 알아야 할 것들이 많았습니다. 그러기 위해서는 용어가 뜻하는 바가 무엇인지 알아야 하기 때문에, 아래 그림을 기반으로 용어부터 간단하게 정리해 보겠습니다.

Span

이 그림에서는 노란색, 보라색, 주황색 세개의 span이 존재한다.

Span은 애플리케이션 내에서 수행된 작업의 단위를 의미합니다. 위 그림에서 그래프를 이루는 하나의 작대기가 하나의 span이라고 볼 수 있는데요. http 요청, 쿼리 등을 span으로 지정하여 소요시간 등의 데이터들을 수집할 수 있습니다.

 

그리고 span은 parent관계를 지정할 수 있는데요. 위 그림에서 노란색 span 아래에 보라색 주황색 span이 있는 것을 보실 수 있습니다. 이것을 노란색 span이 보라색 주황색 span의 parent라고 볼 수 있는데요. parent span의 세부 정보를 제공하는 것이라 볼 수 있고, 시각화가 가능해서 어떤 부분이 구체적인 병목인지 파악하는 것에 큰 도움을 줍니다.

 

Trace

Trace는 여러 개의 Span을 연결하여 전체적인 애플리케이션 성능을 추적하는 기능입니다. 각 Span의 시작 시간, 종료 시간, 수행 결과 등을 추적하여, 전체적인 애플리케이션 성능을 파악할 수 있습니다.

 

Service

Service는 애플리케이션에서 수행되는 서비스를 의미합니다. 위의 그림에서는 backend, rest client, psql이 서비스로 나누어져 있는데요. 각각을 모아볼 수 있어서 psql의 slow query 등 문제를 파악할 수 있습니다.

 

Operation

Operation은 애플리케이션 성능 모니터링을 수행할 때, 각 작업의 유형을 구분하기 위해 사용됩니다. 예를 들면, rest client service는 결제 서버에 요청을 보낼 수 있고, 상품 서버에도 요청을 보낼 수 있겠죠. 이때 이를 결제 operation, 상품 operation 등으로 나누어서 분석을 도울 수 있습니다. 위 그림에서는 그래프 내에 있는 dw.http 와 같은 것이 operation입니다.

 

Span with Golang

위 용어에서 보았을 때 어플리케이션에서 해야 하는 일은 결국 Span을 만드는 일이라고 볼 수 있을 것 같은데요. 그럼 Golang에서는 Span을 어떻게 만들 수 있을까요?

import (
    "github.com/DataDog/dd-trace-go/tracer"
)

func function() {
    // 새로운 Span 생성
    span := tracer.StartSpan("myOperation")
     // 함수 종료 시 Span 종료
    defer span.Finish()

    // Span에 태그 추가
    span.SetTag("myTagKey", "myTagValue")

    // Span에서 수행할 작업 수행
    doSomething()
}

 

간단하다고 생각이 들 수 있을 것 같은데요. 하지만, 이 부분을 모든 서비스 input output에 붙여야 한다고 생각하면 머리가 좀 아파집니다. 서비스 나갈 때마다 StartSpan 해주고, defer로  다른 관심사에 의한 오염도가 꽤나 증가할 것만 같은데요. dd-trace-go에서 지원하는 다양한 함수를 잘 사용하면 꽤나 합리적으로 해결할 수 있었는데요. 다음 편에서 이런 부분을 이어서 말해보도록 하겠습니다.

 

댓글