콘텐츠로 이동

OpenTelemetry Rust SDK

이 문서는 SDK 계측 방식을 사용합니다. 애플리케이션 코드에서 SDK를 초기화하고 Span을 생성한 다음 Exporter를 구성하여 DataKit을 통해 트레이스를 Guance로 전송합니다. 이 방식은 제로 코드 주입이 아니므로 의존성을 설치하거나 환경 변수를 설정해도 모든 프레임워크 호출이 자동으로 수집되지는 않습니다. 이 문서에서는 Trace만 활성화하며 Kubernetes는 다루지 않습니다.

Rust + OpenTelemetry SDK -> OTLP/HTTP -> DataKit -> Guance

사전 요구 사항

  • Rust stable 툴체인과 Cargo를 설치합니다. 아래 예제는 OpenTelemetry 0.31.0을 고정하여 사용하며, 현재 안정 툴체인을 사용하고 Cargo.lock을 애플리케이션 버전 관리에 포함할 것을 권장합니다.
  • 예제는 동기식 main, 블로킹 HTTP 클라이언트, 백그라운드 배치 내보내기 스레드를 사용하므로 Tokio가 필요하지 않습니다.
  • DataKit이 설치되어 있고 대상 워크스페이스의 설치 명령으로 전송 주소와 Token이 구성되어 있어야 합니다. 애플리케이션에서 DataKit HTTP 포트 9529에 접근할 수 있어야 합니다.

1. OpenTelemetry 수집기 활성화

DataKit 호스트에서 구성 디렉터리로 이동합니다. 구성 파일이 없을 때만 샘플을 복사하고, 이미 파일이 있으면 바로 수정합니다:

cd /usr/local/datakit/conf.d/opentelemetry
sudo cp -n opentelemetry.conf.sample opentelemetry.conf

opentelemetry.conf에 다음 구성이 포함되어 있는지 확인합니다. 커스텀 태그는 customer_tags로 유지합니다:

[[inputs.opentelemetry]]
  customer_tags = ["team", "app.operation"]

  [inputs.opentelemetry.http]
    http_status_ok = 200
    trace_api = "/otel/v1/traces"
    metric_api = "/otel/v1/metrics"
    logs_api = "/otel/v1/logs"

로컬 연동은 127.0.0.1:9529을 사용합니다. 다른 호스트에서 연동할 때는 DataKit 기본 구성 datakit.conf[http_api].listen에서 애플리케이션이 접근할 수 있는 수신 주소를 설정하고 네트워크 접근 범위를 제한합니다. HTTP 수신 주소는 수집기 파일에서 설정하지 않습니다.

DataKit을 재시작하고 확인합니다:

sudo datakit service restart
curl http://127.0.0.1:9529/v1/ping

/v1/ping은 HTTP 서비스에 접근할 수 있는지만 확인하며, 트레이스가 저장소에 적재되었음을 의미하지는 않습니다. 자세한 설명은 OpenTelemetry 수집기를 참조하세요. DataKit이 워크스페이스 인증을 담당하므로 예제 애플리케이션에서 워크스페이스 Token을 직접 구성하지 않습니다.

2. 애플리케이션에 OpenTelemetry 연동

의존성 설치

빈 디렉터리에 예제 프로젝트를 생성한 다음 Cargo.toml을 다음 내용으로 설정합니다. 기존 프로젝트는 원래 구성을 덮어쓰지 말고 의존성을 병합합니다:

cargo new otel-rust-demo
cd otel-rust-demo
[package]
name = "otel-rust-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
opentelemetry = { version = "=0.31.0", default-features = false, features = ["trace"] }
opentelemetry_sdk = { version = "=0.31.0", default-features = false, features = ["trace"] }
opentelemetry-otlp = { version = "=0.31.0", default-features = false, features = ["trace", "http-proto", "reqwest-blocking-client"] }

SDK 초기화 및 Span 생성

다음 내용을 src/main.rs로 저장합니다. SDK는 시작 시 한 번 초기화되며 checkout 아래에 하위 Span db.lookup을 생성하고, 종료 전에 배치 내보내기가 완료될 때까지 대기합니다:

use opentelemetry::{
    global,
    trace::{TraceContextExt, Tracer},
    KeyValue,
};
use opentelemetry_otlp::{Protocol, SpanExporter, WithExportConfig};
use opentelemetry_sdk::{
    propagation::TraceContextPropagator,
    trace::{Sampler, SdkTracerProvider},
    Resource,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let exporter = SpanExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary)
        .build()?;

    let provider = SdkTracerProvider::builder()
        .with_batch_exporter(exporter)
        .with_resource(Resource::builder().build())
        .with_sampler(Sampler::ParentBased(Box::new(
            Sampler::TraceIdRatioBased(1.0),
        )))
        .build();

    global::set_text_map_propagator(TraceContextPropagator::new());
    global::set_tracer_provider(provider.clone());
    let tracer = global::tracer("otel-rust-demo");

    tracer.in_span("checkout", |_cx| {
        tracer.in_span("db.lookup", |cx| {
            cx.span().set_attribute(KeyValue::new("app.operation", "lookup"));
        });
    });

    provider.shutdown()?;
    Ok(())
}

빌드 및 실행

애플리케이션을 시작하는 동일한 터미널에서 다음 파라미터를 구성합니다. 다른 호스트에서 연동할 때는 127.0.0.1을 실제 DataKit 주소로 바꿉니다:

export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=prod,service.version=1.0.0,team=backend"
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://127.0.0.1:9529/otel/v1/traces"

cargo run

첫 실행 시 의존성을 다운로드하고 컴파일합니다. 이후 cargo build --release를 실행하여 프로덕션 바이너리를 빌드할 수 있습니다. 장기 실행 서비스는 Provider를 유지하고, 요청 수신을 중단하고 처리 중인 Span을 종료한 후 shutdown()을 호출해야 합니다.

3. 데이터 전송 파라미터

파라미터 또는 구성 설명
OTEL_SERVICE_NAME service.name에 해당합니다. 예제에서는 order-service를 사용하며, 안정적인 서비스 이름으로 설정해야 합니다.
OTEL_RESOURCE_ATTRIBUTES 쉼표로 구분된 리소스 속성입니다. 예제는 환경, 버전, team을 설정합니다. 커스텀 필드는 DataKit customer_tags에 추가해야 합니다.
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Trace 전용 전체 주소입니다: http://127.0.0.1:9529/otel/v1/traces. 공통 기본 주소보다 우선합니다.
OTEL_EXPORTER_OTLP_ENDPOINT 선택적 기본 주소입니다: http://127.0.0.1:9529/otel. Trace 전용 주소를 설정하지 않으면 Exporter가 /v1/traces를 추가합니다.
OTEL_EXPORTER_OTLP_HEADERS 선택적 OTLP 요청 헤더이며 형식은 key=value,key2=value2입니다. 수신 측이나 프록시가 인증을 요구하는 경우에만 구성합니다.

이 예제는 코드에서 .with_http()Protocol::HttpBinary를 사용하여 HTTP/Protobuf를 선택합니다. OTEL_EXPORTER_OTLP_PROTOCOL을 수정해도 이 예제가 gRPC로 자동 전환되지는 않습니다. gRPC로 변경하려면 grpc-tonic 기능을 활성화하고 .with_tonic()을 사용하며 적절한 Tokio 런타임을 제공해야 합니다.

샘플링은 코드에서 ParentBased + 루트 트레이스 비율 1.0으로 구성되며 부모 Span의 샘플링 결정을 따릅니다. 프로덕션 환경에서는 예제의 비율을 0.1로 변경하여 루트 트레이스의 약 10%를 샘플링할 수 있습니다. 이 예제는 샘플러를 명시적으로 설정하므로 OTEL_TRACES_SAMPLER 또는 OTEL_TRACES_SAMPLER_ARG에 의존하지 않습니다.

예제는 Trace 내보내기 파이프라인을 명시적으로 생성하므로 OTEL_TRACES_EXPORTER가 이를 선택하거나 종료하지 않습니다. Metric·Log Provider를 생성하지 않았기 때문에 OTEL_METRICS_EXPORTEROTEL_LOGS_EXPORTER를 설정해도 해당 신호가 활성화되지 않습니다. 로그는 별도로 DataKit의 로그 파일 수집을 사용할 수 있습니다.

컨텍스트 전파와 비즈니스 연동

이 예제는 W3C TraceContext 전파기를 등록하지만 네트워크 요청을 자동으로 가로채지는 않습니다. HTTP/RPC에 연동할 때는 서버 측에서 Extractor를 통해 업스트림 컨텍스트를 추출하여 새 Span의 부모로 사용하고, 클라이언트 측에서는 Injector를 통해 traceparent, tracestate를 주입해야 합니다. 동기식 예제의 in_span.await를 직접 가로질러 사용해서는 안 됩니다. 비동기 작업은 FutureExt::with_context 등을 사용하여 컨텍스트를 전파해야 합니다. tracing을 사용하는 애플리케이션에는 호환 버전의 tracing-opentelemetry Layer도 구성해야 합니다.

검증 및 문제 해결

  1. 예제를 실행한 후 Guance의 애플리케이션 성능 모니터링(APM)에서 order-service로 트레이스를 조회하여 checkout과 그 하위 Span db.lookup이 있는지 확인합니다.
  2. 데이터가 없으면 수집기가 활성화되었는지, 애플리케이션 환경 변수가 적용되었는지, HTTP 경로에 /otel/v1/traces가 포함되어 있는지, DataKit과 애플리케이션의 내보내기 오류를 확인합니다.
  3. Span이 종료되었고 Provider가 종료 전에 플러시를 완료했는지 확인합니다. 강제 종료하거나 루트 트레이스 비율을 0으로 설정하면 예상한 데이터를 볼 수 없습니다. 네트워크 연결이 가능하다고 해서 내보내기가 성공한 것은 아닙니다.

참고 문서

문서 평가

이 페이지가 도움이 되었나요?