OTEL Agent 샘플링 전략¶
작성자: 송룡치
들어가며¶
분산 서비스 추적에서 요청이 하나의 서비스에서 다른 서비스로 이동하는 과정을 관찰하는 것은 매우 유용합니다. 하지만 많은 경우 데이터가 대량으로 중복됩니다. 이러한 데이터가 정말 중요할까요? 이때 올바른 샘플링 전략을 통해 불필요한 트래픽 비용을 줄일 수 있습니다.
샘플링이란 데이터를 수신하여 내보내거나 저장할 데이터를 결정하는 것을 의미합니다. 때때로 샘플링이 데이터를 폐기하는 것으로 오해되기도 하지만 이는 올바르지 않습니다.
이전 글 OpenTelemetry 샘플링 모범 사례에서는 주로 opentelemetry-collector의 샘플링 전략을 소개했습니다. 이 글에서는 Java Agent 측의 샘플링을 주로 설명합니다.
샘플링 전략¶
헤드 샘플링¶
헤드 샘플링의 가장 일반적인 형태는 일관된 확률 샘플링입니다. 이는 결정적 샘플링이라고도 합니다. 이 경우 TraceID와 원하는 샘플링 비율에 따라 샘플링 결정이 내려집니다. 이렇게 하면 전체 트레이스가 샘플링되도록 보장됩니다. 예를 들어 모든 트레이스의 5%를 샘플링하려면 생성된 모든 트레이스를 5% 비율로 샘플링합니다.
헤드 샘플링의 장점은 많습니다:
- 구성이 간단함
- 이해하기 쉬움
- 높은 효율성
- 어디서나 구성 가능
마찬가지로 단점도 분명합니다: 트레이스의 전체 데이터를 기반으로 샘플링 결정을 내릴 수 없다는 점입니다. 트레이스에서 오류가 발생한 경우에도 샘플링되어 업로드된다는 보장이 없으므로 문제 해결에 불리합니다.
따라서 테일 샘플링을 이해할 필요가 있습니다.
테일 샘플링¶
테일 샘플링이란 트레이스의 모든 또는 대부분의 Span을 고려하여 샘플링 여부를 결정하는 경우를 말합니다. "테일 샘플링"을 사용하면 트레이스의 여러 부분에서 비롯된 특정 기준에 따라 트레이스를 샘플링할 수 있습니다. 이는 "헤드 샘플링"으로는 불가능한 기능입니다.
테일 샘플링의 몇 가지 사용 사례:
- 모든 오류를 포함하는 트레이스 샘플링
- 전체 지연 시간을 기반으로 샘플링
- 특정 이벤트를 기반으로 샘플링
- HTTP 코드가 200이 아닌 경우 샘플링
- 기타 기준에 따른 샘플링
테일 샘플링에는 많은 어려움이 있습니다:
- 테일 샘플링은 구현이 어려울 수 있습니다. 사용 가능한 샘플링 기술 유형에 따라 "설정하고 잊어버리는" 일이 아닌 경우가 많습니다. 시스템이 변경됨에 따라 샘플링 전략도 변경됩니다. 대규모 복잡한 분산 시스템의 경우 샘플링 전략을 구현하는 규칙도 당연히 대규모로 복잡해집니다.
- 테일 샘플링은 운영이 어려울 수 있습니다. 테일 샘플링을 구현하는 노드는 모든 데이터를 수신해야 합니다. 때로는 수십 개 노드의 트레이스 데이터를 수신하여 계산해야 합니다. 계산 속도가 수신량을 따라가지 못하는 경우도 있으며, 올바른 테일 샘플링 노드는 많은 리소스가 필요합니다.
- 현재 일부 대규모 공급업체만이 테일 샘플링 옵션을 제공할 수 있습니다.
헤드 샘플링과 테일 샘플링에 대한 소개는 대략 이렇습니다.
마지막으로, 대량의 Span 데이터를 생성하는 애플리케이션의 경우 헤드 샘플링을 선택하는 것이 좋습니다. 이렇게 하면 트레이스 채널이 과부하로 인해 정체되지 않습니다.
OTEL JAVA Agent에서는 헤드 샘플링 기반 전략을 채택하고 있습니다. 이 점을 미리 알아두시기 바랍니다.
다른 언어 지원 또는 Collector 구성에 대해서는 공식 문서를 참조하세요.
Agent의 샘플러 Sampler¶
OTEL에는 샘플링을 구성하는 두 가지 환경 변수가 있습니다: OTEL_TRACES_SAMPLER와 OTEL_TRACES_SAMPLER_ARG:
샘플링 구성 선택 방법¶
OTEL_TRACES_SAMPLER의 구성 값:
"always_on": AlwaysOnSampler 기본 구성 1.0, 즉 샘플링하지 않음"always_off": AlwaysOffSampler 항상 모든 트레이스를 수집"traceidratio": TraceIdRatioBased trace ID에 기반한 확률 샘플링"parentbased_always_on": ParentBased(AlwaysOnSampler 기반)"parentbased_always_off": ParentBased(AlwaysOffSampler 기반)"parentbased_traceidratio": ParentBased(TraceIdRatioBased 기반)"parentbased_jaeger_remote": ParentBased(JaegerRemoteSampler 기반)"jaeger_remote": JaegerRemoteSampler"xray": AWS X-Ray 중앙 집중식 샘플링 (서드파티)
OTEL_TRACES_SAMPLER_ARG 구성:
- 1.0 기본값은 1.0, 즉 전체 샘플링
- [0-1.0] 확률 샘플링, 예: 0.25는 25% 샘플링 비율
traceidratio및parentbased_traceidratio인 경우에만OTEL_TRACES_SAMPLER_ARG가 적용됩니다.
일반적인 구성¶
OTEL_TRACES_SAMPLER=parentbased_traceidratio (부모 기반 Trace ID 비율): 이 샘플링 전략은 부모의 Trace ID를 기반으로 하위 항목의 샘플링 여부를 결정합니다. 새 요청이 도착하면 부모의 Trace ID가 지정된 비율 범위 내에 있는지 확인합니다. 그렇다면 하위 항목도 샘플링됩니다. 이 전략은 요청과 관련 작업 간의 연관성을 보장합니다. 분산 추적 시스템에 매우 유용합니다.
OTEL_TRACES_SAMPLER=traceidratio (Trace ID 비율): 이 샘플링 전략은 각 요청의 Trace ID를 기반으로 해당 요청의 샘플링 여부를 결정합니다. 각 요청에는 고유한 Trace ID가 있습니다. 이 전략을 사용하면 각 요청이 지정된 비율에 따라 독립적으로 샘플링됩니다. 요청 간의 연관성을 고려할 필요 없이 각 요청 자체의 샘플링 비율에만 관심이 있는 경우에 적합합니다.
요약하면, OTEL_TRACES_SAMPLER=parentbased_traceidratio는 부모의 Trace ID를 기반으로 하위 항목의 샘플링 비율을 결정하고, OTEL_TRACES_SAMPLER=traceidratio는 각 요청의 Trace ID에 따라 독립적으로 샘플링 비율을 결정합니다. 요구 사항에 맞는 전략을 선택하는 것은 요청 간 연관성에 대한 중요도에 따라 달라집니다.
일반적으로 사용되는 명령:
사용자 정의 Sampler 플러그인¶
OTEL Agent는 플러그인 인터페이스 Extensions를 제공하며, 사용자 정의 Sampler를 통해 샘플링을 구현할 수 있습니다.
사용자 정의 Sampler가 구현해야 하는 인터페이스:
public class DemoSampler implements Sampler {
@Override
public SamplingResult shouldSample(
Context parentContext,
String traceId,
String name,
SpanKind spanKind,
Attributes attributes,
List<LinkData> parentLinks) {
if (spanKind == SpanKind.INTERNAL && name.contains("greeting")) {
return SamplingResult.create(SamplingDecision.DROP);
} else {
return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE);
}
}
@Override
public String getDescription() {
return "DemoSampler";
}
}
인터페이스 구현에서 볼 수 있듯이 다음과 같은 매개변수가 있습니다: parentContext, traceId, name, spanKind, attributes, parentLinks.
따라서 사용자 정의 Sampler에서는 여러 차원에서 해당 트레이스의 샘플링 여부를 판단할 수 있습니다:
- 태그 필터링
- traceId 비율 샘플링
- name 등
그러나 이 방법으로도 비율 샘플링에서 오류가 있는 Span이 삭제되는 것을 막을 수는 없습니다. 그 이유는 shouldSample()이 Span 초기화 시점에 호출되는 반면, 오류는 보통 Span이 종료될 때 해당 이벤트에 채워지기 때문입니다.