Trace#
Trace는 분산 시스템에서 요청의 흐름을 추적하고 시각화하는 데 사용된다.
Trace는 분산 시스템에서 요청이나 트랜잭션이 여러 서비스와 컴포넌트를 통과하는 전체 여정을 기록한 것이다.
각 Trace는 하나 이상의 span으로 구성되며, 첫 번째 span은 root span이라고 한다.
Trace의 목적#
- 분산 시스템에서의 요청 흐름 이해
- 성능 병목 지점 식별
- 서비스 간 의존성 파악
- 오류 및 지연의 근본 원인 분석
Trace의 구성 요소#
트레이스는 다음과 같은 구성 요소들로 이루어진다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 트레이스 시작
Span rootSpan = tracer.spanBuilder("checkout-process")
.setSpanKind(SpanKind.SERVER)
.startSpan();
try (Scope scope = rootSpan.makeCurrent()) {
// 자식 스팬 생성
Span paymentSpan = tracer.spanBuilder("process-payment")
.setParent(Context.current().with(rootSpan))
.startSpan();
try {
processPayment();
paymentSpan.setStatus(StatusCode.OK);
} catch (Exception e) {
paymentSpan.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
paymentSpan.end();
}
} finally {
rootSpan.end();
}
|
트레이스 구성의 핵심 요소들:
- Trace ID: 전체 트랜잭션을 식별하는 고유 식별자
- Span: 작업의 단위를 나타내며, 시작/종료 시간과 메타데이터를 포함
- Parent-Child 관계: 스팬들 간의 계층 구조를 표현
- Tags/Attributes: 추가적인 컨텍스트 정보를 제공
- Events: 스팬 내에서 발생한 중요한 시점들을 기록
Trace의 특징#
- 인과관계 표현: 요청의 전체 흐름과 각 단계 간의 관계를 보여준다.
- 시각화: 주로 워터폴 다이어그램 형태로 시각화되어 직관적인 이해를 돕는다.
- 분산 시스템 최적화: 마이크로서비스 아키텍처에서 특히 유용하다.
Trace의 활용#
- 성능 최적화: 지연 시간이 긴 구간을 식별하고 개선할 수 있다.
- 오류 디버깅: 오류가 발생한 정확한 위치와 원인을 파악할 수 있다.
- 시스템 이해: 복잡한 분산 시스템의 동작을 더 잘 이해할 수 있다.
분산 추적의 구현#
여러 서비스에 걸친 추적을 구현하는 방법을 살펴보자:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
tracer = trace.get_tracer(__name__)
@app.route('/order')
def create_order():
with tracer.start_as_current_span("create_order") as span:
span.set_attribute("user_id", request.user.id)
# 결제 서비스 호출
with tracer.start_span("payment_service_call") as payment_span:
try:
process_payment()
payment_span.set_status(Status(StatusCode.OK))
except Exception as e:
payment_span.set_status(Status(StatusCode.ERROR))
payment_span.record_exception(e)
raise
|
컨텍스트 전파#
서비스 간에 트레이스 컨텍스트를 전달하는 방법:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // HTTP 클라이언트에서 트레이스 컨텍스트 전달
public Response makeHttpCall() {
Span span = tracer.spanBuilder("http-call").startSpan();
try {
HttpHeaders headers = new HttpHeaders();
TextMapSetter<HttpHeaders> setter =
(carrier, key, value) -> carrier.set(key, value);
// 현재 컨텍스트를 HTTP 헤더에 주입
OpenTelemetry.getPropagators().getTextMapPropagator()
.inject(Context.current(), headers, setter);
return httpClient.send(request, headers);
} finally {
span.end();
}
}
|
트레이스 데이터 분석#
수집된 트레이스 데이터를 분석하는 방법은 다음과 같다:
성능 분석:
1
2
3
| // 성능 메트릭 추출
span.setAttribute("db.query.duration_ms", queryDuration);
span.setAttribute("response.size_bytes", responseSize);
|
오류 분석:
1
2
3
| // 오류 정보 기록
span.recordException(exception);
span.setStatus(StatusCode.ERROR, "Database connection failed");
|
병목 지점 식별:
1
2
3
4
5
| // 임계값 초과 시 경고 이벤트 기록
if (duration > threshold) {
span.addEvent("performance_warning",
Attributes.of("duration_ms", duration));
}
|
참고 및 출처#