본문으로 건너뛰기
최종 수정: 에

Trace 저장 및 분석

이 문서에서는 Apache Doris에서 Trace 데이터를 저장하고 분석하는 방법을 테이블 생성부터 수집, 쿼리까지 전체 과정에 걸쳐 설명합니다.

  • 전반적인 관측 가능성 솔루션은 개요를 참조하세요.
  • 리소스 산정, 클러스터 배포, 튜닝은 로그를 참조하세요.

빠른 탐색​

전체 도입 과정은 세 단계로 구성되며, 순서대로 진행할 수 있습니다.

단계내용목표
1. 테이블 생성Doris에 Trace 저장용 테이블 생성Trace 쓰기 및 쿼리 패턴에 맞게 성능 최적화
2. 수집OpenTelemetry를 통해 Doris에 Trace 데이터 쓰기애플리케이션에서 Collector를 거쳐 Doris로 이어지는 파이프라인 연결
3. 쿼리Grafana에서 Trace 데이터 시각적 분석검색, 지연 시간 분포 확인, Trace 세부 정보 조회

1. 테이블 생성​

Trace 데이터는 쓰기 및 쿼리 패턴에 뚜렷한 특성이 있습니다. 테이블을 생성할 때 이러한 특성에 맞게 설정하면 성능을 높일 수 있습니다.

1.1 주요 설정 항목​

다음 표는 테이블 생성 시 고려해야 할 주요 설정 항목과 권장 방식을 정리한 것입니다.

설정 항목권장 방식설명
파티셔닝시간 필드에 RANGE 파티션을 적용하고 동적 파티셔닝을 활성화하여 일별 파티션을 자동으로 관리파티션을 자동으로 순환 관리하여 핫/콜드 데이터 분리와 만료 데이터 정리가 쉬워짐
정렬 키service_name과 DATETIME 타입의 시간 필드를 키로 사용특정 서비스를 시간 범위로 스캔하는 쿼리의 속도를 몇 배 높임
버킷 수클러스터 전체 디스크 수의 약 3배병렬성을 확보하면서 작은 파일 수를 제어
버킷팅 전략RANDOM을 사용하고 쓰기 시 단일 tablet으로 데이터 적재쓰기 배치 처리 효율 향상
Compactiontime_series Compaction 정책 사용쓰기 증폭을 줄이며, 이는 처리량이 높은 Trace 쓰기에서 리소스를 최적화하는 데 매우 중요함
반정형 필드span_attributes와 resource_attributes에 VARIANT 타입 사용JSON을 하위 컬럼으로 자동 분리하여 저장하므로 압축 효율과 하위 컬럼 필터링 및 분석 성능이 향상됨
인덱스자주 쿼리하는 필드에 역색인 생성동등 조건 필터링과 범위 쿼리 속도 향상
전문 검색parser 매개변수로 토크나이저를 지정하고(일반적으로 unicode면 충분함), 필요에 따라 support_phrase 활성화support_phrase는 구문 쿼리를 활성화하며, 필요하지 않으면 비활성화하여 저장 공간을 절약
복제본클라우드 디스크는 복제본 1개를 사용할 수 있으며, 물리 디스크는 최소 2개 사용 권장안정성과 비용 사이의 균형 확보
핫/콜드 데이터 분리log_s3 객체 스토리지와 log_policy_3day 정책 설정3일이 지난 데이터를 S3로 자동 이동하여 핫 스토리지 비용 절감

1.2 테이블 생성 SQL 예제​

다음 예제는 리소스, 스토리지 정책, 테이블을 생성하는 전체 과정을 보여 줍니다.

CREATE DATABASE log_db;
USE log_db;

-- Not required in the storage-compute decoupled mode
CREATE RESOURCE "log_s3"
PROPERTIES
(
"type" = "s3",
"s3.endpoint" = "your_endpoint_url",
"s3.region" = "your_region",
"s3.bucket" = "your_bucket",
"s3.root.path" = "your_path",
"s3.access_key" = "your_ak",
"s3.secret_key" = "your_sk"
);

-- Not required in the storage-compute decoupled mode
CREATE STORAGE POLICY log_policy_3day
PROPERTIES(
"storage_resource" = "log_s3",
"cooldown_ttl" = "259200"
);

CREATE TABLE trace_table
(
service_name VARCHAR(200),
timestamp DATETIME(6),
service_instance_id VARCHAR(200),
trace_id VARCHAR(200),
span_id STRING,
trace_state STRING,
parent_span_id STRING,
span_name STRING,
span_kind STRING,
end_time DATETIME(6),
duration BIGINT,
span_attributes VARIANT,
events ARRAY<STRUCT<timestamp:DATETIME(6), name:STRING, attributes:MAP<STRING, STRING>>>,
links ARRAY<STRUCT<trace_id:STRING, span_id:STRING, trace_state:STRING, attributes:MAP<STRING, STRING>>>,
status_message STRING,
status_code STRING,
resource_attributes VARIANT,
scope_name STRING,
scope_version STRING,
INDEX idx_timestamp(timestamp) USING INVERTED,
INDEX idx_service_instance_id(service_instance_id) USING INVERTED,
INDEX idx_trace_id(trace_id) USING INVERTED,
INDEX idx_span_id(span_id) USING INVERTED,
INDEX idx_trace_state(trace_state) USING INVERTED,
INDEX idx_parent_span_id(parent_span_id) USING INVERTED,
INDEX idx_span_name(span_name) USING INVERTED,
INDEX idx_span_kind(span_kind) USING INVERTED,
INDEX idx_end_time(end_time) USING INVERTED,
INDEX idx_duration(duration) USING INVERTED,
INDEX idx_span_attributes(span_attributes) USING INVERTED,
INDEX idx_status_message(status_message) USING INVERTED,
INDEX idx_status_code(status_code) USING INVERTED,
INDEX idx_resource_attributes(resource_attributes) USING INVERTED,
INDEX idx_scope_name(scope_name) USING INVERTED,
INDEX idx_scope_version(scope_version) USING INVERTED
)
ENGINE = OLAP
DUPLICATE KEY(service_name, timestamp)
PARTITION BY RANGE(timestamp) ()
DISTRIBUTED BY RANDOM BUCKETS 250
PROPERTIES (
"compression" = "zstd",
"compaction_policy" = "time_series",
"inverted_index_storage_format" = "V2",
"dynamic_partition.enable" = "true",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-30",
"dynamic_partition.end" = "1",
"dynamic_partition.prefix" = "p",
"dynamic_partition.buckets" = "250",
"dynamic_partition.replication_num" = "2", -- Not required in storage-compute decoupled mode
"replication_num" = "2", -- Not required in storage-compute decoupled mode
"storage_policy" = "log_policy_3day" -- Not required in storage-compute decoupled mode
);

2. Trace 수집​

Doris는 OpenTelemetry와 같은 Trace 수집 시스템과 연동할 수 있는 개방적이고 범용적인 Stream HTTP API를 제공합니다.

2.1 전체 파이프라인​

애플리케이션 → OpenTelemetry SDK/Agent → OpenTelemetry Collector(Doris Exporter 포함) → Doris 테이블로 이어집니다.

2.2 OpenTelemetry 연동 단계​

1단계: 애플리케이션에 OpenTelemetry SDK 통합​

이 예제에서는 공식 Spring Boot 데모에 OpenTelemetry Java SDK를 통합하여, 간단한 문자열 Hello World!를 경로 /에 대한 응답으로 반환합니다.

OpenTelemetry Java Agent를 다운로드합니다. Java Agent를 사용하면 기존 애플리케이션을 수정할 필요가 없다는 장점이 있습니다.

다른 언어와 연동 방법은 다음을 참조하세요.

2단계: OpenTelemetry Collector 배포 및 설정​

OpenTelemetry Collector를 다운로드하고 압축을 해제합니다.

이름이 otelcol-contrib로 시작하는 릴리스 패키지를 다운로드하세요. 이 패키지에는 Trace 데이터를 Doris에 적재할 수 있는 Doris Exporter 구성 요소가 포함되어 있습니다.

다음과 같이 otel_demo.yaml 설정 파일을 생성합니다. 더 많은 설정 옵션은 Doris Exporter 문서를 참조하세요.

receivers:
otlp: # otlp protocol, receives data sent by the OpenTelemetry Java Agent
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

processors:
batch:
send_batch_size: 100000 # Number of records per batch; recommended batch size is between 100M and 1G
timeout: 10s

exporters:
doris:
endpoint: http://localhost:8030 # FE HTTP address
database: doris_db_name
username: doris_username
password: doris_password
table:
traces: doris_table_name
create_schema: true # Whether to automatically create the schema; when set to false, you need to create the table manually
mysql_endpoint: localhost:9030 # FE MySQL address
history_days: 10
create_history_days: 10
timezone: Asia/Shanghai
timeout: 60s # http stream load client timeout
log_response: true
sending_queue:
enabled: true
num_consumers: 20
queue_size: 1000
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
headers:
load_to_single_tablet: "true"

3단계: OpenTelemetry Collector 실행​

./otelcol-contrib --config otel_demo.yaml

4단계: Spring Boot 샘플 애플리케이션 시작​

애플리케이션을 시작하기 전에 몇 가지 환경 변수만 설정하면 되며, 코드를 변경할 필요는 없습니다.

export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS} -javaagent:/your/path/to/opentelemetry-javaagent.jar" # Path to the OpenTelemetry Java Agent
export OTEL_JAVAAGENT_LOGGING="none" # Disable otel logging to avoid interfering with the service's own logs
export OTEL_SERVICE_NAME="myproject"
export OTEL_TRACES_EXPORTER="otlp" # Use the otlp protocol to send trace data
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" # Address of the OpenTelemetry Collector

java -jar myproject-0.0.1-SNAPSHOT.jar

5단계: 샘플 애플리케이션에 접속하여 Trace 데이터 생성​

curl localhost:8080을 실행하면 hello 서비스가 호출됩니다. OpenTelemetry Java Agent는 Trace 데이터를 자동으로 생성하여 OpenTelemetry Collector로 전송합니다. 이어서 Collector는 설정된 Doris Exporter를 통해 Trace 데이터를 Doris 테이블에 씁니다(기본 테이블 이름은 otel.otel_traces).

3. Trace 쿼리​

Trace 쿼리는 일반적으로 Grafana와 같은 시각적 인터페이스를 통해 수행합니다. 대표적인 사용 사례는 다음과 같습니다.

  • 시간 범위와 서비스 이름으로 필터링하여 지연 시간 분포 차트와 최신 Trace를 포함한 Trace 개요를 표시합니다.

    Trace 목록

  • 링크를 클릭하여 Trace 세부 정보를 확인합니다.

    Trace 쿼리